mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-15 02:15:14 +00:00
feat(rt): authorizations (#11066)
# Which Problems Are Solved This PR implements relational table and repository interface for the authorizations feature as described in https://github.com/zitadel/zitadel/issues/10767 # How the Problems Are Solved By * adding new `zitadel.authorizations` and `zitadel.authorization_roles` tables * implementing the `AuthorizationRepository` interface to create, update, query, and delete user authorizations and roles * implementing `authorization_relational.go` projection to update `zitadel.authorizations` and `zitadel.authorization_roles` tables based on events # Additional Changes N/A # Additional Context - Closes #10767 --------- Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Livio Spring
parent
11cf2c252a
commit
8472c7683d
@@ -0,0 +1,16 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed 010_authorizations_table/up.sql
|
||||
up010AuthorizationsTable string
|
||||
//go:embed 010_authorizations_table/down.sql
|
||||
down010AuthorizationsTable string
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerSQLMigration(10, up010AuthorizationsTable, down010AuthorizationsTable)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS zitadel.authorization_roles;
|
||||
DROP TABLE IF EXISTS zitadel.authorizations;
|
||||
DROP TYPE IF EXISTS zitadel.authorization_state;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
CREATE TYPE zitadel.authorization_state AS ENUM (
|
||||
'active',
|
||||
'inactive'
|
||||
);
|
||||
|
||||
CREATE TABLE zitadel.authorizations
|
||||
(
|
||||
instance_id TEXT NOT NULL,
|
||||
id TEXT NOT NULL CHECK ( id <> '' ),
|
||||
state zitadel.authorization_state NOT NULL DEFAULT 'active',
|
||||
project_id TEXT NOT NULL,
|
||||
grant_id TEXT,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (instance_id, id),
|
||||
-- FOREIGN KEY (instance_id, user_id) REFERENCES zitadel.users (instance_id, id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (instance_id, project_id) REFERENCES zitadel.projects (instance_id, id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (instance_id, grant_id) REFERENCES zitadel.project_grants (instance_id, id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE zitadel.authorization_roles
|
||||
(
|
||||
instance_id TEXT NOT NULL,
|
||||
authorization_id TEXT NOT NULL,
|
||||
role_key TEXT NOT NULL CHECK ( role_key <> '' ),
|
||||
project_id TEXT NOT NULL,
|
||||
grant_id TEXT,
|
||||
PRIMARY KEY (instance_id, authorization_id, role_key),
|
||||
FOREIGN KEY (instance_id, authorization_id) REFERENCES zitadel.authorizations (instance_id, id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (instance_id, project_id, role_key) REFERENCES zitadel.project_roles (instance_id, project_id, key) ON DELETE CASCADE,
|
||||
FOREIGN KEY (instance_id, grant_id, role_key) REFERENCES zitadel.project_grant_roles (instance_id, grant_id, key) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TRIGGER trigger_set_updated_at
|
||||
BEFORE UPDATE
|
||||
ON zitadel.authorizations
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.updated_at IS NULL)
|
||||
EXECUTE FUNCTION zitadel.set_updated_at();
|
||||
@@ -0,0 +1,505 @@
|
||||
//go:build integration
|
||||
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/domain"
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
authorization_v2 "github.com/zitadel/zitadel/pkg/grpc/authorization/v2"
|
||||
instance_v2 "github.com/zitadel/zitadel/pkg/grpc/instance/v2"
|
||||
org_v2beta "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
project_v2beta "github.com/zitadel/zitadel/pkg/grpc/project/v2beta"
|
||||
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func TestServer_AuthorizationReduces(t *testing.T) {
|
||||
instanceID := Instance.ID()
|
||||
orgID := Instance.DefaultOrg.Id
|
||||
authorizationRepo := repository.AuthorizationRepository()
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
|
||||
|
||||
t.Run("user grant added reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
createAndEnsureAuthorization(t, instanceID, orgID, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
})
|
||||
|
||||
t.Run("user grant update reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
|
||||
beforeCreate := time.Now()
|
||||
// create authorization
|
||||
createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: user.UserId,
|
||||
ProjectId: projectID,
|
||||
OrganizationId: orgID,
|
||||
RoleKeys: []string{role1, role2},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterCreate := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
// add a new role to the project
|
||||
role3 := "role3"
|
||||
_, err = ProjectClient.AddProjectRole(CTX, &project_v2beta.AddProjectRoleRequest{
|
||||
ProjectId: projectID,
|
||||
RoleKey: role3,
|
||||
DisplayName: "display",
|
||||
Group: nil,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
beforeUpdate := time.Now()
|
||||
// update roles to [role1, role2, role3]
|
||||
_, err = AuthorizationClient.UpdateAuthorization(CTX, &authorization_v2.UpdateAuthorizationRequest{
|
||||
Id: createdAuthorization.Id,
|
||||
RoleKeys: []string{role1, role2, role3},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterUpdate := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
require.Len(collect, az.Roles, 3)
|
||||
assert.Equal(collect, []string{role1, role2, role3}, az.Roles)
|
||||
assert.WithinRange(collect, az.UpdatedAt, beforeUpdate, afterUpdate)
|
||||
assert.WithinRange(collect, az.CreatedAt, beforeCreate, afterCreate)
|
||||
}, retryDuration, tick, "authorization not updated within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("user grant deactivate reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, nil)
|
||||
// create authorization
|
||||
beforeCreate := time.Now()
|
||||
createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: user.UserId,
|
||||
ProjectId: projectID,
|
||||
OrganizationId: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterCreate := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
before := time.Now()
|
||||
// deactivate authorization
|
||||
_, err = AuthorizationClient.DeactivateAuthorization(CTX, &authorization_v2.DeactivateAuthorizationRequest{
|
||||
Id: createdAuthorization.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
after := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, domain.AuthorizationStateInactive, az.State)
|
||||
assert.WithinRange(collect, az.UpdatedAt, before, after)
|
||||
assert.WithinRange(collect, az.CreatedAt, beforeCreate, afterCreate)
|
||||
}, retryDuration, tick, "authorization not deactivated within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("user grant activate reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, nil)
|
||||
// create authorization
|
||||
beforeCreate := time.Now()
|
||||
createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: user.UserId,
|
||||
ProjectId: projectID,
|
||||
OrganizationId: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterCreate := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
// deactivate authorization
|
||||
_, err = AuthorizationClient.DeactivateAuthorization(CTX, &authorization_v2.DeactivateAuthorizationRequest{
|
||||
Id: createdAuthorization.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
before := time.Now()
|
||||
// re-activate authorization
|
||||
_, err = AuthorizationClient.ActivateAuthorization(CTX, &authorization_v2.ActivateAuthorizationRequest{
|
||||
Id: createdAuthorization.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
after := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, domain.AuthorizationStateActive, az.State)
|
||||
assert.WithinRange(collect, az.UpdatedAt, before, after)
|
||||
assert.WithinRange(collect, az.CreatedAt, beforeCreate, afterCreate)
|
||||
}, retryDuration, tick, "authorization not activated within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("user grant removed reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
|
||||
// create authorization with roles
|
||||
createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: user.UserId,
|
||||
ProjectId: projectID,
|
||||
RoleKeys: []string{role1, role2},
|
||||
OrganizationId: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// delete authorization
|
||||
_, err = AuthorizationClient.DeleteAuthorization(CTX, &authorization_v2.DeleteAuthorizationRequest{
|
||||
Id: createdAuthorization.Id,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("user removed reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, orgID, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
// delete the user
|
||||
_, err := UserClient.DeleteUser(CTX, &user_v2.DeleteUserRequest{
|
||||
UserId: user.UserId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure authorization is deleted
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("project removed reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, orgID, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
// delete the project
|
||||
_, err := ProjectClient.DeleteProject(CTX, &project_v2beta.DeleteProjectRequest{
|
||||
Id: projectID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure authorization is deleted
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("project role removed reduces", func(t *testing.T) {
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgID, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, orgID, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
// delete a project role
|
||||
before := time.Now()
|
||||
_, err := ProjectClient.RemoveProjectRole(CTX, &project_v2beta.RemoveProjectRoleRequest{
|
||||
ProjectId: projectID,
|
||||
RoleKey: role2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
after := time.Now()
|
||||
|
||||
// ensure authorization is updated
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, []string{role1}, az.Roles)
|
||||
assert.Equal(collect, domain.AuthorizationStateActive, az.State)
|
||||
assert.WithinRange(collect, az.UpdatedAt, before, after)
|
||||
}, retryDuration, tick, "authorization not updated within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("user grant added for a project grant reduces", func(t *testing.T) {
|
||||
// prepare project and project roles
|
||||
role1 := "role1"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1})
|
||||
|
||||
// granted organization
|
||||
grantedOrganizationName := integration.OrganizationName()
|
||||
grantedOrganization := Instance.CreateOrganization(CTX, grantedOrganizationName, integration.Email())
|
||||
|
||||
// create project grant
|
||||
_, err := ProjectClient.CreateProjectGrant(CTX, &project_v2beta.CreateProjectGrantRequest{
|
||||
ProjectId: projectID,
|
||||
RoleKeys: []string{role1},
|
||||
GrantedOrganizationId: grantedOrganization.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, grantedOrganization.OrganizationId, integration.Email(), integration.Phone())
|
||||
// create authorization with roles
|
||||
createAndEnsureAuthorization(t, instanceID, grantedOrganization.OrganizationId, user.UserId, projectID, []string{role1}, retryDuration, tick)
|
||||
})
|
||||
|
||||
t.Run("project grant removed reduces", func(t *testing.T) {
|
||||
// prepare project and project roles
|
||||
role1 := "role1"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1})
|
||||
|
||||
// granted organization
|
||||
grantedOrganizationName := integration.OrganizationName()
|
||||
grantedOrganization := Instance.CreateOrganization(CTX, grantedOrganizationName, integration.Email())
|
||||
|
||||
// create project grant
|
||||
_, err := ProjectClient.CreateProjectGrant(CTX, &project_v2beta.CreateProjectGrantRequest{
|
||||
ProjectId: projectID,
|
||||
RoleKeys: []string{role1},
|
||||
GrantedOrganizationId: grantedOrganization.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, grantedOrganization.OrganizationId, integration.Email(), integration.Phone())
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, grantedOrganization.OrganizationId, user.UserId, projectID, []string{role1}, retryDuration, tick)
|
||||
// delete project grant
|
||||
_, err = ProjectClient.DeleteProjectGrant(CTX, &project_v2beta.DeleteProjectGrantRequest{
|
||||
ProjectId: projectID,
|
||||
GrantedOrganizationId: grantedOrganization.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure authorization is deleted
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("project grant updated reduces", func(t *testing.T) {
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgID, []string{role1, role2})
|
||||
// granted organization
|
||||
grantedOrganizationName := integration.OrganizationName()
|
||||
grantedOrganization := Instance.CreateOrganization(CTX, grantedOrganizationName, integration.Email())
|
||||
// create project grant
|
||||
_, err := ProjectClient.CreateProjectGrant(CTX, &project_v2beta.CreateProjectGrantRequest{
|
||||
ProjectId: projectID,
|
||||
RoleKeys: []string{role1, role2},
|
||||
GrantedOrganizationId: grantedOrganization.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, grantedOrganization.OrganizationId, integration.Email(), integration.Phone())
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, grantedOrganization.OrganizationId, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
// update project grant
|
||||
//beforeUpdate := time.Now()
|
||||
_, err = ProjectClient.UpdateProjectGrant(CTX, &project_v2beta.UpdateProjectGrantRequest{
|
||||
ProjectId: projectID,
|
||||
GrantedOrganizationId: grantedOrganization.OrganizationId,
|
||||
RoleKeys: []string{role1},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
//afterUpdate := time.Now().Add(500 * time.Millisecond) // add some buffer for eventual consistency of cascading changes
|
||||
|
||||
// ensure authorization is updated
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, []string{role1}, az.Roles)
|
||||
//assert.WithinRange(collect, az.UpdatedAt, beforeUpdate, afterUpdate)
|
||||
}, retryDuration, tick, "authorization not updated within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("org removed reduces", func(t *testing.T) {
|
||||
// create a new organization
|
||||
orgName := integration.OrganizationName()
|
||||
orgResp := Instance.CreateOrganization(CTX, orgName, integration.Email())
|
||||
// create user
|
||||
user := Instance.CreateHumanUserVerified(CTX, orgResp.OrganizationId, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectID := prepareProjectAndProjectRoles(t, orgResp.OrganizationId, []string{role1, role2})
|
||||
// create authorization with roles
|
||||
createdAuthorization := createAndEnsureAuthorization(t, instanceID, orgResp.OrganizationId, user.UserId, projectID, []string{role1, role2}, retryDuration, tick)
|
||||
// delete the organization
|
||||
_, err := OrgClient.DeleteOrganization(CTX, &org_v2beta.DeleteOrganizationRequest{
|
||||
Id: orgResp.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure authorization is deleted
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
|
||||
t.Run("instance removed reduces", func(t *testing.T) {
|
||||
// create a new instance
|
||||
instance := integration.NewInstance(CTX)
|
||||
// create a new organization
|
||||
orgResp := instance.CreateOrganization(CTX, integration.OrganizationName(), integration.Email())
|
||||
// create a user
|
||||
user := instance.CreateHumanUserVerified(CTX, orgResp.OrganizationId, integration.Email(), integration.Phone())
|
||||
// prepare project and project roles
|
||||
role1, role2 := "role1", "role2"
|
||||
projectResp := instance.CreateProject(CTX, t, orgResp.OrganizationId, integration.ProjectName(), false, false)
|
||||
_ = instance.AddProjectRole(CTX, t, projectResp.Id, role1, "display", "")
|
||||
_ = instance.AddProjectRole(CTX, t, projectResp.Id, role2, "display", "")
|
||||
|
||||
// create authorization with roles
|
||||
beforeCreate := time.Now()
|
||||
createdAuthorization, err := instance.Client.AuthorizationV2.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: user.UserId,
|
||||
ProjectId: projectResp.Id,
|
||||
RoleKeys: []string{role1, role2},
|
||||
OrganizationId: orgResp.OrganizationId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterCreate := time.Now().Add(500 * time.Millisecond)
|
||||
|
||||
// ensure authorization exists
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instance.ID(), createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, []string{role1, role2}, az.Roles)
|
||||
assert.Equal(collect, domain.AuthorizationStateActive, az.State)
|
||||
assert.WithinRange(collect, az.CreatedAt, beforeCreate, afterCreate)
|
||||
assert.WithinRange(collect, az.UpdatedAt, beforeCreate, afterCreate)
|
||||
}, retryDuration, tick, "authorization not found within %v: %v", retryDuration, err)
|
||||
|
||||
// delete the instance
|
||||
_, err = Instance.Client.InstanceV2.DeleteInstance(CTX, &instance_v2.DeleteInstanceRequest{
|
||||
InstanceId: instance.ID(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure authorization is deleted
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authorizationRepo.Get(CTX, pool, database.WithCondition(
|
||||
authorizationRepo.PrimaryKeyCondition(instance.ID(), createdAuthorization.Id),
|
||||
))
|
||||
require.Empty(collect, az)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick, "authorization not deleted within %v: %v", retryDuration, err)
|
||||
})
|
||||
}
|
||||
|
||||
func createAndEnsureAuthorization(t *testing.T, instanceID, orgID, userID, projectID string, roles []string, retryDuration time.Duration, tick time.Duration) *authorization_v2.CreateAuthorizationResponse {
|
||||
// create authorization
|
||||
beforeCreate := time.Now()
|
||||
createdAuthorization, err := AuthorizationClient.CreateAuthorization(CTX, &authorization_v2.CreateAuthorizationRequest{
|
||||
UserId: userID,
|
||||
ProjectId: projectID,
|
||||
RoleKeys: roles,
|
||||
OrganizationId: orgID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
afterCreate := time.Now().Add(500 * time.Millisecond)
|
||||
// ensure authorization exists
|
||||
authzRepo := repository.AuthorizationRepository()
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
az, err := authzRepo.Get(CTX, pool, database.WithCondition(
|
||||
authzRepo.PrimaryKeyCondition(instanceID, createdAuthorization.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
assert.Equal(collect, roles, az.Roles)
|
||||
assert.Equal(collect, domain.AuthorizationStateActive, az.State)
|
||||
assert.WithinRange(collect, az.CreatedAt, beforeCreate, afterCreate)
|
||||
assert.WithinRange(collect, az.UpdatedAt, beforeCreate, afterCreate)
|
||||
}, retryDuration, tick, "authorization not found within %v: %v", retryDuration, err)
|
||||
return createdAuthorization
|
||||
}
|
||||
|
||||
func prepareProjectAndProjectRoles(t *testing.T, orgID string, roles []string) string {
|
||||
project, err := ProjectClient.CreateProject(CTX, &project_v2beta.CreateProjectRequest{
|
||||
OrganizationId: orgID,
|
||||
Name: integration.ProjectName(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(roles) == 0 {
|
||||
return project.Id
|
||||
}
|
||||
|
||||
// Wait for the project to be created in the relational db before adding roles
|
||||
projectRepo := repository.ProjectRepository()
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, time.Minute)
|
||||
require.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
_, err := projectRepo.Get(CTX, pool, database.WithCondition(
|
||||
projectRepo.PrimaryKeyCondition(Instance.ID(), project.Id),
|
||||
))
|
||||
require.NoError(collect, err)
|
||||
}, retryDuration, tick, "project not found")
|
||||
|
||||
for _, role := range roles {
|
||||
_, err = ProjectClient.AddProjectRole(CTX, &project_v2beta.AddProjectRoleRequest{
|
||||
ProjectId: project.GetId(),
|
||||
RoleKey: role,
|
||||
DisplayName: "display",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
return project.Id
|
||||
}
|
||||
@@ -17,11 +17,13 @@ import (
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database/dialect/postgres"
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/authorization/v2"
|
||||
v2beta "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta"
|
||||
mgmt "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
v2beta_project "github.com/zitadel/zitadel/pkg/grpc/project/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/system"
|
||||
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
@@ -42,15 +44,17 @@ var ConnString = fmt.Sprintf(
|
||||
)
|
||||
|
||||
var (
|
||||
dbPool *pgxpool.Pool
|
||||
CTX context.Context
|
||||
IAMCTX context.Context
|
||||
Instance *integration.Instance
|
||||
SystemClient system.SystemServiceClient
|
||||
OrgClient v2beta_org.OrganizationServiceClient
|
||||
ProjectClient v2beta_project.ProjectServiceClient
|
||||
AdminClient admin.AdminServiceClient
|
||||
MgmtClient mgmt.ManagementServiceClient
|
||||
dbPool *pgxpool.Pool
|
||||
CTX context.Context
|
||||
IAMCTX context.Context
|
||||
Instance *integration.Instance
|
||||
SystemClient system.SystemServiceClient
|
||||
OrgClient v2beta_org.OrganizationServiceClient
|
||||
ProjectClient v2beta_project.ProjectServiceClient
|
||||
AdminClient admin.AdminServiceClient
|
||||
MgmtClient mgmt.ManagementServiceClient
|
||||
UserClient user_v2.UserServiceClient
|
||||
AuthorizationClient authorization.AuthorizationServiceClient
|
||||
)
|
||||
|
||||
var pool database.Pool
|
||||
@@ -69,6 +73,8 @@ func TestMain(m *testing.M) {
|
||||
ProjectClient = Instance.Client.Projectv2Beta
|
||||
AdminClient = Instance.Client.Admin
|
||||
MgmtClient = Instance.Client.Mgmt
|
||||
UserClient = Instance.Client.UserV2
|
||||
AuthorizationClient = Instance.Client.AuthorizationV2
|
||||
|
||||
defer func() {
|
||||
_, err := Instance.Client.InstanceV2Beta.DeleteInstance(CTX, &v2beta.DeleteInstanceRequest{
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/backend/v3/domain"
|
||||
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
||||
)
|
||||
|
||||
var _ domain.AuthorizationRepository = (*authorization)(nil)
|
||||
|
||||
type authorization struct{}
|
||||
|
||||
func AuthorizationRepository() domain.AuthorizationRepository {
|
||||
return new(authorization)
|
||||
}
|
||||
|
||||
func (a authorization) unqualifiedTableName() string {
|
||||
return "authorizations"
|
||||
}
|
||||
|
||||
func (a authorization) qualifiedTableName() string {
|
||||
return "zitadel." + a.unqualifiedTableName()
|
||||
}
|
||||
|
||||
func (a authorization) unqualifiedAuthorizationRolesTableName() string {
|
||||
return "authorization_roles"
|
||||
}
|
||||
|
||||
func (a authorization) qualifiedAuthorizationRolesTableName() string {
|
||||
return "zitadel." + a.unqualifiedAuthorizationRolesTableName()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// repository
|
||||
// -------------------------------------------------------------
|
||||
|
||||
const insertAuthorizationStmt = `INSERT INTO zitadel.authorizations (
|
||||
instance_id, id, user_id, grant_id, project_id, state
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING created_at, updated_at`
|
||||
|
||||
const insertAuthorizationWithRolesStmt = `WITH roles AS (
|
||||
INSERT INTO zitadel.authorization_roles
|
||||
(instance_id, authorization_id, grant_id, project_id, role_key)
|
||||
VALUES ($1, $2, $4, $5, unnest($7::text[])))` + insertAuthorizationStmt
|
||||
|
||||
// Create implements [domain.AuthorizationRepository].
|
||||
func (a authorization) Create(ctx context.Context, client database.QueryExecutor, authorization *domain.Authorization) error {
|
||||
builder := database.NewStatementBuilder(insertAuthorizationWithRolesStmt,
|
||||
authorization.InstanceID,
|
||||
authorization.ID,
|
||||
authorization.UserID,
|
||||
authorization.GrantID,
|
||||
authorization.ProjectID,
|
||||
authorization.State,
|
||||
authorization.Roles,
|
||||
)
|
||||
|
||||
if err := client.QueryRow(
|
||||
ctx,
|
||||
builder.String(),
|
||||
builder.Args()...).
|
||||
Scan(
|
||||
&authorization.CreatedAt,
|
||||
&authorization.UpdatedAt,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const queryAuthorizationStmt = `SELECT zitadel.authorizations.instance_id,
|
||||
zitadel.authorizations.id,
|
||||
zitadel.authorizations.user_id,
|
||||
zitadel.authorizations.grant_id,
|
||||
zitadel.authorizations.project_id,
|
||||
zitadel.authorizations.state,
|
||||
zitadel.authorizations.created_at,
|
||||
zitadel.authorizations.updated_at,
|
||||
ARRAY_AGG(authorization_roles.role_key)
|
||||
FILTER (WHERE authorization_roles.authorization_id IS NOT NULL) AS roles
|
||||
FROM zitadel.authorizations
|
||||
LEFT JOIN zitadel.authorization_roles
|
||||
ON zitadel.authorizations.instance_id = zitadel.authorization_roles.instance_id
|
||||
AND zitadel.authorizations.id = zitadel.authorization_roles.authorization_id`
|
||||
|
||||
// Get implements [domain.AuthorizationRepository].
|
||||
func (a authorization) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.Authorization, error) {
|
||||
opts = append(opts,
|
||||
database.WithGroupBy(a.InstanceIDColumn(), a.IDColumn()),
|
||||
)
|
||||
|
||||
builder, err := a.prepareQuery(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getOne[domain.Authorization](ctx, client, builder)
|
||||
}
|
||||
|
||||
// List implements [domain.AuthorizationRepository].
|
||||
func (a authorization) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.Authorization, error) {
|
||||
opts = append(opts,
|
||||
database.WithGroupBy(a.InstanceIDColumn(), a.IDColumn()),
|
||||
)
|
||||
|
||||
builder, err := a.prepareQuery(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getMany[domain.Authorization](ctx, client, builder)
|
||||
}
|
||||
|
||||
func (a authorization) prepareQuery(opts []database.QueryOption) (*database.StatementBuilder, error) {
|
||||
options := new(database.QueryOpts)
|
||||
for _, opt := range opts {
|
||||
opt(options)
|
||||
}
|
||||
if err := checkRestrictingColumns(options.Condition, a.InstanceIDColumn()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
builder := database.NewStatementBuilder(queryAuthorizationStmt)
|
||||
options.Write(builder)
|
||||
|
||||
return builder, nil
|
||||
}
|
||||
|
||||
const queryUpdateAuthorizationRoleStmt = `SELECT instance_id, id, project_id, grant_id, $1::text[] as roles from zitadel.authorizations`
|
||||
|
||||
const updateAuthorizationRoleStmt = `deleted_roles AS (
|
||||
DELETE FROM zitadel.authorization_roles as azr
|
||||
USING az
|
||||
WHERE azr.instance_id = az.instance_id
|
||||
AND azr.authorization_id = az.id
|
||||
AND NOT azr.role_key = ANY ($1::text[])
|
||||
RETURNING *
|
||||
), inserted_roles AS (
|
||||
INSERT INTO zitadel.authorization_roles (instance_id, authorization_id, project_id, grant_id, role_key)
|
||||
SELECT instance_id,
|
||||
id,
|
||||
project_id,
|
||||
grant_id,
|
||||
UNNEST(az.roles) AS role_key
|
||||
FROM az
|
||||
ON CONFLICT DO NOTHING
|
||||
RETURNING *
|
||||
)
|
||||
UPDATE zitadel.authorizations SET `
|
||||
|
||||
const updateAuthorizationRoleStmtWhere = ` FROM az
|
||||
WHERE az.instance_id = authorizations.instance_id
|
||||
AND az.id = authorizations.id`
|
||||
|
||||
// Update implements [domain.AuthorizationRepository].
|
||||
func (a authorization) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, roles []string, changes ...database.Change) (int64, error) {
|
||||
if roles != nil {
|
||||
return a.setRoles(ctx, client, condition, roles, changes...)
|
||||
}
|
||||
return updateOne(ctx, client, a, condition, changes...)
|
||||
}
|
||||
|
||||
// setRoles sets the roles of an authorization.
|
||||
func (a authorization) setRoles(ctx context.Context, client database.QueryExecutor, condition database.Condition, roles []string, changes ...database.Change) (int64, error) {
|
||||
if err := checkPKCondition(a, condition); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !database.Changes(changes).IsOnColumn(a.UpdatedAtColumn()) {
|
||||
changes = append(changes, database.NewChange(a.UpdatedAtColumn(), database.NullInstruction))
|
||||
}
|
||||
|
||||
// get the authorization to be updated
|
||||
builder := database.NewStatementBuilder("WITH az AS (")
|
||||
builder.WriteString(queryUpdateAuthorizationRoleStmt)
|
||||
builder.AppendArg(roles)
|
||||
writeCondition(builder, condition)
|
||||
builder.WriteString(" ), ")
|
||||
|
||||
// set the roles
|
||||
builder.WriteString(updateAuthorizationRoleStmt)
|
||||
err := database.Changes(changes).Write(builder)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
builder.WriteString(updateAuthorizationRoleStmtWhere)
|
||||
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// Delete implements [domain.AuthorizationRepository].
|
||||
func (a authorization) Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error) {
|
||||
if !condition.IsRestrictingColumn(a.InstanceIDColumn()) {
|
||||
return 0, database.NewMissingConditionError(a.InstanceIDColumn())
|
||||
}
|
||||
|
||||
builder := database.NewStatementBuilder("DELETE FROM zitadel.authorizations")
|
||||
writeCondition(builder, condition)
|
||||
|
||||
return client.Exec(ctx, builder.String(), builder.Args()...)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// columns
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// PrimaryKeyColumns implements [domain.Repository]
|
||||
func (a authorization) PrimaryKeyColumns() []database.Column {
|
||||
return []database.Column{
|
||||
a.InstanceIDColumn(),
|
||||
a.IDColumn(),
|
||||
}
|
||||
}
|
||||
|
||||
func (a authorization) IDColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "id")
|
||||
}
|
||||
|
||||
func (a authorization) UserIDColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "user_id")
|
||||
}
|
||||
|
||||
func (a authorization) GrantIDColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "grant_id")
|
||||
}
|
||||
|
||||
func (a authorization) ProjectIDColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "project_id")
|
||||
}
|
||||
|
||||
func (a authorization) InstanceIDColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "instance_id")
|
||||
}
|
||||
|
||||
func (a authorization) StateColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "state")
|
||||
}
|
||||
|
||||
func (a authorization) CreatedAtColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "created_at")
|
||||
}
|
||||
|
||||
func (a authorization) UpdatedAtColumn() database.Column {
|
||||
return database.NewColumn(a.unqualifiedTableName(), "updated_at")
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// conditions
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// PrimaryKeyCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) PrimaryKeyCondition(instanceID, id string) database.Condition {
|
||||
return database.And(
|
||||
a.InstanceIDCondition(instanceID),
|
||||
a.IDCondition(id),
|
||||
)
|
||||
}
|
||||
|
||||
// IDCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) IDCondition(id string) database.Condition {
|
||||
return database.NewTextCondition(a.IDColumn(), database.TextOperationEqual, id)
|
||||
}
|
||||
|
||||
// InstanceIDCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) InstanceIDCondition(instanceID string) database.Condition {
|
||||
return database.NewTextCondition(a.InstanceIDColumn(), database.TextOperationEqual, instanceID)
|
||||
}
|
||||
|
||||
// ProjectIDCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) ProjectIDCondition(projectID string) database.Condition {
|
||||
return database.NewTextCondition(a.ProjectIDColumn(), database.TextOperationEqual, projectID)
|
||||
}
|
||||
|
||||
// GrantIDCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) GrantIDCondition(grantID string) database.Condition {
|
||||
return database.NewTextCondition(a.GrantIDColumn(), database.TextOperationEqual, grantID)
|
||||
}
|
||||
|
||||
// UserIDCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) UserIDCondition(userID string) database.Condition {
|
||||
return database.NewTextCondition(a.UserIDColumn(), database.TextOperationEqual, userID)
|
||||
}
|
||||
|
||||
// RoleCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) RoleCondition(op database.TextOperation, role string) database.Condition {
|
||||
return database.NewTextCondition(database.NewColumn(a.unqualifiedAuthorizationRolesTableName(), "role_key"), op, role)
|
||||
}
|
||||
|
||||
// ExistsRole implements [domain.authorizationConditions]
|
||||
// ExistsRole creates a correlated [database.Exists] condition on the authorization_roles table.
|
||||
// Use this filter to make sure the authorization returned contains a specific role.
|
||||
func (a authorization) ExistsRole(cond database.Condition) database.Condition {
|
||||
return database.Exists(
|
||||
a.qualifiedAuthorizationRolesTableName(),
|
||||
database.And(
|
||||
database.NewColumnCondition(a.InstanceIDColumn(), database.NewColumn(a.unqualifiedAuthorizationRolesTableName(), "instance_id")),
|
||||
database.NewColumnCondition(a.IDColumn(), database.NewColumn(a.unqualifiedAuthorizationRolesTableName(), "authorization_id")),
|
||||
cond,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// StateCondition implements [domain.authorizationConditions]
|
||||
func (a authorization) StateCondition(state domain.AuthorizationState) database.Condition {
|
||||
return database.NewTextCondition(a.StateColumn(), database.TextOperationEqual, state.String())
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// changes
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// SetState implements [domain.authorizationChanges]
|
||||
func (a authorization) SetState(state domain.AuthorizationState) database.Change {
|
||||
return database.NewChange(a.StateColumn(), state)
|
||||
}
|
||||
|
||||
func (a authorization) SetUpdatedAt(updatedAt time.Time) database.Change {
|
||||
return database.NewChange(a.UpdatedAtColumn(), updatedAt)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -143,3 +143,21 @@ func createProjectRole(t *testing.T, tx database.Transaction, instanceID, orgID,
|
||||
|
||||
return projectRole.Key
|
||||
}
|
||||
|
||||
func createProjectGrant(t *testing.T, tx database.Transaction, instanceID, grantingOrgID, grantedOrgID, projectID string, roleKeys []string) string {
|
||||
t.Helper()
|
||||
projectGrant := domain.ProjectGrant{
|
||||
InstanceID: instanceID,
|
||||
ID: gofakeit.UUID(),
|
||||
ProjectID: projectID,
|
||||
GrantingOrganizationID: grantingOrgID,
|
||||
GrantedOrganizationID: grantedOrgID,
|
||||
State: domain.ProjectGrantStateActive,
|
||||
RoleKeys: roleKeys,
|
||||
}
|
||||
projectGrantRepo := repository.ProjectGrantRepository()
|
||||
err := projectGrantRepo.Create(t.Context(), tx, &projectGrant)
|
||||
require.NoError(t, err)
|
||||
|
||||
return projectGrant.ID
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user