From ef46f7db9a10ea5f884e0740504ae266d64a50e8 Mon Sep 17 00:00:00 2001 From: Razco Date: Thu, 27 Jun 2024 10:58:08 +0300 Subject: [PATCH 1/2] add facts api to go sdk --- go.mod | 2 +- pkg/api/api.go | 78 +++++++++++++++++++++++++++++++---- pkg/api/relationshipTuples.go | 17 +++++--- pkg/api/resourceInstances.go | 18 +++++--- pkg/api/roleAssignments.go | 18 +++++--- pkg/api/tenants.go | 18 +++++--- pkg/api/users.go | 18 +++++--- pkg/config/builder.go | 26 ++++++++---- pkg/config/config.go | 30 ++++++++++---- pkg/config/consts.go | 3 +- pkg/permit/permit.go | 3 +- pkg/tests/integration_test.go | 44 +++++++++++++++++++- 12 files changed, 222 insertions(+), 53 deletions(-) diff --git a/go.mod b/go.mod index f0a2f22..312c6f2 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/permitio/permit-golang -go 1.17 +go 1.18 require ( github.com/google/uuid v1.4.0 diff --git a/pkg/api/api.go b/pkg/api/api.go index ac0f27f..e84745a 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -2,10 +2,12 @@ package api import ( "context" + "fmt" "github.com/permitio/permit-golang/pkg/config" "github.com/permitio/permit-golang/pkg/errors" "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" + "time" ) type permitBaseApi struct { @@ -14,10 +16,37 @@ type permitBaseApi struct { logger *zap.Logger } +type PermitBaseFactsApi struct { + permitBaseApi +} + +type IPermitBaseFactsApi interface { + lazyLoadPermitContext(ctx context.Context, methodApiLevelArg ...config.APIKeyLevel) error + WaitForSync(timeout *time.Duration) *PermitBaseFactsApi +} + type IPermitBaseApi interface { lazyLoadPermitContext(ctx context.Context, methodApiLevelArg ...config.APIKeyLevel) error } +func (a *PermitBaseFactsApi) WaitForSync(timeout *time.Duration) *PermitBaseFactsApi { + if a.config.GetProxyFactsViaPDP() { + stringTimeout := "" + if timeout == nil { + if timeoutFromConfig := a.config.GetFactsSyncTimeout(); timeoutFromConfig != nil { + stringTimeout = fmt.Sprintf("%d", int64(timeoutFromConfig.Seconds())) + } + } + + clientConfig := a.client.GetConfig() + clientConfig.DefaultHeader["X-Wait-Timeout"] = stringTimeout + return NewPermitBaseFactsApi(openapi.NewAPIClient(clientConfig), a.config) + } else { + a.logger.Warn("Attempted to wait for sync, but 'proxyFactsViaPdp' is not enabled. Ignoring") + return a + } +} + func (a *permitBaseApi) lazyLoadPermitContext(ctx context.Context, methodApiLevelArg ...config.APIKeyLevel) error { var methodApiLevel config.APIKeyLevel permitContext := a.config.Context.GetContext() @@ -84,14 +113,37 @@ func (p *PermitApiClient) SetContext(ctx context.Context, project string, enviro } p.config.Context = permitContext } - -func NewPermitApiClient(ctx context.Context, config *config.PermitConfig) *PermitApiClient { +func NewClientConfig(config *config.PermitConfig) *openapi.Configuration { clientConfig := openapi.NewConfiguration() clientConfig.Host = getHostFromUrl(config.GetApiUrl()) clientConfig.Scheme = getSchemaFromUrl(config.GetApiUrl()) clientConfig.AddDefaultHeader("Authorization", "Bearer "+config.GetToken()) clientConfig.HTTPClient = config.GetHTTPClient() - client := openapi.NewAPIClient(clientConfig) + return clientConfig +} +func NewFactsClientConfig(config *config.PermitConfig) *openapi.Configuration { + clientConfig := openapi.NewConfiguration() + stringTimeout := "" + + //if timeout == nil { + if timeoutFromConfig := config.GetFactsSyncTimeout(); timeoutFromConfig != nil { + stringTimeout = fmt.Sprintf("%d", int64(timeoutFromConfig.Seconds())) + } + //} + clientConfig.DefaultHeader["X-Wait-Timeout"] = stringTimeout + + clientConfig.AddDefaultHeader("Authorization", "Bearer "+config.GetToken()) + clientConfig.Host = getHostFromUrl(config.GetPdpUrl()) + clientConfig.Scheme = getSchemaFromUrl(config.GetPdpUrl()) + clientConfig.HTTPClient = config.GetHTTPClient() + return clientConfig +} + +func NewPermitApiClient(config *config.PermitConfig) *PermitApiClient { + baseClientConfig := NewClientConfig(config) + factsClientConfig := NewFactsClientConfig(config) + client := openapi.NewAPIClient(baseClientConfig) + factsClient := openapi.NewAPIClient(factsClientConfig) return &PermitApiClient{ config: config, logger: config.Logger, @@ -102,17 +154,27 @@ func NewPermitApiClient(ctx context.Context, config *config.PermitConfig) *Permi ImplicitGrants: NewImplicitGrantsApi(client, config), Projects: NewProjectsApi(client, config), ProxyConfigs: NewProxyConfigsApi(client, config), - RelationshipTuples: NewRelationshipTuplesApi(client, config), + RelationshipTuples: NewRelationshipTuplesApi(factsClient, config), ResourceActionGroups: NewResourceActionGroupsApi(client, config), ResourceActions: NewResourceActionsApi(client, config), ResourceAttributes: NewResourceAttributesApi(client, config), - ResourceInstances: NewResourceInstancesApi(client, config), + ResourceInstances: NewResourceInstancesApi(factsClient, config), ResourceRelations: NewResourceRelationsApi(client, config), ResourceRoles: NewResourceRolesApi(client, config), Resources: NewResourcesApi(client, config), - RoleAssignments: NewRoleAssignmentsApi(client, config), + RoleAssignments: NewRoleAssignmentsApi(factsClient, config), Roles: NewRolesApi(client, config), - Tenants: NewTenantsApi(client, config), - Users: NewUsersApi(client, config), + Tenants: NewTenantsApi(factsClient, config), + Users: NewUsersApi(factsClient, config), + } +} + +func NewPermitBaseFactsApi(client *openapi.APIClient, config *config.PermitConfig) *PermitBaseFactsApi { + return &PermitBaseFactsApi{ + permitBaseApi: permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, } } diff --git a/pkg/api/relationshipTuples.go b/pkg/api/relationshipTuples.go index 6bc7c1d..8ac6d7b 100644 --- a/pkg/api/relationshipTuples.go +++ b/pkg/api/relationshipTuples.go @@ -7,21 +7,28 @@ import ( "github.com/permitio/permit-golang/pkg/models" "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" + "time" ) type RelationshipTuples struct { - permitBaseApi + PermitBaseFactsApi } func NewRelationshipTuplesApi(client *openapi.APIClient, config *config.PermitConfig) *RelationshipTuples { return &RelationshipTuples{ - permitBaseApi{ - client: client, - config: config, - logger: config.Logger, + PermitBaseFactsApi{ + permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, }, } } +func (u *RelationshipTuples) WaitForSync(timeout *time.Duration) *RelationshipTuples { + u.PermitBaseFactsApi.WaitForSync(timeout) + return u +} func (r *RelationshipTuples) Create( ctx context.Context, diff --git a/pkg/api/resourceInstances.go b/pkg/api/resourceInstances.go index a113d4b..7720389 100644 --- a/pkg/api/resourceInstances.go +++ b/pkg/api/resourceInstances.go @@ -8,22 +8,30 @@ import ( "github.com/permitio/permit-golang/pkg/models" "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" + "time" ) type ResourceInstances struct { - permitBaseApi + PermitBaseFactsApi } func NewResourceInstancesApi(client *openapi.APIClient, config *config.PermitConfig) *ResourceInstances { return &ResourceInstances{ - permitBaseApi{ - client: client, - config: config, - logger: config.Logger, + PermitBaseFactsApi{ + permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, }, } } +func (r *ResourceInstances) WaitForSync(timeout *time.Duration) *ResourceInstances { + r.PermitBaseFactsApi.WaitForSync(timeout) + return r +} + func (r *ResourceInstances) Create( ctx context.Context, resourceInstanceCreate models.ResourceInstanceCreate, diff --git a/pkg/api/roleAssignments.go b/pkg/api/roleAssignments.go index 00f927a..fbbd727 100644 --- a/pkg/api/roleAssignments.go +++ b/pkg/api/roleAssignments.go @@ -7,22 +7,30 @@ import ( "github.com/permitio/permit-golang/pkg/models" "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" + "time" ) type RoleAssignments struct { - permitBaseApi + PermitBaseFactsApi } func NewRoleAssignmentsApi(client *openapi.APIClient, config *config.PermitConfig) *RoleAssignments { return &RoleAssignments{ - permitBaseApi{ - client: client, - config: config, - logger: config.Logger, + PermitBaseFactsApi{ + permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, }, } } +func (r *RoleAssignments) WaitForSync(timeout *time.Duration) *RoleAssignments { + r.PermitBaseFactsApi.WaitForSync(timeout) + return r +} + func (r *RoleAssignments) List(ctx context.Context, page int, perPage int, userFilter, roleFilter, tenantFilter string) (*[]models.RoleAssignmentRead, error) { response, err := r.list(ctx, page, perPage, userFilter, roleFilter, tenantFilter, false) if err != nil { diff --git a/pkg/api/tenants.go b/pkg/api/tenants.go index e332ee3..def1c7f 100644 --- a/pkg/api/tenants.go +++ b/pkg/api/tenants.go @@ -8,22 +8,30 @@ import ( "github.com/permitio/permit-golang/pkg/models" "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" + "time" ) type Tenants struct { - permitBaseApi + PermitBaseFactsApi } func NewTenantsApi(client *openapi.APIClient, config *config.PermitConfig) *Tenants { return &Tenants{ - permitBaseApi{ - client: client, - config: config, - logger: config.Logger, + PermitBaseFactsApi{ + permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, }, } } +func (t *Tenants) WaitForSync(timeout *time.Duration) *Tenants { + t.PermitBaseFactsApi.WaitForSync(timeout) + return t +} + // List all tenants under the context's environment. // Usage Example: // `tenants, err := PermitClient.Api.Tenants.List(ctx, 1, 10)` diff --git a/pkg/api/users.go b/pkg/api/users.go index 12cefdd..1ff8335 100644 --- a/pkg/api/users.go +++ b/pkg/api/users.go @@ -9,22 +9,30 @@ import ( "github.com/permitio/permit-golang/pkg/openapi" "go.uber.org/zap" "strings" + "time" ) type Users struct { - permitBaseApi + PermitBaseFactsApi } func NewUsersApi(client *openapi.APIClient, config *config.PermitConfig) *Users { return &Users{ - permitBaseApi{ - client: client, - config: config, - logger: config.Logger, + PermitBaseFactsApi{ + permitBaseApi: permitBaseApi{ + client: client, + config: config, + logger: config.Logger, + }, }, } } +func (u *Users) WaitForSync(timeout *time.Duration) *Users { + u.PermitBaseFactsApi.WaitForSync(timeout) + return u +} + // List the users from your context's environment. // Usage Example: // diff --git a/pkg/config/builder.go b/pkg/config/builder.go index 856e758..47aec8d 100644 --- a/pkg/config/builder.go +++ b/pkg/config/builder.go @@ -11,19 +11,31 @@ type PermitBuilder struct { } func NewConfigBuilder(token string) *PermitBuilder { + factsSyncTimeout := DefaultFactsSyncTimeout return &PermitBuilder{ PermitConfig: PermitConfig{ - apiUrl: DefaultApiUrl, - token: token, - pdpUrl: DefaultPdpUrl, - debug: DefaultDebugMode, - Context: nil, - Logger: nil, - httpClient: &http.Client{Timeout: DefaultTimeout}, + apiUrl: DefaultApiUrl, + token: token, + pdpUrl: DefaultPdpUrl, + debug: DefaultDebugMode, + Context: nil, + Logger: nil, + proxyFactsViaPDP: false, + factsSyncTimeout: &factsSyncTimeout, + httpClient: &http.Client{Timeout: DefaultTimeout}, }, } } +func (c *PermitConfig) WithProxyFactsViaPDP(proxyFactsViaPDP bool) *PermitConfig { + c.proxyFactsViaPDP = proxyFactsViaPDP + return c +} + +func (c *PermitConfig) WithFactsSyncTimeout(timeout time.Duration) *PermitConfig { + c.factsSyncTimeout = &timeout + return c +} func (c *PermitConfig) WithHTTPClient(client *http.Client) *PermitConfig { c.httpClient = client return c diff --git a/pkg/config/config.go b/pkg/config/config.go index b7f0182..ea3f9d0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,17 +3,20 @@ package config import ( "go.uber.org/zap" "net/http" + "time" ) type PermitConfig struct { - apiUrl string - token string - pdpUrl string - opaUrl string - debug bool - Context *PermitContext - Logger *zap.Logger - httpClient *http.Client + apiUrl string + token string + pdpUrl string + opaUrl string + debug bool + Context *PermitContext + Logger *zap.Logger + httpClient *http.Client + proxyFactsViaPDP bool + factsSyncTimeout *time.Duration } type IPermitConfig interface { @@ -24,6 +27,9 @@ type IPermitConfig interface { GetDebug() bool GetContext() *PermitContext GetLogger() *zap.Logger + GetProxyFactsViaPDP() bool + GetFactsSyncTimeout() *int64 + GetHTTPClient() *http.Client } func NewPermitConfig(apiUrl string, token string, pdpUrl string, debug bool, context *PermitContext, logger *zap.Logger) *PermitConfig { @@ -65,6 +71,14 @@ func (c *PermitConfig) GetLogger() *zap.Logger { return c.Logger } +func (c *PermitConfig) GetProxyFactsViaPDP() bool { + return c.proxyFactsViaPDP +} + +func (c *PermitConfig) GetFactsSyncTimeout() *time.Duration { + return c.factsSyncTimeout +} + func (c *PermitConfig) GetHTTPClient() *http.Client { return c.httpClient } diff --git a/pkg/config/consts.go b/pkg/config/consts.go index de20d8d..93359fb 100644 --- a/pkg/config/consts.go +++ b/pkg/config/consts.go @@ -9,5 +9,6 @@ const ( DefaultPdpUrl = "http://localhost:7766" DefaultOpaUrl = "http://localhost:8181" - DefaultTimeout = time.Second * 5 + DefaultTimeout = time.Second * 5 + DefaultFactsSyncTimeout = 10 * time.Second ) diff --git a/pkg/permit/permit.go b/pkg/permit/permit.go index 0b7b4b0..162190b 100644 --- a/pkg/permit/permit.go +++ b/pkg/permit/permit.go @@ -18,8 +18,7 @@ type Client struct { var New = NewPermit func NewPermit(config config.PermitConfig) *Client { - ctx := context.Background() - apiClient := api.NewPermitApiClient(ctx, &config) + apiClient := api.NewPermitApiClient(&config) enforcerClient := enforcement.NewPermitEnforcerClient(&config) return &Client{ config: config, diff --git a/pkg/tests/integration_test.go b/pkg/tests/integration_test.go index 0db570e..2bbb7e7 100644 --- a/pkg/tests/integration_test.go +++ b/pkg/tests/integration_test.go @@ -133,6 +133,45 @@ func checkBulk(ctx context.Context, t *testing.T, permitClient *permit.Client, r assert.EqualValues(t, 3, *unassignReport.AssignmentsRemoved) } +func factsApi(ctx context.Context, t *testing.T, permitContext *config.PermitContext, logger *zap.Logger, token string) { + permitClient := permit.New(config.NewConfigBuilder(token). + WithPdpUrl(os.Getenv("PDP_URL")). + WithApiUrl(os.Getenv("API_URL")). + WithContext(permitContext). + WithLogger(logger). + WithProxyFactsViaPDP(true). + WithFactsSyncTimeout(10 * time.Second). + Build()) + + resourceKey := randKey("resource") + resourceCreate := *models.NewResourceCreate(resourceKey, resourceKey, + map[string]models.ActionBlockEditable{ + "read": {Attributes: map[string]interface{}{"marker": "marker"}}, + }) + _, err := permitClient.Api.Resources.Create(ctx, resourceCreate) + assert.NoError(t, err) + + roleKey := randKey("role") + roleCreate := models.NewRoleCreate(roleKey, roleKey) + roleCreate.SetPermissions([]string{fmt.Sprintf("%s:read", resourceKey)}) + _, err = permitClient.Api.Roles.Create(ctx, *roleCreate) + assert.NoError(t, err) + + userKey := randKey("user") + userCreate := *models.NewUserCreate(userKey) + userCreate.SetFirstName("John") + userCreate.SetLastName("Doe") + userCreate.SetEmail("john@example.com") + _, err = permitClient.Api.Users.WaitForSync(nil).Create(ctx, userCreate) + assert.NoError(t, err) + + _, err = permitClient.Api.Users.WaitForSync(nil).AssignRole(ctx, userKey, roleKey, "default") + assert.NoError(t, err) + // check if user has permission immediately + allowed, err := permitClient.Check(enforcement.UserBuilder(userKey).Build(), "read", enforcement.ResourceBuilder(resourceKey).Build()) + assert.NoError(t, err) + assert.True(t, allowed) +} func TestIntegration(t *testing.T) { logger := zap.NewExample() ctx := context.Background() @@ -173,6 +212,9 @@ func TestIntegration(t *testing.T) { WithLogger(logger). Build()) + // Test Facts API + factsApi(ctx, t, permitContext, logger, token) + // Create a user userCreate := *models.NewUserCreate(userKey) userCreate.SetFirstName("John") @@ -306,7 +348,7 @@ func TestIntegration(t *testing.T) { userPermissions, err := permitClient.GetUserPermissions(enforcement.UserBuilder(userKey).Build()) assert.NoError(t, err) - userPermissionsInTenant, found := userPermissions["__tenant:" + tenantKey] + userPermissionsInTenant, found := userPermissions["__tenant:"+tenantKey] assert.True(t, found) assert.Equal(t, tenantKey, userPermissionsInTenant.Tenant.Key) assert.True(t, assert.ObjectsAreEqual(tenantCreate.Attributes, userPermissionsInTenant.Tenant.Attributes)) From 52f90173dbfd602d7f7f892ff7899b283c843a5f Mon Sep 17 00:00:00 2001 From: Razco Date: Thu, 27 Jun 2024 12:13:33 +0300 Subject: [PATCH 2/2] fix for CR --- go.mod | 2 +- pkg/api/relationshipTuples.go | 3 +-- pkg/api/resourceInstances.go | 3 +-- pkg/api/roleAssignments.go | 3 +-- pkg/api/tenants.go | 3 +-- pkg/api/users.go | 3 +-- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 312c6f2..f0a2f22 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/permitio/permit-golang -go 1.18 +go 1.17 require ( github.com/google/uuid v1.4.0 diff --git a/pkg/api/relationshipTuples.go b/pkg/api/relationshipTuples.go index 8ac6d7b..bc02b0c 100644 --- a/pkg/api/relationshipTuples.go +++ b/pkg/api/relationshipTuples.go @@ -26,8 +26,7 @@ func NewRelationshipTuplesApi(client *openapi.APIClient, config *config.PermitCo } } func (u *RelationshipTuples) WaitForSync(timeout *time.Duration) *RelationshipTuples { - u.PermitBaseFactsApi.WaitForSync(timeout) - return u + return NewRelationshipTuplesApi(u.PermitBaseFactsApi.WaitForSync(timeout).client, u.config) } func (r *RelationshipTuples) Create( diff --git a/pkg/api/resourceInstances.go b/pkg/api/resourceInstances.go index 7720389..5c8bf73 100644 --- a/pkg/api/resourceInstances.go +++ b/pkg/api/resourceInstances.go @@ -28,8 +28,7 @@ func NewResourceInstancesApi(client *openapi.APIClient, config *config.PermitCon } func (r *ResourceInstances) WaitForSync(timeout *time.Duration) *ResourceInstances { - r.PermitBaseFactsApi.WaitForSync(timeout) - return r + return NewResourceInstancesApi(r.PermitBaseFactsApi.WaitForSync(timeout).client, r.config) } func (r *ResourceInstances) Create( diff --git a/pkg/api/roleAssignments.go b/pkg/api/roleAssignments.go index fbbd727..bd8685d 100644 --- a/pkg/api/roleAssignments.go +++ b/pkg/api/roleAssignments.go @@ -27,8 +27,7 @@ func NewRoleAssignmentsApi(client *openapi.APIClient, config *config.PermitConfi } func (r *RoleAssignments) WaitForSync(timeout *time.Duration) *RoleAssignments { - r.PermitBaseFactsApi.WaitForSync(timeout) - return r + return NewRoleAssignmentsApi(r.PermitBaseFactsApi.WaitForSync(timeout).client, r.config) } func (r *RoleAssignments) List(ctx context.Context, page int, perPage int, userFilter, roleFilter, tenantFilter string) (*[]models.RoleAssignmentRead, error) { diff --git a/pkg/api/tenants.go b/pkg/api/tenants.go index def1c7f..78a11d0 100644 --- a/pkg/api/tenants.go +++ b/pkg/api/tenants.go @@ -28,8 +28,7 @@ func NewTenantsApi(client *openapi.APIClient, config *config.PermitConfig) *Tena } func (t *Tenants) WaitForSync(timeout *time.Duration) *Tenants { - t.PermitBaseFactsApi.WaitForSync(timeout) - return t + return NewTenantsApi(t.PermitBaseFactsApi.WaitForSync(timeout).client, t.config) } // List all tenants under the context's environment. diff --git a/pkg/api/users.go b/pkg/api/users.go index 1ff8335..a94c010 100644 --- a/pkg/api/users.go +++ b/pkg/api/users.go @@ -29,8 +29,7 @@ func NewUsersApi(client *openapi.APIClient, config *config.PermitConfig) *Users } func (u *Users) WaitForSync(timeout *time.Duration) *Users { - u.PermitBaseFactsApi.WaitForSync(timeout) - return u + return NewUsersApi(u.PermitBaseFactsApi.WaitForSync(timeout).client, u.config) } // List the users from your context's environment.