mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
Introduces the `administrators` relational table and repository layer for the v3 backend, unifying instance, organization, project, and project-grant membership into a single scoped model. Closes #10220 ## Changes - **Domain**: `Administrator` struct, `AdministratorScope` enum, `AdministratorRepository` interface with typed CRUD, column, condition, and change helpers. Generated enumer and GoMock. - **Migration** (`016`): `zitadel.administrators` table with a generated `id`, scope-alignment check constraint, cascading foreign keys, and a child `administrator_roles` table. - **Repository**: CTE-based role mutations (`AddRole`, `RemoveRole`, `SetRoles`) for atomicity. Roles aggregated via `ARRAY_AGG`. - **Projection**: Event reducers for `member.added/changed/removed/cascade_removed` across all four admin scopes. - **Fixes**: `Rows.Close()` error wrapping; `TextArray` nil-safe scan + full `pgtype` array interface; `authorization` and `projectGrant` scan helpers fixed for `ARRAY_AGG` under `database/sql`; removed unused `ctx` parameter from `callReduce`. --------- Co-authored-by: Marco Ardizzone <[email protected]>
94 lines
4.1 KiB
Go
94 lines
4.1 KiB
Go
package domain
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
|
)
|
|
|
|
//go:generate enumer -type AdministratorScope -transform snake -trimprefix AdministratorScope -sql
|
|
type AdministratorScope uint8
|
|
|
|
const (
|
|
AdministratorScopeInstance AdministratorScope = iota
|
|
AdministratorScopeOrganization
|
|
AdministratorScopeProject
|
|
AdministratorScopeProjectGrant
|
|
)
|
|
|
|
type Administrator struct {
|
|
// ID is generated by the storage layer during creation and any value
|
|
// provided by the caller is ignored. The domain package must not
|
|
// generate this value. The format is storage-specific, for example
|
|
// PostgreSQL uses `<user_id>:<scope>:<scope_id>`.
|
|
ID string `json:"id,omitempty" db:"id"`
|
|
InstanceID string `json:"instanceId,omitempty" db:"instance_id"`
|
|
UserID string `json:"userId,omitempty" db:"user_id"`
|
|
Scope AdministratorScope `json:"scope,omitempty" db:"scope"`
|
|
OrganizationID *string `json:"organizationId,omitempty" db:"organization_id"`
|
|
ProjectID *string `json:"projectId,omitempty" db:"project_id"`
|
|
ProjectGrantID *string `json:"projectGrantId,omitempty" db:"project_grant_id"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updatedAt,omitempty" db:"updated_at"`
|
|
Roles []string `json:"roles,omitempty" db:"roles"`
|
|
}
|
|
|
|
type administratorColumns interface {
|
|
PrimaryKeyColumns() []database.Column
|
|
IDColumn() database.Column
|
|
InstanceIDColumn() database.Column
|
|
UserIDColumn() database.Column
|
|
ScopeColumn() database.Column
|
|
OrganizationIDColumn() database.Column
|
|
ProjectIDColumn() database.Column
|
|
ProjectGrantIDColumn() database.Column
|
|
CreatedAtColumn() database.Column
|
|
UpdatedAtColumn() database.Column
|
|
}
|
|
|
|
type administratorConditions interface {
|
|
PrimaryKeyCondition(instanceID, administratorID string) database.Condition
|
|
IDCondition(administratorID string) database.Condition
|
|
InstanceIDCondition(instanceID string) database.Condition
|
|
UserIDCondition(userID string) database.Condition
|
|
ScopeCondition(scope AdministratorScope) database.Condition
|
|
InstanceAdministratorCondition(instanceID, userID string) database.Condition
|
|
OrganizationAdministratorCondition(instanceID, organizationID, userID string) database.Condition
|
|
ProjectAdministratorCondition(instanceID, projectID, userID string) database.Condition
|
|
ProjectGrantAdministratorCondition(instanceID, projectGrantID, userID string) database.Condition
|
|
OrganizationIDCondition(organizationID string) database.Condition
|
|
ProjectIDCondition(projectID string) database.Condition
|
|
ProjectGrantIDCondition(projectGrantID string) database.Condition
|
|
RoleCondition(op database.TextOperation, role string) database.Condition
|
|
}
|
|
|
|
type administratorChanges interface {
|
|
SetUpdatedAt(updatedAt time.Time) database.Change
|
|
// AddRole adds the specified role to the administrator.
|
|
// If the administrator already has the role, no change will be made.
|
|
AddRole(role string) database.Change
|
|
// RemoveRole removes the specified role from the administrator.
|
|
// If the administrator does not have the role, no change will be made.
|
|
RemoveRole(role string) database.Change
|
|
// SetRoles replaces all existing roles with the provided ones.
|
|
// If no roles are provided, all existing roles will be removed.
|
|
SetRoles(roles []string) database.Change
|
|
}
|
|
|
|
//go:generate mockgen -typed -package domainmock -destination ./mock/administrator.mock.go . AdministratorRepository
|
|
type AdministratorRepository interface {
|
|
Repository
|
|
|
|
administratorColumns
|
|
administratorConditions
|
|
administratorChanges
|
|
|
|
Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*Administrator, error)
|
|
List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*Administrator, error)
|
|
|
|
Create(ctx context.Context, client database.QueryExecutor, administrator *Administrator) error
|
|
Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error)
|
|
Delete(ctx context.Context, client database.QueryExecutor, condition database.Condition) (int64, error)
|
|
}
|