mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
feat: add schema user create and remove (#8494)
# Which Problems Are Solved Added functionality that user with a userschema can be created and removed. # How the Problems Are Solved Added logic and moved APIs so that everything is API v3 conform. # Additional Changes - move of user and userschema API to resources folder - changed testing and parameters - some renaming # Additional Context closes #7308 --------- Co-authored-by: Elio Bischof <elio@zitadel.com>
This commit is contained in:
parent
90b908c361
commit
41ae35f2ef
@ -45,13 +45,14 @@ import (
|
||||
org_v2 "github.com/zitadel/zitadel/internal/api/grpc/org/v2"
|
||||
org_v2beta "github.com/zitadel/zitadel/internal/api/grpc/org/v2beta"
|
||||
action_v3_alpha "github.com/zitadel/zitadel/internal/api/grpc/resources/action/v3alpha"
|
||||
user_v3_alpha "github.com/zitadel/zitadel/internal/api/grpc/resources/user/v3alpha"
|
||||
userschema_v3_alpha "github.com/zitadel/zitadel/internal/api/grpc/resources/userschema/v3alpha"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/resources/webkey/v3"
|
||||
session_v2 "github.com/zitadel/zitadel/internal/api/grpc/session/v2"
|
||||
session_v2beta "github.com/zitadel/zitadel/internal/api/grpc/session/v2beta"
|
||||
settings_v2 "github.com/zitadel/zitadel/internal/api/grpc/settings/v2"
|
||||
settings_v2beta "github.com/zitadel/zitadel/internal/api/grpc/settings/v2beta"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/system"
|
||||
user_schema_v3_alpha "github.com/zitadel/zitadel/internal/api/grpc/user/schema/v3alpha"
|
||||
user_v2 "github.com/zitadel/zitadel/internal/api/grpc/user/v2"
|
||||
user_v2beta "github.com/zitadel/zitadel/internal/api/grpc/user/v2beta"
|
||||
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||||
@ -444,7 +445,10 @@ func startAPIs(
|
||||
if err := apis.RegisterService(ctx, action_v3_alpha.CreateServer(config.SystemDefaults, commands, queries, domain.AllFunctions, apis.ListGrpcMethods, apis.ListGrpcServices)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := apis.RegisterService(ctx, user_schema_v3_alpha.CreateServer(commands, queries)); err != nil {
|
||||
if err := apis.RegisterService(ctx, userschema_v3_alpha.CreateServer(config.SystemDefaults, commands, queries)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := apis.RegisterService(ctx, user_v3_alpha.CreateServer(commands, keys.User)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := apis.RegisterService(ctx, webkey.CreateServer(commands, queries)); err != nil {
|
||||
|
@ -317,7 +317,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
user_schema_v3: {
|
||||
specPath: ".artifacts/openapi/zitadel/user/schema/v3alpha/user_schema_service.swagger.json",
|
||||
specPath: ".artifacts/openapi/zitadel/resources/userschema/v3alpha/user_schema_service.swagger.json",
|
||||
outputDir: "docs/apis/resources/user_schema_service_v3",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
@ -325,7 +325,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
user_v3: {
|
||||
specPath: ".artifacts/openapi/zitadel/user/v3alpha/user_service.swagger.json",
|
||||
specPath: ".artifacts/openapi/zitadel/resources/user/v3alpha/user_service.swagger.json",
|
||||
outputDir: "docs/apis/resources/user_service_v3",
|
||||
sidebarOptions: {
|
||||
groupPathsBy: "tag",
|
||||
|
51
internal/api/grpc/resources/user/v3alpha/server.go
Normal file
51
internal/api/grpc/resources/user/v3alpha/server.go
Normal file
@ -0,0 +1,51 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
)
|
||||
|
||||
var _ user.ZITADELUsersServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
user.UnimplementedZITADELUsersServer
|
||||
command *command.Commands
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
userCodeAlg: userCodeAlg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
user.RegisterZITADELUsersServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return user.ZITADELUsers_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return user.ZITADELUsers_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return user.ZITADELUsers_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return user.RegisterZITADELUsersHandler
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
)
|
||||
|
||||
var (
|
||||
IAMOwnerCTX, SystemCTX context.Context
|
||||
UserCTX context.Context
|
||||
Tester *integration.Tester
|
||||
Client user.ZITADELUsersClient
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, _, cancel := integration.Contexts(time.Hour)
|
||||
defer cancel()
|
||||
|
||||
Tester = integration.NewTester(ctx)
|
||||
defer Tester.Done()
|
||||
|
||||
IAMOwnerCTX = Tester.WithAuthorization(ctx, integration.IAMOwner)
|
||||
SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser)
|
||||
UserCTX = Tester.WithAuthorization(ctx, integration.Login)
|
||||
Client = Tester.Client.UserV3Alpha
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func ensureFeatureEnabled(t *testing.T, iamOwnerCTX context.Context) {
|
||||
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if f.UserSchema.GetEnabled() {
|
||||
return
|
||||
}
|
||||
_, err = Tester.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
|
||||
UserSchema: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
retryDuration := time.Minute
|
||||
if ctxDeadline, ok := iamOwnerCTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
require.EventuallyWithT(t,
|
||||
func(ttt *assert.CollectT) {
|
||||
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(ttt, err)
|
||||
if f.UserSchema.GetEnabled() {
|
||||
return
|
||||
}
|
||||
},
|
||||
retryDuration,
|
||||
100*time.Millisecond,
|
||||
"timed out waiting for ensuring instance feature")
|
||||
}
|
66
internal/api/grpc/resources/user/v3alpha/user.go
Normal file
66
internal/api/grpc/resources/user/v3alpha/user.go
Normal file
@ -0,0 +1,66 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
)
|
||||
|
||||
func (s *Server) CreateUser(ctx context.Context, req *user.CreateUserRequest) (_ *user.CreateUserResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemauser, err := createUserRequestToCreateSchemaUser(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.command.CreateSchemaUser(ctx, schemauser, s.userCodeAlg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.CreateUserResponse{
|
||||
Details: resource_object.DomainToDetailsPb(schemauser.Details, object.OwnerType_OWNER_TYPE_ORG, schemauser.ResourceOwner),
|
||||
EmailCode: gu.Ptr(schemauser.ReturnCodeEmail),
|
||||
PhoneCode: gu.Ptr(schemauser.ReturnCodePhone),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createUserRequestToCreateSchemaUser(ctx context.Context, req *user.CreateUserRequest) (*command.CreateSchemaUser, error) {
|
||||
data, err := req.GetUser().GetData().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.CreateSchemaUser{
|
||||
ResourceOwner: authz.GetCtxData(ctx).OrgID,
|
||||
SchemaID: req.GetUser().GetSchemaId(),
|
||||
ID: req.GetUser().GetUserId(),
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUser(ctx context.Context, req *user.DeleteUserRequest) (_ *user.DeleteUserResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.DeleteSchemaUser(ctx, req.GetUserId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.DeleteUserResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func checkUserSchemaEnabled(ctx context.Context) error {
|
||||
if authz.GetInstance(ctx).Features().UserSchema {
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "TODO", "Errors.UserSchema.NotEnabled")
|
||||
}
|
@ -0,0 +1,354 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
)
|
||||
|
||||
func TestServer_CreateUser(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
schema := []byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
schemaResp := Tester.CreateUserSchema(IAMOwnerCTX, schema)
|
||||
permissionSchema := []byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"urn:zitadel:schema:permission": {
|
||||
"owner": "r",
|
||||
"self": "r"
|
||||
},
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
permissionSchemaResp := Tester.CreateUserSchema(IAMOwnerCTX, permissionSchema)
|
||||
orgResp := Tester.CreateOrganization(IAMOwnerCTX, gofakeit.Name(), gofakeit.Email())
|
||||
|
||||
type res struct {
|
||||
want *resource_object.Details
|
||||
returnCodeEmail bool
|
||||
returnCodePhone bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *user.CreateUserRequest
|
||||
res res
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user create, no schemaID",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{Data: unmarshalJSON("{\"name\": \"user\"}")},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user create, no context",
|
||||
ctx: context.Background(),
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: schemaResp.GetDetails().GetId(),
|
||||
Data: unmarshalJSON("{\"name\": \"user\"}"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user create, no permission",
|
||||
ctx: UserCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: schemaResp.GetDetails().GetId(),
|
||||
Data: unmarshalJSON("{\"name\": \"user\"}"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user create, invalid schema permission, owner",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: permissionSchemaResp.GetDetails().GetId(),
|
||||
Data: unmarshalJSON("{\"name\": \"user\"}"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user create, no user data",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: schemaResp.GetDetails().GetId(),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "user create, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: schemaResp.GetDetails().GetId(),
|
||||
Data: unmarshalJSON("{\"name\": \"user\"}"),
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "user create, full contact, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.CreateUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
User: &user.CreateUser{
|
||||
SchemaId: schemaResp.GetDetails().GetId(),
|
||||
Data: unmarshalJSON("{\"name\": \"user\"}"),
|
||||
Contact: &user.SetContact{
|
||||
Email: &user.SetEmail{
|
||||
Address: gofakeit.Email(),
|
||||
Verification: &user.SetEmail_ReturnCode{ReturnCode: &user.ReturnEmailVerificationCode{}},
|
||||
},
|
||||
Phone: &user.SetPhone{
|
||||
Number: gofakeit.Phone(),
|
||||
Verification: &user.SetPhone_ReturnCode{ReturnCode: &user.ReturnPhoneVerificationCode{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
returnCodePhone: true,
|
||||
returnCodeEmail: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Tester.Client.UserV3Alpha.CreateUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
if tt.res.returnCodeEmail {
|
||||
require.NotNil(t, got.EmailCode)
|
||||
}
|
||||
if tt.res.returnCodePhone {
|
||||
require.NotNil(t, got.PhoneCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeleteUser(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
schema := []byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`)
|
||||
schemaResp := Tester.CreateUserSchema(IAMOwnerCTX, schema)
|
||||
orgResp := Tester.CreateOrganization(IAMOwnerCTX, gofakeit.Name(), gofakeit.Email())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
dep func(ctx context.Context, req *user.DeleteUserRequest) error
|
||||
req *user.DeleteUserRequest
|
||||
want *resource_object.Details
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user delete, no userID",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.DeleteUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
UserId: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user delete, not existing",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &user.DeleteUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
UserId: "notexisting",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user delete, no context",
|
||||
ctx: context.Background(),
|
||||
dep: func(ctx context.Context, req *user.DeleteUserRequest) error {
|
||||
userResp := Tester.CreateSchemaUser(IAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.UserId = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.DeleteUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user delete, no permission",
|
||||
ctx: UserCTX,
|
||||
dep: func(ctx context.Context, req *user.DeleteUserRequest) error {
|
||||
userResp := Tester.CreateSchemaUser(IAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.UserId = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.DeleteUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user delete, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
dep: func(ctx context.Context, req *user.DeleteUserRequest) error {
|
||||
userResp := Tester.CreateSchemaUser(ctx, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.UserId = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.DeleteUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.dep != nil {
|
||||
err := tt.dep(tt.ctx, tt.req)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
got, err := Tester.Client.UserV3Alpha.DeleteUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshalJSON(data string) *structpb.Struct {
|
||||
user := new(structpb.Struct)
|
||||
err := user.UnmarshalJSON([]byte(data))
|
||||
if err != nil {
|
||||
logging.OnError(err).Fatal("unmarshalling user json")
|
||||
}
|
||||
return user
|
||||
}
|
251
internal/api/grpc/resources/userschema/v3alpha/query.go
Normal file
251
internal/api/grpc/resources/userschema/v3alpha/query.go
Normal file
@ -0,0 +1,251 @@
|
||||
package userschema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
func (s *Server) SearchUserSchemas(ctx context.Context, req *schema.SearchUserSchemasRequest) (*schema.SearchUserSchemasResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := s.searchUserSchemaToModel(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserSchema(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchemas, err := userSchemasToPb(res.UserSchemas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.SearchUserSchemasResponse{
|
||||
Details: resource_object.ToSearchDetailsPb(queries.SearchRequest, res.SearchResponse),
|
||||
Result: userSchemas,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetUserSchemaByID(ctx context.Context, req *schema.GetUserSchemaByIDRequest) (*schema.GetUserSchemaByIDResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.GetUserSchemaByID(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchema, err := userSchemaToPb(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.GetUserSchemaByIDResponse{
|
||||
Schema: userSchema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) searchUserSchemaToModel(req *schema.SearchUserSchemasRequest) (*query.UserSchemaSearchQueries, error) {
|
||||
offset, limit, asc, err := resource_object.SearchQueryPbToQuery(s.systemDefaults, req.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := userSchemaFiltersToQuery(req.Filters, 0) // start at level 0
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.UserSchemaSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: userSchemaFieldNameToSortingColumn(req.SortingColumn),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userSchemaFieldNameToSortingColumn(field *schema.FieldName) query.Column {
|
||||
if field == nil {
|
||||
return query.UserSchemaCreationDateCol
|
||||
}
|
||||
switch *field {
|
||||
case schema.FieldName_FIELD_NAME_TYPE:
|
||||
return query.UserSchemaTypeCol
|
||||
case schema.FieldName_FIELD_NAME_STATE:
|
||||
return query.UserSchemaStateCol
|
||||
case schema.FieldName_FIELD_NAME_REVISION:
|
||||
return query.UserSchemaRevisionCol
|
||||
case schema.FieldName_FIELD_NAME_CHANGE_DATE:
|
||||
return query.UserSchemaChangeDateCol
|
||||
case schema.FieldName_FIELD_NAME_CREATION_DATE:
|
||||
return query.UserSchemaCreationDateCol
|
||||
case schema.FieldName_FIELD_NAME_UNSPECIFIED:
|
||||
return query.UserSchemaIDCol
|
||||
default:
|
||||
return query.UserSchemaIDCol
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemasToPb(schemas []*query.UserSchema) (_ []*schema.UserSchema, err error) {
|
||||
userSchemas := make([]*schema.UserSchema, len(schemas))
|
||||
for i, userSchema := range schemas {
|
||||
userSchemas[i], err = userSchemaToPb(userSchema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return userSchemas, nil
|
||||
}
|
||||
|
||||
func userSchemaToPb(userSchema *query.UserSchema) (*schema.UserSchema, error) {
|
||||
s := new(structpb.Struct)
|
||||
if err := s.UnmarshalJSON(userSchema.Schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.UserSchema{
|
||||
Details: resource_object.DomainToDetailsPb(&userSchema.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, userSchema.ResourceOwner),
|
||||
Type: userSchema.Type,
|
||||
State: userSchemaStateToPb(userSchema.State),
|
||||
Revision: userSchema.Revision,
|
||||
Schema: s,
|
||||
PossibleAuthenticators: authenticatorTypesToPb(userSchema.PossibleAuthenticators),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authenticatorTypesToPb(authenticators []domain.AuthenticatorType) []schema.AuthenticatorType {
|
||||
authTypes := make([]schema.AuthenticatorType, len(authenticators))
|
||||
for i, authenticator := range authenticators {
|
||||
authTypes[i] = authenticatorTypeToPb(authenticator)
|
||||
}
|
||||
return authTypes
|
||||
}
|
||||
|
||||
func authenticatorTypeToPb(authenticator domain.AuthenticatorType) schema.AuthenticatorType {
|
||||
switch authenticator {
|
||||
case domain.AuthenticatorTypeUsername:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME
|
||||
case domain.AuthenticatorTypePassword:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_PASSWORD
|
||||
case domain.AuthenticatorTypeWebAuthN:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_WEBAUTHN
|
||||
case domain.AuthenticatorTypeTOTP:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_TOTP
|
||||
case domain.AuthenticatorTypeOTPEmail:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_EMAIL
|
||||
case domain.AuthenticatorTypeOTPSMS:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_SMS
|
||||
case domain.AuthenticatorTypeAuthenticationKey:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_AUTHENTICATION_KEY
|
||||
case domain.AuthenticatorTypeIdentityProvider:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_IDENTITY_PROVIDER
|
||||
case domain.AuthenticatorTypeUnspecified:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED
|
||||
default:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaStateToPb(state domain.UserSchemaState) schema.State {
|
||||
switch state {
|
||||
case domain.UserSchemaStateActive:
|
||||
return schema.State_STATE_ACTIVE
|
||||
case domain.UserSchemaStateInactive:
|
||||
return schema.State_STATE_INACTIVE
|
||||
case domain.UserSchemaStateUnspecified,
|
||||
domain.UserSchemaStateDeleted:
|
||||
return schema.State_STATE_UNSPECIFIED
|
||||
default:
|
||||
return schema.State_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaStateToDomain(state schema.State) domain.UserSchemaState {
|
||||
switch state {
|
||||
case schema.State_STATE_ACTIVE:
|
||||
return domain.UserSchemaStateActive
|
||||
case schema.State_STATE_INACTIVE:
|
||||
return domain.UserSchemaStateInactive
|
||||
case schema.State_STATE_UNSPECIFIED:
|
||||
return domain.UserSchemaStateUnspecified
|
||||
default:
|
||||
return domain.UserSchemaStateUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaFiltersToQuery(queries []*schema.SearchFilter, level uint8) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = userSchemaFilterToQuery(query, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func userSchemaFilterToQuery(query *schema.SearchFilter, level uint8) (query.SearchQuery, error) {
|
||||
if level > 20 {
|
||||
// can't go deeper than 20 levels of nesting.
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-zsQ97", "Errors.Query.TooManyNestingLevels")
|
||||
}
|
||||
switch q := query.Filter.(type) {
|
||||
case *schema.SearchFilter_StateFilter:
|
||||
return stateQueryToQuery(q.StateFilter)
|
||||
case *schema.SearchFilter_TypeFilter:
|
||||
return typeQueryToQuery(q.TypeFilter)
|
||||
case *schema.SearchFilter_IdFilter:
|
||||
return idQueryToQuery(q.IdFilter)
|
||||
case *schema.SearchFilter_OrFilter:
|
||||
return orQueryToQuery(q.OrFilter, level)
|
||||
case *schema.SearchFilter_AndFilter:
|
||||
return andQueryToQuery(q.AndFilter, level)
|
||||
case *schema.SearchFilter_NotFilter:
|
||||
return notQueryToQuery(q.NotFilter, level)
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-vR9nC", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func stateQueryToQuery(q *schema.StateFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaStateSearchQuery(userSchemaStateToDomain(q.GetState()))
|
||||
}
|
||||
|
||||
func typeQueryToQuery(q *schema.TypeFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaTypeSearchQuery(q.GetType(), resource_object.TextMethodPbToQuery(q.GetMethod()))
|
||||
}
|
||||
|
||||
func idQueryToQuery(q *schema.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaIDSearchQuery(q.GetId(), resource_object.TextMethodPbToQuery(q.GetMethod()))
|
||||
}
|
||||
|
||||
func orQueryToQuery(q *schema.OrFilter, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := userSchemaFiltersToQuery(q.GetQueries(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserOrSearchQuery(mappedQueries)
|
||||
}
|
||||
|
||||
func andQueryToQuery(q *schema.AndFilter, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := userSchemaFiltersToQuery(q.GetQueries(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserAndSearchQuery(mappedQueries)
|
||||
}
|
||||
|
||||
func notQueryToQuery(q *schema.NotFilter, level uint8) (query.SearchQuery, error) {
|
||||
mappedQuery, err := userSchemaFilterToQuery(q.GetFilter(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserNotSearchQuery(mappedQuery)
|
||||
}
|
@ -0,0 +1,302 @@
|
||||
//go:build integration
|
||||
|
||||
package userschema_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc"
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
func TestServer_ListUserSchemas(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
userSchema := new(structpb.Struct)
|
||||
err := userSchema.UnmarshalJSON([]byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}`))
|
||||
require.NoError(t, err)
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.SearchUserSchemasRequest
|
||||
prepare func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *schema.SearchUserSchemasResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
args: args{
|
||||
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
|
||||
req: &schema.SearchUserSchemasRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "not found, error",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.SearchUserSchemasRequest{
|
||||
Filters: []*schema.SearchFilter{
|
||||
{
|
||||
Filter: &schema.SearchFilter_IdFilter{
|
||||
IdFilter: &schema.IDFilter{
|
||||
Id: "notexisting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.SearchUserSchemasResponse{
|
||||
Details: &object.ListDetails{
|
||||
TotalResult: 0,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Result: []*schema.UserSchema{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single (id), ok",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.SearchUserSchemasRequest{},
|
||||
prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error {
|
||||
schemaType := fmt.Sprint(time.Now().UnixNano() + 1)
|
||||
createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
|
||||
request.Filters = []*schema.SearchFilter{
|
||||
{
|
||||
Filter: &schema.SearchFilter_IdFilter{
|
||||
IdFilter: &schema.IDFilter{
|
||||
Id: createResp.GetDetails().GetId(),
|
||||
Method: object.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
resp.Result[0].Type = schemaType
|
||||
resp.Result[0].Details = createResp.GetDetails()
|
||||
// as schema is freshly created, the changed date is the created date
|
||||
resp.Result[0].Details.Created = resp.Result[0].Details.GetChanged()
|
||||
resp.Details.Timestamp = resp.Result[0].Details.GetChanged()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: &schema.SearchUserSchemasResponse{
|
||||
Details: &object.ListDetails{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Result: []*schema.UserSchema{
|
||||
{
|
||||
State: schema.State_STATE_ACTIVE,
|
||||
Revision: 1,
|
||||
Schema: userSchema,
|
||||
PossibleAuthenticators: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple (type), ok",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.SearchUserSchemasRequest{},
|
||||
prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error {
|
||||
schemaType := fmt.Sprint(time.Now().UnixNano())
|
||||
schemaType1 := schemaType + "_1"
|
||||
schemaType2 := schemaType + "_2"
|
||||
createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType1)
|
||||
createResp2 := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType2)
|
||||
|
||||
request.SortingColumn = gu.Ptr(schema.FieldName_FIELD_NAME_TYPE)
|
||||
request.Query = &object.SearchQuery{Desc: false}
|
||||
request.Filters = []*schema.SearchFilter{
|
||||
{
|
||||
Filter: &schema.SearchFilter_TypeFilter{
|
||||
TypeFilter: &schema.TypeFilter{
|
||||
Type: schemaType,
|
||||
Method: object.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
resp.Result[0].Type = schemaType1
|
||||
resp.Result[0].Details = createResp.GetDetails()
|
||||
resp.Result[1].Type = schemaType2
|
||||
resp.Result[1].Details = createResp2.GetDetails()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: &schema.SearchUserSchemasResponse{
|
||||
Details: &object.ListDetails{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Result: []*schema.UserSchema{
|
||||
{
|
||||
State: schema.State_STATE_ACTIVE,
|
||||
Revision: 1,
|
||||
Schema: userSchema,
|
||||
PossibleAuthenticators: nil,
|
||||
},
|
||||
{
|
||||
State: schema.State_STATE_ACTIVE,
|
||||
Revision: 1,
|
||||
Schema: userSchema,
|
||||
PossibleAuthenticators: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.prepare != nil {
|
||||
err := tt.args.prepare(tt.args.req, tt.want)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
retryDuration := 20 * time.Second
|
||||
if ctxDeadline, ok := IAMOwnerCTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, err := Client.SearchUserSchemas(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(ttt, err)
|
||||
|
||||
// always first check length, otherwise its failed anyway
|
||||
assert.Len(ttt, got.Result, len(tt.want.Result))
|
||||
for i := range tt.want.Result {
|
||||
want := tt.want.Result[i]
|
||||
got := got.Result[i]
|
||||
|
||||
integration.AssertResourceDetails(t, want.GetDetails(), got.GetDetails())
|
||||
want.Details = got.Details
|
||||
grpc.AllFieldsEqual(t, want.ProtoReflect(), got.ProtoReflect(), grpc.CustomMappers)
|
||||
}
|
||||
integration.AssertListDetails(t, tt.want, got)
|
||||
}, retryDuration, time.Millisecond*100, "timeout waiting for expected user schema result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_GetUserSchemaByID(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
userSchema := new(structpb.Struct)
|
||||
err := userSchema.UnmarshalJSON([]byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}`))
|
||||
require.NoError(t, err)
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.GetUserSchemaByIDRequest
|
||||
prepare func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *schema.GetUserSchemaByIDResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
args: args{
|
||||
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
|
||||
req: &schema.GetUserSchemaByIDRequest{},
|
||||
prepare: func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error {
|
||||
schemaType := fmt.Sprint(time.Now().UnixNano() + 1)
|
||||
createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
|
||||
request.Id = createResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "not existing, error",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.GetUserSchemaByIDRequest{
|
||||
Id: "notexisting",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "get, ok",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.GetUserSchemaByIDRequest{},
|
||||
prepare: func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error {
|
||||
schemaType := fmt.Sprint(time.Now().UnixNano() + 1)
|
||||
createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
|
||||
request.Id = createResp.GetDetails().GetId()
|
||||
|
||||
resp.Schema.Type = schemaType
|
||||
resp.Schema.Details = createResp.GetDetails()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: &schema.GetUserSchemaByIDResponse{
|
||||
Schema: &schema.UserSchema{
|
||||
State: schema.State_STATE_ACTIVE,
|
||||
Revision: 1,
|
||||
Schema: userSchema,
|
||||
PossibleAuthenticators: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.prepare != nil {
|
||||
err := tt.args.prepare(tt.args.req, tt.want)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
retryDuration := 5 * time.Second
|
||||
if ctxDeadline, ok := IAMOwnerCTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, err := Client.GetUserSchemaByID(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(ttt, err)
|
||||
|
||||
integration.AssertResourceDetails(t, tt.want.GetSchema().GetDetails(), got.GetSchema().GetDetails())
|
||||
tt.want.Schema.Details = got.GetSchema().GetDetails()
|
||||
grpc.AllFieldsEqual(t, tt.want.ProtoReflect(), got.ProtoReflect(), grpc.CustomMappers)
|
||||
}, retryDuration, time.Millisecond*100, "timeout waiting for expected user schema result")
|
||||
})
|
||||
}
|
||||
}
|
65
internal/api/grpc/resources/userschema/v3alpha/server.go
Normal file
65
internal/api/grpc/resources/userschema/v3alpha/server.go
Normal file
@ -0,0 +1,65 @@
|
||||
package userschema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
var _ schema.ZITADELUserSchemasServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
schema.UnimplementedZITADELUserSchemasServer
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
schema.RegisterZITADELUserSchemasServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return schema.ZITADELUserSchemas_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return schema.ZITADELUserSchemas_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return schema.ZITADELUserSchemas_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return schema.RegisterZITADELUserSchemasHandler
|
||||
}
|
||||
|
||||
func checkUserSchemaEnabled(ctx context.Context) error {
|
||||
if authz.GetInstance(ctx).Features().UserSchema {
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "SCHEMA-SFjk3", "Errors.UserSchema.NotEnabled")
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
//go:build integration
|
||||
|
||||
package userschema_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
var (
|
||||
IAMOwnerCTX, SystemCTX context.Context
|
||||
Tester *integration.Tester
|
||||
Client schema.ZITADELUserSchemasClient
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, _, cancel := integration.Contexts(5 * time.Minute)
|
||||
defer cancel()
|
||||
|
||||
Tester = integration.NewTester(ctx)
|
||||
defer Tester.Done()
|
||||
|
||||
IAMOwnerCTX = Tester.WithAuthorization(ctx, integration.IAMOwner)
|
||||
SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser)
|
||||
Client = Tester.Client.UserSchemaV3
|
||||
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func ensureFeatureEnabled(t *testing.T, iamOwnerCTX context.Context) {
|
||||
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if f.UserSchema.GetEnabled() {
|
||||
return
|
||||
}
|
||||
_, err = Tester.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
|
||||
UserSchema: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
retryDuration := time.Minute
|
||||
if ctxDeadline, ok := iamOwnerCTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
require.EventuallyWithT(t,
|
||||
func(ttt *assert.CollectT) {
|
||||
f, err := Tester.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(ttt, err)
|
||||
if f.UserSchema.GetEnabled() {
|
||||
return
|
||||
}
|
||||
},
|
||||
retryDuration,
|
||||
100*time.Millisecond,
|
||||
"timed out waiting for ensuring instance feature")
|
||||
}
|
149
internal/api/grpc/resources/userschema/v3alpha/userschema.go
Normal file
149
internal/api/grpc/resources/userschema/v3alpha/userschema.go
Normal file
@ -0,0 +1,149 @@
|
||||
package userschema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
func (s *Server) CreateUserSchema(ctx context.Context, req *schema.CreateUserSchemaRequest) (*schema.CreateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
userSchema, err := createUserSchemaToCommand(req, instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.command.CreateUserSchema(ctx, userSchema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.CreateUserSchemaResponse{
|
||||
Details: resource_object.DomainToDetailsPb(userSchema.Details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) PatchUserSchema(ctx context.Context, req *schema.PatchUserSchemaRequest) (*schema.PatchUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
userSchema, err := patchUserSchemaToCommand(req, instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.command.ChangeUserSchema(ctx, userSchema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.PatchUserSchemaResponse{
|
||||
Details: resource_object.DomainToDetailsPb(userSchema.Details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeactivateUserSchema(ctx context.Context, req *schema.DeactivateUserSchemaRequest) (*schema.DeactivateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
details, err := s.command.DeactivateUserSchema(ctx, req.GetId(), instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.DeactivateUserSchemaResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ReactivateUserSchema(ctx context.Context, req *schema.ReactivateUserSchemaRequest) (*schema.ReactivateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
details, err := s.command.ReactivateUserSchema(ctx, req.GetId(), instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.ReactivateUserSchemaResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUserSchema(ctx context.Context, req *schema.DeleteUserSchemaRequest) (*schema.DeleteUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
details, err := s.command.DeleteUserSchema(ctx, req.GetId(), instanceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.DeleteUserSchemaResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_INSTANCE, instanceID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createUserSchemaToCommand(req *schema.CreateUserSchemaRequest, resourceOwner string) (*command.CreateUserSchema, error) {
|
||||
schema, err := req.GetUserSchema().GetSchema().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.CreateUserSchema{
|
||||
ResourceOwner: resourceOwner,
|
||||
Type: req.GetUserSchema().GetType(),
|
||||
Schema: schema,
|
||||
PossibleAuthenticators: authenticatorsToDomain(req.GetUserSchema().GetPossibleAuthenticators()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func patchUserSchemaToCommand(req *schema.PatchUserSchemaRequest, resourceOwner string) (*command.ChangeUserSchema, error) {
|
||||
schema, err := req.GetSchema().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.ChangeUserSchema{
|
||||
ID: req.GetId(),
|
||||
ResourceOwner: resourceOwner,
|
||||
Type: req.Type,
|
||||
Schema: schema,
|
||||
PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authenticatorsToDomain(authenticators []schema.AuthenticatorType) []domain.AuthenticatorType {
|
||||
types := make([]domain.AuthenticatorType, len(authenticators))
|
||||
for i, authenticator := range authenticators {
|
||||
types[i] = authenticatorTypeToDomain(authenticator)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func authenticatorTypeToDomain(authenticator schema.AuthenticatorType) domain.AuthenticatorType {
|
||||
switch authenticator {
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED:
|
||||
return domain.AuthenticatorTypeUnspecified
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME:
|
||||
return domain.AuthenticatorTypeUsername
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_PASSWORD:
|
||||
return domain.AuthenticatorTypePassword
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_WEBAUTHN:
|
||||
return domain.AuthenticatorTypeWebAuthN
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_TOTP:
|
||||
return domain.AuthenticatorTypeTOTP
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_EMAIL:
|
||||
return domain.AuthenticatorTypeOTPEmail
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_SMS:
|
||||
return domain.AuthenticatorTypeOTPSMS
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_AUTHENTICATION_KEY:
|
||||
return domain.AuthenticatorTypeAuthenticationKey
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_IDENTITY_PROVIDER:
|
||||
return domain.AuthenticatorTypeIdentityProvider
|
||||
default:
|
||||
return domain.AuthenticatorTypeUnspecified
|
||||
}
|
||||
}
|
@ -0,0 +1,811 @@
|
||||
//go:build integration
|
||||
|
||||
package userschema_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
)
|
||||
|
||||
func TestServer_CreateUserSchema(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *schema.CreateUserSchemaRequest
|
||||
want *schema.CreateUserSchemaResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission, error",
|
||||
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty type",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: "",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty schema, error",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid schema, error",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no authenticators, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.CreateUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid authenticator, error",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with authenticator, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.CreateUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with invalid permission, error",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": "read"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "with valid permission, ok",
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.CreateUserSchemaRequest{
|
||||
UserSchema: &schema.CreateUserSchema{
|
||||
Type: gofakeit.Name(),
|
||||
DataType: &schema.CreateUserSchema_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"urn:zitadel:schema:permission": {
|
||||
"owner": "rw",
|
||||
"self": "r"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.CreateUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.CreateUserSchema(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
integration.AssertResourceDetails(t, tt.want.GetDetails(), got.GetDetails())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_UpdateUserSchema(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.PatchUserSchemaRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(request *schema.PatchUserSchemaRequest) error
|
||||
args args
|
||||
want *schema.PatchUserSchemaResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner),
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing id, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "not existing, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
request.Id = "notexisting"
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty type, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
Type: gu.Ptr(""),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update type, ok",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)),
|
||||
},
|
||||
},
|
||||
want: &schema.PatchUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty schema, ok",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
DataType: &schema.PatchUserSchemaRequest_Schema{},
|
||||
},
|
||||
},
|
||||
want: &schema.PatchUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid schema, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
DataType: &schema.PatchUserSchemaRequest_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update schema, ok",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
DataType: &schema.PatchUserSchemaRequest_Schema{
|
||||
Schema: func() *structpb.Struct {
|
||||
s := new(structpb.Struct)
|
||||
err := s.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.PatchUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid authenticator, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update authenticator, ok",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
PossibleAuthenticators: []schema.AuthenticatorType{
|
||||
schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &schema.PatchUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "inactive, error",
|
||||
prepare: func(request *schema.PatchUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
|
||||
Id: schemaID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.PatchUserSchemaRequest{
|
||||
Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.prepare(tt.args.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := Client.PatchUserSchema(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want.GetDetails(), got.GetDetails())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeactivateUserSchema(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.DeactivateUserSchemaRequest
|
||||
prepare func(request *schema.DeactivateUserSchemaRequest) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *schema.DeactivateUserSchemaResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not existing, error",
|
||||
args: args{
|
||||
IAMOwnerCTX,
|
||||
&schema.DeactivateUserSchemaRequest{
|
||||
Id: "notexisting",
|
||||
},
|
||||
func(request *schema.DeactivateUserSchemaRequest) error { return nil },
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "active, ok",
|
||||
args: args{
|
||||
IAMOwnerCTX,
|
||||
&schema.DeactivateUserSchemaRequest{},
|
||||
func(request *schema.DeactivateUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: &schema.DeactivateUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "inactive, error",
|
||||
args: args{
|
||||
IAMOwnerCTX,
|
||||
&schema.DeactivateUserSchemaRequest{},
|
||||
func(request *schema.DeactivateUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
|
||||
Id: schemaID,
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.args.prepare(tt.args.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := Client.DeactivateUserSchema(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want.GetDetails(), got.GetDetails())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ReactivateUserSchema(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.ReactivateUserSchemaRequest
|
||||
prepare func(request *schema.ReactivateUserSchemaRequest) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *schema.ReactivateUserSchemaResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not existing, error",
|
||||
args: args{
|
||||
IAMOwnerCTX,
|
||||
&schema.ReactivateUserSchemaRequest{
|
||||
Id: "notexisting",
|
||||
},
|
||||
func(request *schema.ReactivateUserSchemaRequest) error { return nil },
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "active, error",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.ReactivateUserSchemaRequest{},
|
||||
prepare: func(request *schema.ReactivateUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "inactive, ok",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.ReactivateUserSchemaRequest{},
|
||||
prepare: func(request *schema.ReactivateUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
|
||||
Id: schemaID,
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
want: &schema.ReactivateUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.args.prepare(tt.args.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := Client.ReactivateUserSchema(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want.GetDetails(), got.GetDetails())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeleteUserSchema(t *testing.T) {
|
||||
ensureFeatureEnabled(t, IAMOwnerCTX)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *schema.DeleteUserSchemaRequest
|
||||
prepare func(request *schema.DeleteUserSchemaRequest) error
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *schema.DeleteUserSchemaResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "not existing, error",
|
||||
args: args{
|
||||
IAMOwnerCTX,
|
||||
&schema.DeleteUserSchemaRequest{
|
||||
Id: "notexisting",
|
||||
},
|
||||
func(request *schema.DeleteUserSchemaRequest) error { return nil },
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, ok",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.DeleteUserSchemaRequest{},
|
||||
prepare: func(request *schema.DeleteUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: &schema.DeleteUserSchemaResponse{
|
||||
Details: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
|
||||
Id: Tester.Instance.InstanceID(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deleted, error",
|
||||
args: args{
|
||||
ctx: IAMOwnerCTX,
|
||||
req: &schema.DeleteUserSchemaRequest{},
|
||||
prepare: func(request *schema.DeleteUserSchemaRequest) error {
|
||||
schemaID := Tester.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
|
||||
request.Id = schemaID
|
||||
_, err := Client.DeleteUserSchema(IAMOwnerCTX, &schema.DeleteUserSchemaRequest{
|
||||
Id: schemaID,
|
||||
})
|
||||
return err
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.args.prepare(tt.args.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := Client.DeleteUserSchema(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want.GetDetails(), got.GetDetails())
|
||||
})
|
||||
}
|
||||
}
|
@ -1,386 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha"
|
||||
)
|
||||
|
||||
func (s *Server) CreateUserSchema(ctx context.Context, req *schema.CreateUserSchemaRequest) (*schema.CreateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchema, err := createUserSchemaToCommand(req, authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, details, err := s.command.CreateUserSchema(ctx, userSchema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.CreateUserSchemaResponse{
|
||||
Id: id,
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateUserSchema(ctx context.Context, req *schema.UpdateUserSchemaRequest) (*schema.UpdateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchema, err := updateUserSchemaToCommand(req, authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.UpdateUserSchema(ctx, userSchema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.UpdateUserSchemaResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeactivateUserSchema(ctx context.Context, req *schema.DeactivateUserSchemaRequest) (*schema.DeactivateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.DeactivateUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.DeactivateUserSchemaResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ReactivateUserSchema(ctx context.Context, req *schema.ReactivateUserSchemaRequest) (*schema.ReactivateUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.ReactivateUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.ReactivateUserSchemaResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUserSchema(ctx context.Context, req *schema.DeleteUserSchemaRequest) (*schema.DeleteUserSchemaResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.DeleteUserSchema(ctx, req.GetId(), authz.GetInstance(ctx).InstanceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.DeleteUserSchemaResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListUserSchemas(ctx context.Context, req *schema.ListUserSchemasRequest) (*schema.ListUserSchemasResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := listUserSchemaToQuery(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserSchema(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchemas, err := userSchemasToPb(res.UserSchemas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.ListUserSchemasResponse{
|
||||
Details: object.ToListDetails(res.SearchResponse),
|
||||
Result: userSchemas,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetUserSchemaByID(ctx context.Context, req *schema.GetUserSchemaByIDRequest) (*schema.GetUserSchemaByIDResponse, error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.GetUserSchemaByID(ctx, req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userSchema, err := userSchemaToPb(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.GetUserSchemaByIDResponse{
|
||||
Schema: userSchema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userSchemasToPb(schemas []*query.UserSchema) (_ []*schema.UserSchema, err error) {
|
||||
userSchemas := make([]*schema.UserSchema, len(schemas))
|
||||
for i, userSchema := range schemas {
|
||||
userSchemas[i], err = userSchemaToPb(userSchema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return userSchemas, nil
|
||||
}
|
||||
|
||||
func userSchemaToPb(userSchema *query.UserSchema) (*schema.UserSchema, error) {
|
||||
s := new(structpb.Struct)
|
||||
if err := s.UnmarshalJSON(userSchema.Schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &schema.UserSchema{
|
||||
Id: userSchema.ID,
|
||||
Details: object.DomainToDetailsPb(&userSchema.ObjectDetails),
|
||||
Type: userSchema.Type,
|
||||
State: userSchemaStateToPb(userSchema.State),
|
||||
Revision: userSchema.Revision,
|
||||
Schema: s,
|
||||
PossibleAuthenticators: authenticatorTypesToPb(userSchema.PossibleAuthenticators),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authenticatorTypesToPb(authenticators []domain.AuthenticatorType) []schema.AuthenticatorType {
|
||||
authTypes := make([]schema.AuthenticatorType, len(authenticators))
|
||||
for i, authenticator := range authenticators {
|
||||
authTypes[i] = authenticatorTypeToPb(authenticator)
|
||||
}
|
||||
return authTypes
|
||||
}
|
||||
|
||||
func authenticatorTypeToPb(authenticator domain.AuthenticatorType) schema.AuthenticatorType {
|
||||
switch authenticator {
|
||||
case domain.AuthenticatorTypeUsername:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME
|
||||
case domain.AuthenticatorTypePassword:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_PASSWORD
|
||||
case domain.AuthenticatorTypeWebAuthN:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_WEBAUTHN
|
||||
case domain.AuthenticatorTypeTOTP:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_TOTP
|
||||
case domain.AuthenticatorTypeOTPEmail:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_EMAIL
|
||||
case domain.AuthenticatorTypeOTPSMS:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_SMS
|
||||
case domain.AuthenticatorTypeAuthenticationKey:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_AUTHENTICATION_KEY
|
||||
case domain.AuthenticatorTypeIdentityProvider:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_IDENTITY_PROVIDER
|
||||
case domain.AuthenticatorTypeUnspecified:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED
|
||||
default:
|
||||
return schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaStateToPb(state domain.UserSchemaState) schema.State {
|
||||
switch state {
|
||||
case domain.UserSchemaStateActive:
|
||||
return schema.State_STATE_ACTIVE
|
||||
case domain.UserSchemaStateInactive:
|
||||
return schema.State_STATE_INACTIVE
|
||||
case domain.UserSchemaStateUnspecified,
|
||||
domain.UserSchemaStateDeleted:
|
||||
return schema.State_STATE_UNSPECIFIED
|
||||
default:
|
||||
return schema.State_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func listUserSchemaToQuery(req *schema.ListUserSchemasRequest) (*query.UserSchemaSearchQueries, error) {
|
||||
offset, limit, asc := object.ListQueryToQuery(req.Query)
|
||||
queries, err := userSchemaQueriesToQuery(req.Queries, 0) // start at level 0
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.UserSchemaSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: userSchemaFieldNameToSortingColumn(req.SortingColumn),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userSchemaFieldNameToSortingColumn(column schema.FieldName) query.Column {
|
||||
switch column {
|
||||
case schema.FieldName_FIELD_NAME_TYPE:
|
||||
return query.UserSchemaTypeCol
|
||||
case schema.FieldName_FIELD_NAME_STATE:
|
||||
return query.UserSchemaStateCol
|
||||
case schema.FieldName_FIELD_NAME_REVISION:
|
||||
return query.UserSchemaRevisionCol
|
||||
case schema.FieldName_FIELD_NAME_CHANGE_DATE:
|
||||
return query.UserSchemaChangeDateCol
|
||||
case schema.FieldName_FIELD_NAME_UNSPECIFIED:
|
||||
return query.UserSchemaIDCol
|
||||
default:
|
||||
return query.UserSchemaIDCol
|
||||
}
|
||||
}
|
||||
|
||||
func checkUserSchemaEnabled(ctx context.Context) error {
|
||||
if authz.GetInstance(ctx).Features().UserSchema {
|
||||
return nil
|
||||
}
|
||||
return zerrors.ThrowPreconditionFailed(nil, "SCHEMA-SFjk3", "Errors.UserSchema.NotEnabled")
|
||||
}
|
||||
|
||||
func createUserSchemaToCommand(req *schema.CreateUserSchemaRequest, resourceOwner string) (*command.CreateUserSchema, error) {
|
||||
schema, err := req.GetSchema().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.CreateUserSchema{
|
||||
ResourceOwner: resourceOwner,
|
||||
Type: req.GetType(),
|
||||
Schema: schema,
|
||||
PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func updateUserSchemaToCommand(req *schema.UpdateUserSchemaRequest, resourceOwner string) (*command.UpdateUserSchema, error) {
|
||||
schema, err := req.GetSchema().MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &command.UpdateUserSchema{
|
||||
ID: req.GetId(),
|
||||
ResourceOwner: resourceOwner,
|
||||
Type: req.Type,
|
||||
Schema: schema,
|
||||
PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authenticatorsToDomain(authenticators []schema.AuthenticatorType) []domain.AuthenticatorType {
|
||||
types := make([]domain.AuthenticatorType, len(authenticators))
|
||||
for i, authenticator := range authenticators {
|
||||
types[i] = authenticatorTypeToDomain(authenticator)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func authenticatorTypeToDomain(authenticator schema.AuthenticatorType) domain.AuthenticatorType {
|
||||
switch authenticator {
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED:
|
||||
return domain.AuthenticatorTypeUnspecified
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME:
|
||||
return domain.AuthenticatorTypeUsername
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_PASSWORD:
|
||||
return domain.AuthenticatorTypePassword
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_WEBAUTHN:
|
||||
return domain.AuthenticatorTypeWebAuthN
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_TOTP:
|
||||
return domain.AuthenticatorTypeTOTP
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_EMAIL:
|
||||
return domain.AuthenticatorTypeOTPEmail
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_OTP_SMS:
|
||||
return domain.AuthenticatorTypeOTPSMS
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_AUTHENTICATION_KEY:
|
||||
return domain.AuthenticatorTypeAuthenticationKey
|
||||
case schema.AuthenticatorType_AUTHENTICATOR_TYPE_IDENTITY_PROVIDER:
|
||||
return domain.AuthenticatorTypeIdentityProvider
|
||||
default:
|
||||
return domain.AuthenticatorTypeUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaStateToDomain(state schema.State) domain.UserSchemaState {
|
||||
switch state {
|
||||
case schema.State_STATE_ACTIVE:
|
||||
return domain.UserSchemaStateActive
|
||||
case schema.State_STATE_INACTIVE:
|
||||
return domain.UserSchemaStateInactive
|
||||
case schema.State_STATE_UNSPECIFIED:
|
||||
return domain.UserSchemaStateUnspecified
|
||||
default:
|
||||
return domain.UserSchemaStateUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
func userSchemaQueriesToQuery(queries []*schema.SearchQuery, level uint8) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = userSchemaQueryToQuery(query, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func userSchemaQueryToQuery(query *schema.SearchQuery, level uint8) (query.SearchQuery, error) {
|
||||
if level > 20 {
|
||||
// can't go deeper than 20 levels of nesting.
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-zsQ97", "Errors.Query.TooManyNestingLevels")
|
||||
}
|
||||
switch q := query.Query.(type) {
|
||||
case *schema.SearchQuery_StateQuery:
|
||||
return stateQueryToQuery(q.StateQuery)
|
||||
case *schema.SearchQuery_TypeQuery:
|
||||
return typeQueryToQuery(q.TypeQuery)
|
||||
case *schema.SearchQuery_IdQuery:
|
||||
return idQueryToQuery(q.IdQuery)
|
||||
case *schema.SearchQuery_OrQuery:
|
||||
return orQueryToQuery(q.OrQuery, level)
|
||||
case *schema.SearchQuery_AndQuery:
|
||||
return andQueryToQuery(q.AndQuery, level)
|
||||
case *schema.SearchQuery_NotQuery:
|
||||
return notQueryToQuery(q.NotQuery, level)
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-vR9nC", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func stateQueryToQuery(q *schema.StateQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaStateSearchQuery(userSchemaStateToDomain(q.GetState()))
|
||||
}
|
||||
|
||||
func typeQueryToQuery(q *schema.TypeQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaTypeSearchQuery(q.GetType(), object.TextMethodToQuery(q.GetMethod()))
|
||||
}
|
||||
|
||||
func idQueryToQuery(q *schema.IDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserSchemaIDSearchQuery(q.GetId(), object.TextMethodToQuery(q.GetMethod()))
|
||||
}
|
||||
|
||||
func orQueryToQuery(q *schema.OrQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := userSchemaQueriesToQuery(q.GetQueries(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserOrSearchQuery(mappedQueries)
|
||||
}
|
||||
|
||||
func andQueryToQuery(q *schema.AndQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := userSchemaQueriesToQuery(q.GetQueries(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserAndSearchQuery(mappedQueries)
|
||||
}
|
||||
|
||||
func notQueryToQuery(q *schema.NotQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQuery, err := userSchemaQueryToQuery(q.GetQuery(), level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserNotSearchQuery(mappedQuery)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,51 +0,0 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha"
|
||||
)
|
||||
|
||||
var _ schema.UserSchemaServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
schema.UnimplementedUserSchemaServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
schema.RegisterUserSchemaServiceServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return schema.UserSchemaService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return schema.UserSchemaService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return schema.UserSchemaService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return schema.RegisterUserSchemaServiceHandler
|
||||
}
|
@ -12,6 +12,8 @@ import (
|
||||
)
|
||||
|
||||
type CreateUserSchema struct {
|
||||
Details *domain.ObjectDetails
|
||||
|
||||
ResourceOwner string
|
||||
Type string
|
||||
Schema json.RawMessage
|
||||
@ -33,7 +35,9 @@ func (s *CreateUserSchema) Valid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateUserSchema struct {
|
||||
type ChangeUserSchema struct {
|
||||
Details *domain.ObjectDetails
|
||||
|
||||
ID string
|
||||
ResourceOwner string
|
||||
Type *string
|
||||
@ -41,7 +45,7 @@ type UpdateUserSchema struct {
|
||||
PossibleAuthenticators []domain.AuthenticatorType
|
||||
}
|
||||
|
||||
func (s *UpdateUserSchema) Valid() error {
|
||||
func (s *ChangeUserSchema) Valid() error {
|
||||
if s.ID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-H5421", "Errors.IDMissing")
|
||||
}
|
||||
@ -59,40 +63,43 @@ func (s *UpdateUserSchema) Valid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) CreateUserSchema(ctx context.Context, userSchema *CreateUserSchema) (string, *domain.ObjectDetails, error) {
|
||||
func (c *Commands) CreateUserSchema(ctx context.Context, userSchema *CreateUserSchema) error {
|
||||
if err := userSchema.Valid(); err != nil {
|
||||
return "", nil, err
|
||||
return err
|
||||
}
|
||||
if userSchema.ResourceOwner == "" {
|
||||
return "", nil, zerrors.ThrowInvalidArgument(nil, "COMMA-J3hhj", "Errors.ResourceOwnerMissing")
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMA-J3hhj", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
id, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return err
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, userSchema.ResourceOwner)
|
||||
err = c.pushAppendAndReduce(ctx, writeModel,
|
||||
writeModel, err := c.getSchemaWriteModelByID(ctx, userSchema.ResourceOwner, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewCreatedEvent(ctx,
|
||||
UserSchemaAggregateFromWriteModel(&writeModel.WriteModel),
|
||||
userSchema.Type, userSchema.Schema, userSchema.PossibleAuthenticators,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return id, writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
userSchema.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) UpdateUserSchema(ctx context.Context, userSchema *UpdateUserSchema) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) ChangeUserSchema(ctx context.Context, userSchema *ChangeUserSchema) error {
|
||||
if err := userSchema.Valid(); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(userSchema.ID, userSchema.ResourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
writeModel, err := c.getSchemaWriteModelByID(ctx, userSchema.ResourceOwner, userSchema.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-HB3e1", "Errors.UserSchema.NotActive")
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMA-HB3e1", "Errors.UserSchema.NotActive")
|
||||
}
|
||||
updatedEvent := writeModel.NewUpdatedEvent(
|
||||
ctx,
|
||||
@ -102,29 +109,30 @@ func (c *Commands) UpdateUserSchema(ctx context.Context, userSchema *UpdateUserS
|
||||
userSchema.PossibleAuthenticators,
|
||||
)
|
||||
if updatedEvent == nil {
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
userSchema.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel, updatedEvent); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
userSchema.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeactivateUserSchema(ctx context.Context, id, resourceOwner string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-Vvf3w", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
writeModel, err := c.getSchemaWriteModelByID(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateActive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-E4t4z", "Errors.UserSchema.NotActive")
|
||||
}
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewDeactivatedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
@ -134,17 +142,16 @@ func (c *Commands) ReactivateUserSchema(ctx context.Context, id, resourceOwner s
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-wq3Gw", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
writeModel, err := c.getSchemaWriteModelByID(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.State != domain.UserSchemaStateInactive {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-DGzh5", "Errors.UserSchema.NotInactive")
|
||||
}
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewReactivatedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
@ -154,18 +161,17 @@ func (c *Commands) DeleteUserSchema(ctx context.Context, id, resourceOwner strin
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMA-E22gg", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUserSchemaWriteModel(id, resourceOwner)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
writeModel, err := c.getSchemaWriteModelByID(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.Exists() {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-Grg41", "Errors.UserSchema.NotExists")
|
||||
}
|
||||
// TODO: check for users based on that schema; this is only possible with / after https://github.com/zitadel/zitadel/issues/7308
|
||||
err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schema.NewDeletedEvent(ctx, UserSchemaAggregateFromWriteModel(&writeModel.WriteModel), writeModel.SchemaType),
|
||||
)
|
||||
if err != nil {
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
@ -178,3 +184,11 @@ func validateUserSchema(userSchema json.RawMessage) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserSchemaWriteModel, error) {
|
||||
writeModel := NewUserSchemaWriteModel(resourceOwner, id, "")
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
@ -19,14 +19,16 @@ type UserSchemaWriteModel struct {
|
||||
Schema json.RawMessage
|
||||
PossibleAuthenticators []domain.AuthenticatorType
|
||||
State domain.UserSchemaState
|
||||
Revision uint64
|
||||
}
|
||||
|
||||
func NewUserSchemaWriteModel(schemaID, resourceOwner string) *UserSchemaWriteModel {
|
||||
func NewUserSchemaWriteModel(resourceOwner, schemaID, ty string) *UserSchemaWriteModel {
|
||||
return &UserSchemaWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: schemaID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
SchemaType: ty,
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,10 +40,14 @@ func (wm *UserSchemaWriteModel) Reduce() error {
|
||||
wm.Schema = e.Schema
|
||||
wm.PossibleAuthenticators = e.PossibleAuthenticators
|
||||
wm.State = domain.UserSchemaStateActive
|
||||
wm.Revision = 1
|
||||
case *schema.UpdatedEvent:
|
||||
if e.SchemaType != nil {
|
||||
wm.SchemaType = *e.SchemaType
|
||||
}
|
||||
if e.SchemaRevision != nil {
|
||||
wm.Revision = *e.SchemaRevision
|
||||
}
|
||||
if len(e.Schema) > 0 {
|
||||
wm.Schema = e.Schema
|
||||
}
|
||||
@ -60,7 +66,7 @@ func (wm *UserSchemaWriteModel) Reduce() error {
|
||||
}
|
||||
|
||||
func (wm *UserSchemaWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(schema.AggregateType).
|
||||
@ -71,8 +77,13 @@ func (wm *UserSchemaWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
schema.DeactivatedType,
|
||||
schema.ReactivatedType,
|
||||
schema.DeletedType,
|
||||
).
|
||||
Builder()
|
||||
)
|
||||
|
||||
if wm.SchemaType != "" {
|
||||
query = query.EventData(map[string]interface{}{"schemaType": wm.SchemaType})
|
||||
}
|
||||
|
||||
return query.Builder()
|
||||
}
|
||||
func (wm *UserSchemaWriteModel) NewUpdatedEvent(
|
||||
ctx context.Context,
|
||||
@ -87,6 +98,8 @@ func (wm *UserSchemaWriteModel) NewUpdatedEvent(
|
||||
}
|
||||
if !bytes.Equal(wm.Schema, userSchema) {
|
||||
changes = append(changes, schema.ChangeSchema(userSchema))
|
||||
// change revision if the content of the schema changed
|
||||
changes = append(changes, schema.IncreaseRevision(wm.Revision))
|
||||
}
|
||||
if len(possibleAuthenticators) > 0 && slices.Compare(wm.PossibleAuthenticators, possibleAuthenticators) != 0 {
|
||||
changes = append(changes, schema.ChangePossibleAuthenticators(possibleAuthenticators))
|
||||
|
@ -27,7 +27,6 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
userSchema *CreateUserSchema
|
||||
}
|
||||
type res struct {
|
||||
id string
|
||||
details *domain.ObjectDetails
|
||||
err error
|
||||
}
|
||||
@ -107,6 +106,7 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
"empty user schema created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
@ -131,8 +131,8 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ID: "id1",
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
@ -141,6 +141,7 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
"user schema created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
@ -181,8 +182,8 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ID: "id1",
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
@ -220,6 +221,7 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
"user schema with permission created",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
@ -266,8 +268,8 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
},
|
||||
},
|
||||
res{
|
||||
id: "id1",
|
||||
details: &domain.ObjectDetails{
|
||||
ID: "id1",
|
||||
ResourceOwner: "instanceID",
|
||||
},
|
||||
},
|
||||
@ -279,21 +281,20 @@ func TestCommands_CreateUserSchema(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
gotID, gotDetails, err := c.CreateUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
assert.Equal(t, tt.res.id, gotID)
|
||||
assertObjectDetails(t, tt.res.details, gotDetails)
|
||||
err := c.CreateUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
assertObjectDetails(t, tt.res.details, tt.args.userSchema.Details)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
func TestCommands_ChangeUserSchema(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userSchema *UpdateUserSchema
|
||||
userSchema *ChangeUserSchema
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
@ -312,7 +313,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{},
|
||||
userSchema: &ChangeUserSchema{},
|
||||
},
|
||||
res{
|
||||
err: zerrors.ThrowInvalidArgument(nil, "COMMA-H5421", "Errors.IDMissing"),
|
||||
@ -325,7 +326,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr(""),
|
||||
},
|
||||
@ -341,7 +342,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
},
|
||||
},
|
||||
@ -356,7 +357,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{
|
||||
"properties": {
|
||||
@ -379,7 +380,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
@ -400,7 +401,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr("type"),
|
||||
Schema: json.RawMessage(`{}`),
|
||||
@ -432,7 +433,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Type: gu.Ptr("type"),
|
||||
Schema: json.RawMessage(`{}`),
|
||||
@ -473,7 +474,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
Type: gu.Ptr("newType"),
|
||||
@ -515,7 +516,9 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
schema.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
[]schema.Changes{schema.ChangeSchema(json.RawMessage(`{
|
||||
[]schema.Changes{
|
||||
schema.IncreaseRevision(1),
|
||||
schema.ChangeSchema(json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -539,7 +542,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
@ -597,7 +600,7 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userSchema: &UpdateUserSchema{
|
||||
userSchema: &ChangeUserSchema{
|
||||
ID: "id1",
|
||||
Schema: json.RawMessage(`{}`),
|
||||
PossibleAuthenticators: []domain.AuthenticatorType{
|
||||
@ -618,9 +621,9 @@ func TestCommands_UpdateUserSchema(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := c.UpdateUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
err := c.ChangeUserSchema(tt.args.ctx, tt.args.userSchema)
|
||||
assert.ErrorIs(t, err, tt.res.err)
|
||||
assertObjectDetails(t, tt.res.details, got)
|
||||
assertObjectDetails(t, tt.res.details, tt.args.userSchema.Details)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
220
internal/command/user_v3.go
Normal file
220
internal/command/user_v3.go
Normal file
@ -0,0 +1,220 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
domain_schema "github.com/zitadel/zitadel/internal/domain/schema"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type CreateSchemaUser struct {
|
||||
Details *domain.ObjectDetails
|
||||
ResourceOwner string
|
||||
|
||||
SchemaID string
|
||||
schemaRevision uint64
|
||||
|
||||
ID string
|
||||
Data json.RawMessage
|
||||
|
||||
Email *Email
|
||||
ReturnCodeEmail string
|
||||
Phone *Phone
|
||||
ReturnCodePhone string
|
||||
}
|
||||
|
||||
func (s *CreateSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
|
||||
if s.ResourceOwner == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-urEJKa1tJM", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if s.SchemaID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-TFo06JgnF2", "Errors.UserSchema.ID.Missing")
|
||||
}
|
||||
|
||||
schemaWriteModel, err := c.getSchemaWriteModelByID(ctx, "", s.SchemaID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !schemaWriteModel.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-N9QOuN4F7o", "Errors.UserSchema.NotExists")
|
||||
}
|
||||
s.schemaRevision = schemaWriteModel.Revision
|
||||
|
||||
if s.ID == "" {
|
||||
s.ID, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// get role for permission check in schema through extension
|
||||
role, err := c.getSchemaRoleForWrite(ctx, s.ResourceOwner, s.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
schema, err := domain_schema.NewSchema(role, bytes.NewReader(schemaWriteModel.Schema))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(s.Data, &v); err != nil {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-7o3ZGxtXUz", "Errors.User.Invalid")
|
||||
}
|
||||
|
||||
if err := schema.Validate(v); err != nil {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-SlKXqLSeL6", "Errors.UserSchema.Data.Invalid")
|
||||
}
|
||||
|
||||
if s.Email != nil && s.Email.Address != "" {
|
||||
if err := s.Email.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if s.Phone != nil && s.Phone.Number != "" {
|
||||
if s.Phone.Number, err = s.Phone.Number.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaRoleForWrite(ctx context.Context, resourceOwner, userID string) (domain_schema.Role, error) {
|
||||
if userID == authz.GetCtxData(ctx).UserID {
|
||||
return domain_schema.RoleSelf, nil
|
||||
}
|
||||
if err := c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, userID); err != nil {
|
||||
return domain_schema.RoleUnspecified, err
|
||||
}
|
||||
return domain_schema.RoleOwner, nil
|
||||
}
|
||||
|
||||
func (c *Commands) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser, alg crypto.EncryptionAlgorithm) (err error) {
|
||||
if err := user.Valid(ctx, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeModel, err := c.getSchemaUserExists(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if writeModel.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.AlreadyExists")
|
||||
}
|
||||
|
||||
userAgg := UserV3AggregateFromWriteModel(&writeModel.WriteModel)
|
||||
events := []eventstore.Command{
|
||||
schemauser.NewCreatedEvent(ctx,
|
||||
userAgg,
|
||||
user.SchemaID, user.schemaRevision, user.Data,
|
||||
),
|
||||
}
|
||||
if user.Email != nil {
|
||||
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, events, userAgg, user.Email, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if user.Phone != nil {
|
||||
events, user.ReturnCodePhone, err = c.updateSchemaUserPhone(ctx, events, userAgg, user.Phone, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel, events...); err != nil {
|
||||
return err
|
||||
}
|
||||
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteSchemaUser(ctx context.Context, id string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Vs4wJCME7T", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserExists(ctx, "", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-syHyCsGmvM", "Errors.User.NotFound")
|
||||
}
|
||||
if err := c.checkPermissionDeleteUser(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schemauser.NewDeletedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) updateSchemaUserEmail(ctx context.Context, events []eventstore.Command, agg *eventstore.Aggregate, email *Email, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
|
||||
|
||||
events = append(events, schemauser.NewEmailUpdatedEvent(ctx,
|
||||
agg,
|
||||
email.Address,
|
||||
))
|
||||
if email.Verified {
|
||||
events = append(events, schemauser.NewEmailVerifiedEvent(ctx, agg))
|
||||
} else {
|
||||
cryptoCode, err := c.newEmailCode(ctx, c.eventstore.Filter, alg) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if email.ReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
events = append(events, schemauser.NewEmailCodeAddedEvent(ctx, agg,
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
email.URLTemplate,
|
||||
email.ReturnCode,
|
||||
))
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (c *Commands) updateSchemaUserPhone(ctx context.Context, events []eventstore.Command, agg *eventstore.Aggregate, phone *Phone, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
|
||||
events = append(events, schemauser.NewPhoneChangedEvent(ctx,
|
||||
agg,
|
||||
phone.Number,
|
||||
))
|
||||
if phone.Verified {
|
||||
events = append(events, schemauser.NewPhoneVerifiedEvent(ctx, agg))
|
||||
} else {
|
||||
cryptoCode, err := c.newPhoneCode(ctx, c.eventstore.Filter, alg) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if phone.ReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
events = append(events, schemauser.NewPhoneCodeAddedEvent(ctx, agg,
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
phone.ReturnCode,
|
||||
))
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUserExists(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
|
||||
writeModel := NewExistsUserV3WriteModel(resourceOwner, id)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
174
internal/command/user_v3_model.go
Normal file
174
internal/command/user_v3_model.go
Normal file
@ -0,0 +1,174 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
|
||||
)
|
||||
|
||||
type UserV3WriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
PhoneWM bool
|
||||
EmailWM bool
|
||||
DataWM bool
|
||||
|
||||
SchemaID string
|
||||
SchemaRevision uint64
|
||||
|
||||
Email string
|
||||
IsEmailVerified bool
|
||||
EmailVerifiedFailedCount int
|
||||
Phone string
|
||||
IsPhoneVerified bool
|
||||
PhoneVerifiedFailedCount int
|
||||
|
||||
Data json.RawMessage
|
||||
|
||||
State domain.UserState
|
||||
}
|
||||
|
||||
func NewExistsUserV3WriteModel(resourceOwner, userID string) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
PhoneWM: false,
|
||||
EmailWM: false,
|
||||
DataWM: false,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserV3WriteModel(resourceOwner, userID string) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
PhoneWM: true,
|
||||
EmailWM: true,
|
||||
DataWM: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *schemauser.CreatedEvent:
|
||||
wm.SchemaID = e.SchemaID
|
||||
wm.SchemaRevision = 1
|
||||
wm.Data = e.Data
|
||||
|
||||
wm.State = domain.UserStateActive
|
||||
case *schemauser.UpdatedEvent:
|
||||
if e.SchemaID != nil {
|
||||
wm.SchemaID = *e.SchemaID
|
||||
}
|
||||
if e.SchemaRevision != nil {
|
||||
wm.SchemaRevision = *e.SchemaRevision
|
||||
}
|
||||
if len(e.Data) > 0 {
|
||||
wm.Data = e.Data
|
||||
}
|
||||
case *schemauser.DeletedEvent:
|
||||
wm.State = domain.UserStateDeleted
|
||||
case *schemauser.EmailUpdatedEvent:
|
||||
wm.Email = string(e.EmailAddress)
|
||||
case *schemauser.EmailCodeAddedEvent:
|
||||
wm.IsEmailVerified = false
|
||||
wm.EmailVerifiedFailedCount = 0
|
||||
case *schemauser.EmailVerifiedEvent:
|
||||
wm.IsEmailVerified = true
|
||||
wm.EmailVerifiedFailedCount = 0
|
||||
case *schemauser.EmailVerificationFailedEvent:
|
||||
wm.EmailVerifiedFailedCount += 1
|
||||
case *schemauser.PhoneChangedEvent:
|
||||
wm.Phone = string(e.PhoneNumber)
|
||||
case *schemauser.PhoneCodeAddedEvent:
|
||||
wm.IsPhoneVerified = false
|
||||
wm.PhoneVerifiedFailedCount = 0
|
||||
case *schemauser.PhoneVerifiedEvent:
|
||||
wm.PhoneVerifiedFailedCount = 0
|
||||
wm.IsPhoneVerified = true
|
||||
case *schemauser.PhoneVerificationFailedEvent:
|
||||
wm.PhoneVerifiedFailedCount += 1
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(schemauser.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
schemauser.CreatedType,
|
||||
schemauser.DeletedType,
|
||||
)
|
||||
if wm.DataWM {
|
||||
query = query.EventTypes(
|
||||
schemauser.UpdatedType,
|
||||
)
|
||||
}
|
||||
if wm.EmailWM {
|
||||
query = query.EventTypes(
|
||||
schemauser.EmailUpdatedType,
|
||||
schemauser.EmailVerifiedType,
|
||||
schemauser.EmailCodeAddedType,
|
||||
schemauser.EmailVerificationFailedType,
|
||||
)
|
||||
}
|
||||
if wm.PhoneWM {
|
||||
query = query.EventTypes(
|
||||
schemauser.PhoneUpdatedType,
|
||||
schemauser.PhoneVerifiedType,
|
||||
schemauser.PhoneCodeAddedType,
|
||||
schemauser.PhoneVerificationFailedType,
|
||||
)
|
||||
}
|
||||
return query.Builder()
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewUpdatedEvent(
|
||||
ctx context.Context,
|
||||
agg *eventstore.Aggregate,
|
||||
schemaID *string,
|
||||
schemaRevision *uint64,
|
||||
data json.RawMessage,
|
||||
) *schemauser.UpdatedEvent {
|
||||
changes := make([]schemauser.Changes, 0)
|
||||
if schemaID != nil && wm.SchemaID != *schemaID {
|
||||
changes = append(changes, schemauser.ChangeSchemaID(wm.SchemaID, *schemaID))
|
||||
}
|
||||
if schemaRevision != nil && wm.SchemaRevision != *schemaRevision {
|
||||
changes = append(changes, schemauser.ChangeSchemaRevision(wm.SchemaRevision, *schemaRevision))
|
||||
}
|
||||
if !bytes.Equal(wm.Data, data) {
|
||||
changes = append(changes, schemauser.ChangeData(data))
|
||||
}
|
||||
if len(changes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return schemauser.NewUpdatedEvent(ctx, agg, changes)
|
||||
}
|
||||
|
||||
func UserV3AggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||
return &eventstore.Aggregate{
|
||||
ID: wm.AggregateID,
|
||||
Type: schemauser.AggregateType,
|
||||
ResourceOwner: wm.ResourceOwner,
|
||||
InstanceID: wm.InstanceID,
|
||||
Version: schemauser.AggregateVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) Exists() bool {
|
||||
return wm.State != domain.UserStateDeleted && wm.State != domain.UserStateUnspecified
|
||||
}
|
1103
internal/command/user_v3_test.go
Normal file
1103
internal/command/user_v3_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,16 +20,16 @@ const (
|
||||
PermissionProperty = "urn:zitadel:schema:permission"
|
||||
)
|
||||
|
||||
type role int32
|
||||
type Role int32
|
||||
|
||||
const (
|
||||
roleUnspecified role = iota
|
||||
roleSelf
|
||||
roleOwner
|
||||
RoleUnspecified Role = iota
|
||||
RoleSelf
|
||||
RoleOwner
|
||||
)
|
||||
|
||||
type permissionExtension struct {
|
||||
role role
|
||||
role Role
|
||||
}
|
||||
|
||||
// Compile implements the [jsonschema.ExtCompiler] interface.
|
||||
@ -57,14 +57,14 @@ func (c permissionExtension) Compile(ctx jsonschema.CompilerContext, m map[strin
|
||||
return
|
||||
}
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-GFjio", "invalid permission role")
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SCHEMA-GFjio", "invalid permission Role")
|
||||
}
|
||||
}
|
||||
return permissionExtensionConfig{c.role, perms}, nil
|
||||
}
|
||||
|
||||
type permissionExtensionConfig struct {
|
||||
role role
|
||||
role Role
|
||||
permissions *permissions
|
||||
}
|
||||
|
||||
@ -72,17 +72,17 @@ type permissionExtensionConfig struct {
|
||||
// It validates the fields of the json instance according to the permission schema.
|
||||
func (s permissionExtensionConfig) Validate(ctx jsonschema.ValidationContext, v interface{}) error {
|
||||
switch s.role {
|
||||
case roleSelf:
|
||||
case RoleSelf:
|
||||
if s.permissions.self == nil || !s.permissions.self.write {
|
||||
return ctx.Error("permission", "missing required permission")
|
||||
}
|
||||
return nil
|
||||
case roleOwner:
|
||||
case RoleOwner:
|
||||
if s.permissions.owner == nil || !s.permissions.owner.write {
|
||||
return ctx.Error("permission", "missing required permission")
|
||||
}
|
||||
return nil
|
||||
case roleUnspecified:
|
||||
case RoleUnspecified:
|
||||
fallthrough
|
||||
default:
|
||||
return ctx.Error("permission", "missing required permission")
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
|
||||
func TestPermissionExtension(t *testing.T) {
|
||||
type args struct {
|
||||
role role
|
||||
role Role
|
||||
schema string
|
||||
instance string
|
||||
}
|
||||
@ -83,7 +83,7 @@ func TestPermissionExtension(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid role, compilation err",
|
||||
"invalid Role, compilation err",
|
||||
args{
|
||||
schema: `{
|
||||
"type": "object",
|
||||
@ -98,13 +98,13 @@ func TestPermissionExtension(t *testing.T) {
|
||||
}`,
|
||||
},
|
||||
want{
|
||||
compilationErr: zerrors.ThrowInvalidArgument(nil, "SCHEMA-GFjio", "invalid permission role"),
|
||||
compilationErr: zerrors.ThrowInvalidArgument(nil, "SCHEMA-GFjio", "invalid permission Role"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid permission self, validation err",
|
||||
args{
|
||||
role: roleSelf,
|
||||
role: RoleSelf,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -126,7 +126,7 @@ func TestPermissionExtension(t *testing.T) {
|
||||
{
|
||||
"invalid permission owner, validation err",
|
||||
args{
|
||||
role: roleOwner,
|
||||
role: RoleOwner,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -148,7 +148,7 @@ func TestPermissionExtension(t *testing.T) {
|
||||
{
|
||||
"valid permission self, ok",
|
||||
args{
|
||||
role: roleSelf,
|
||||
role: RoleSelf,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -170,7 +170,7 @@ func TestPermissionExtension(t *testing.T) {
|
||||
{
|
||||
"valid permission owner, ok",
|
||||
args{
|
||||
role: roleOwner,
|
||||
role: RoleOwner,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -190,9 +190,9 @@ func TestPermissionExtension(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"no role, validation err",
|
||||
"no Role, validation err",
|
||||
args{
|
||||
role: roleUnspecified,
|
||||
role: RoleUnspecified,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -214,7 +214,7 @@ func TestPermissionExtension(t *testing.T) {
|
||||
{
|
||||
"no permission required, ok",
|
||||
args{
|
||||
role: roleSelf,
|
||||
role: RoleSelf,
|
||||
schema: `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -19,7 +19,7 @@ const (
|
||||
MetaSchemaID = "urn:zitadel:schema:v1"
|
||||
)
|
||||
|
||||
func NewSchema(role role, r io.Reader) (*jsonschema.Schema, error) {
|
||||
func NewSchema(role Role, r io.Reader) (*jsonschema.Schema, error) {
|
||||
c := jsonschema.NewCompiler()
|
||||
if err := c.AddResource(PermissionSchemaID, strings.NewReader(permissionJSON)); err != nil {
|
||||
return nil, err
|
||||
@ -31,11 +31,11 @@ func NewSchema(role role, r io.Reader) (*jsonschema.Schema, error) {
|
||||
role,
|
||||
})
|
||||
if err := c.AddResource("schema.json", r); err != nil {
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMA-Frh42", "Errors.UserSchema.Schema.Invalid")
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMA-Frh42", "Errors.UserSchema.Invalid")
|
||||
}
|
||||
schema, err := c.Compile("schema.json")
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMA-W21tg", "Errors.UserSchema.Schema.Invalid")
|
||||
return nil, zerrors.ThrowInvalidArgument(err, "COMMA-W21tg", "Errors.UserSchema.Invalid")
|
||||
}
|
||||
return schema, nil
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -378,7 +377,6 @@ func Test_CallTargets(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
fmt.Println(respBody)
|
||||
assert.Equal(t, tt.res.ret, respBody)
|
||||
})
|
||||
}
|
||||
|
@ -34,11 +34,14 @@ import (
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp/v2"
|
||||
mgmt "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||
object_v3alpha "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2"
|
||||
oidc_pb_v2beta "github.com/zitadel/zitadel/pkg/grpc/oidc/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
|
||||
org_v2beta "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
|
||||
action "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha"
|
||||
user_v3alpha "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
userschema_v3alpha "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha"
|
||||
webkey_v3alpha "github.com/zitadel/zitadel/pkg/grpc/resources/webkey/v3alpha"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
||||
session_v2beta "github.com/zitadel/zitadel/pkg/grpc/session/v2beta"
|
||||
@ -46,8 +49,7 @@ import (
|
||||
settings_v2beta "github.com/zitadel/zitadel/pkg/grpc/settings/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/system"
|
||||
user_pb "github.com/zitadel/zitadel/pkg/grpc/user"
|
||||
schema "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
user_v2beta "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
@ -57,7 +59,7 @@ type Client struct {
|
||||
Mgmt mgmt.ManagementServiceClient
|
||||
Auth auth.AuthServiceClient
|
||||
UserV2beta user_v2beta.UserServiceClient
|
||||
UserV2 user.UserServiceClient
|
||||
UserV2 user_v2.UserServiceClient
|
||||
SessionV2beta session_v2beta.SessionServiceClient
|
||||
SessionV2 session.SessionServiceClient
|
||||
SettingsV2beta settings_v2beta.SettingsServiceClient
|
||||
@ -70,9 +72,10 @@ type Client struct {
|
||||
ActionV3Alpha action.ZITADELActionsClient
|
||||
FeatureV2beta feature_v2beta.FeatureServiceClient
|
||||
FeatureV2 feature.FeatureServiceClient
|
||||
UserSchemaV3 schema.UserSchemaServiceClient
|
||||
UserSchemaV3 userschema_v3alpha.ZITADELUserSchemasClient
|
||||
WebKeyV3Alpha webkey_v3alpha.ZITADELWebKeysClient
|
||||
IDPv2 idp_pb.IdentityProviderServiceClient
|
||||
UserV3Alpha user_v3alpha.ZITADELUsersClient
|
||||
}
|
||||
|
||||
func newClient(cc *grpc.ClientConn) Client {
|
||||
@ -82,7 +85,7 @@ func newClient(cc *grpc.ClientConn) Client {
|
||||
Mgmt: mgmt.NewManagementServiceClient(cc),
|
||||
Auth: auth.NewAuthServiceClient(cc),
|
||||
UserV2beta: user_v2beta.NewUserServiceClient(cc),
|
||||
UserV2: user.NewUserServiceClient(cc),
|
||||
UserV2: user_v2.NewUserServiceClient(cc),
|
||||
SessionV2beta: session_v2beta.NewSessionServiceClient(cc),
|
||||
SessionV2: session.NewSessionServiceClient(cc),
|
||||
SettingsV2beta: settings_v2beta.NewSettingsServiceClient(cc),
|
||||
@ -95,9 +98,10 @@ func newClient(cc *grpc.ClientConn) Client {
|
||||
ActionV3Alpha: action.NewZITADELActionsClient(cc),
|
||||
FeatureV2beta: feature_v2beta.NewFeatureServiceClient(cc),
|
||||
FeatureV2: feature.NewFeatureServiceClient(cc),
|
||||
UserSchemaV3: schema.NewUserSchemaServiceClient(cc),
|
||||
UserSchemaV3: userschema_v3alpha.NewZITADELUserSchemasClient(cc),
|
||||
WebKeyV3Alpha: webkey_v3alpha.NewZITADELWebKeysClient(cc),
|
||||
IDPv2: idp_pb.NewIdentityProviderServiceClient(cc),
|
||||
UserV3Alpha: user_v3alpha.NewZITADELUsersClient(cc),
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,29 +152,29 @@ func (t *Tester) UseIsolatedInstance(tt *testing.T, iamOwnerCtx, systemCtx conte
|
||||
return primaryDomain, instanceId, adminUser.GetUserId(), t.updateInstanceAndOrg(newCtx, fmt.Sprintf("%s:%d", primaryDomain, t.Config.ExternalPort))
|
||||
}
|
||||
|
||||
func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user.AddHumanUserRequest{
|
||||
func (s *Tester) CreateHumanUser(ctx context.Context) *user_v2.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user_v2.AddHumanUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Org: &object.Organization_OrgId{
|
||||
OrgId: s.Organisation.ID,
|
||||
},
|
||||
},
|
||||
Profile: &user.SetHumanProfile{
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: gu.Ptr(user.Gender_GENDER_MALE),
|
||||
Gender: gu.Ptr(user_v2.Gender_GENDER_MALE),
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: &user_v2.SetHumanEmail{
|
||||
Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()),
|
||||
Verification: &user.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
Verification: &user_v2.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
Phone: &user.SetHumanPhone{
|
||||
Phone: &user_v2.SetHumanPhone{
|
||||
Phone: "+41791234567",
|
||||
Verification: &user.SetHumanPhone_ReturnCode{
|
||||
ReturnCode: &user.ReturnPhoneVerificationCode{},
|
||||
Verification: &user_v2.SetHumanPhone_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -178,23 +182,23 @@ func (s *Tester) CreateHumanUser(ctx context.Context) *user.AddHumanUserResponse
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Tester) CreateHumanUserNoPhone(ctx context.Context) *user.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user.AddHumanUserRequest{
|
||||
func (s *Tester) CreateHumanUserNoPhone(ctx context.Context) *user_v2.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user_v2.AddHumanUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Org: &object.Organization_OrgId{
|
||||
OrgId: s.Organisation.ID,
|
||||
},
|
||||
},
|
||||
Profile: &user.SetHumanProfile{
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: gu.Ptr(user.Gender_GENDER_MALE),
|
||||
Gender: gu.Ptr(user_v2.Gender_GENDER_MALE),
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: &user_v2.SetHumanEmail{
|
||||
Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()),
|
||||
Verification: &user.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
Verification: &user_v2.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
})
|
||||
@ -202,29 +206,29 @@ func (s *Tester) CreateHumanUserNoPhone(ctx context.Context) *user.AddHumanUserR
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Tester) CreateHumanUserWithTOTP(ctx context.Context, secret string) *user.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user.AddHumanUserRequest{
|
||||
func (s *Tester) CreateHumanUserWithTOTP(ctx context.Context, secret string) *user_v2.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user_v2.AddHumanUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Org: &object.Organization_OrgId{
|
||||
OrgId: s.Organisation.ID,
|
||||
},
|
||||
},
|
||||
Profile: &user.SetHumanProfile{
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: gu.Ptr(user.Gender_GENDER_MALE),
|
||||
Gender: gu.Ptr(user_v2.Gender_GENDER_MALE),
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: &user_v2.SetHumanEmail{
|
||||
Email: fmt.Sprintf("%d@mouse.com", time.Now().UnixNano()),
|
||||
Verification: &user.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
Verification: &user_v2.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
Phone: &user.SetHumanPhone{
|
||||
Phone: &user_v2.SetHumanPhone{
|
||||
Phone: "+41791234567",
|
||||
Verification: &user.SetHumanPhone_ReturnCode{
|
||||
ReturnCode: &user.ReturnPhoneVerificationCode{},
|
||||
Verification: &user_v2.SetHumanPhone_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnPhoneVerificationCode{},
|
||||
},
|
||||
},
|
||||
TotpSecret: gu.Ptr(secret),
|
||||
@ -239,15 +243,15 @@ func (s *Tester) CreateOrganization(ctx context.Context, name, adminEmail string
|
||||
Admins: []*org.AddOrganizationRequest_Admin{
|
||||
{
|
||||
UserType: &org.AddOrganizationRequest_Admin_Human{
|
||||
Human: &user.AddHumanUserRequest{
|
||||
Profile: &user.SetHumanProfile{
|
||||
Human: &user_v2.AddHumanUserRequest{
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "firstname",
|
||||
FamilyName: "lastname",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: &user_v2.SetHumanEmail{
|
||||
Email: adminEmail,
|
||||
Verification: &user.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
Verification: &user_v2.SetHumanEmail_ReturnCode{
|
||||
ReturnCode: &user_v2.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -292,29 +296,29 @@ func (s *Tester) CreateOrganizationWithUserID(ctx context.Context, name, userID
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Tester) CreateHumanUserVerified(ctx context.Context, org, email string) *user.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user.AddHumanUserRequest{
|
||||
func (s *Tester) CreateHumanUserVerified(ctx context.Context, org, email string) *user_v2.AddHumanUserResponse {
|
||||
resp, err := s.Client.UserV2.AddHumanUser(ctx, &user_v2.AddHumanUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Org: &object.Organization_OrgId{
|
||||
OrgId: org,
|
||||
},
|
||||
},
|
||||
Profile: &user.SetHumanProfile{
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: gu.Ptr(user.Gender_GENDER_MALE),
|
||||
Gender: gu.Ptr(user_v2.Gender_GENDER_MALE),
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: &user_v2.SetHumanEmail{
|
||||
Email: email,
|
||||
Verification: &user.SetHumanEmail_IsVerified{
|
||||
Verification: &user_v2.SetHumanEmail_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
Phone: &user.SetHumanPhone{
|
||||
Phone: &user_v2.SetHumanPhone{
|
||||
Phone: "+41791234567",
|
||||
Verification: &user.SetHumanPhone_IsVerified{
|
||||
Verification: &user_v2.SetHumanPhone_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -334,12 +338,12 @@ func (s *Tester) CreateMachineUser(ctx context.Context) *mgmt.AddMachineUserResp
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Tester) CreateUserIDPlink(ctx context.Context, userID, externalID, idpID, username string) *user.AddIDPLinkResponse {
|
||||
func (s *Tester) CreateUserIDPlink(ctx context.Context, userID, externalID, idpID, username string) *user_v2.AddIDPLinkResponse {
|
||||
resp, err := s.Client.UserV2.AddIDPLink(
|
||||
ctx,
|
||||
&user.AddIDPLinkRequest{
|
||||
&user_v2.AddIDPLinkRequest{
|
||||
UserId: userID,
|
||||
IdpLink: &user.IDPLink{
|
||||
IdpLink: &user_v2.IDPLink{
|
||||
IdpId: idpID,
|
||||
UserId: externalID,
|
||||
UserName: username,
|
||||
@ -351,13 +355,13 @@ func (s *Tester) CreateUserIDPlink(ctx context.Context, userID, externalID, idpI
|
||||
}
|
||||
|
||||
func (s *Tester) RegisterUserPasskey(ctx context.Context, userID string) {
|
||||
reg, err := s.Client.UserV2.CreatePasskeyRegistrationLink(ctx, &user.CreatePasskeyRegistrationLinkRequest{
|
||||
reg, err := s.Client.UserV2.CreatePasskeyRegistrationLink(ctx, &user_v2.CreatePasskeyRegistrationLinkRequest{
|
||||
UserId: userID,
|
||||
Medium: &user.CreatePasskeyRegistrationLinkRequest_ReturnCode{},
|
||||
Medium: &user_v2.CreatePasskeyRegistrationLinkRequest_ReturnCode{},
|
||||
})
|
||||
logging.OnError(err).Fatal("create user passkey")
|
||||
|
||||
pkr, err := s.Client.UserV2.RegisterPasskey(ctx, &user.RegisterPasskeyRequest{
|
||||
pkr, err := s.Client.UserV2.RegisterPasskey(ctx, &user_v2.RegisterPasskeyRequest{
|
||||
UserId: userID,
|
||||
Code: reg.GetCode(),
|
||||
Domain: s.Config.ExternalDomain,
|
||||
@ -366,7 +370,7 @@ func (s *Tester) RegisterUserPasskey(ctx context.Context, userID string) {
|
||||
attestationResponse, err := s.WebAuthN.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions())
|
||||
logging.OnError(err).Fatal("create user passkey")
|
||||
|
||||
_, err = s.Client.UserV2.VerifyPasskeyRegistration(ctx, &user.VerifyPasskeyRegistrationRequest{
|
||||
_, err = s.Client.UserV2.VerifyPasskeyRegistration(ctx, &user_v2.VerifyPasskeyRegistrationRequest{
|
||||
UserId: userID,
|
||||
PasskeyId: pkr.GetPasskeyId(),
|
||||
PublicKeyCredential: attestationResponse,
|
||||
@ -376,7 +380,7 @@ func (s *Tester) RegisterUserPasskey(ctx context.Context, userID string) {
|
||||
}
|
||||
|
||||
func (s *Tester) RegisterUserU2F(ctx context.Context, userID string) {
|
||||
pkr, err := s.Client.UserV2.RegisterU2F(ctx, &user.RegisterU2FRequest{
|
||||
pkr, err := s.Client.UserV2.RegisterU2F(ctx, &user_v2.RegisterU2FRequest{
|
||||
UserId: userID,
|
||||
Domain: s.Config.ExternalDomain,
|
||||
})
|
||||
@ -384,7 +388,7 @@ func (s *Tester) RegisterUserU2F(ctx context.Context, userID string) {
|
||||
attestationResponse, err := s.WebAuthN.CreateAttestationResponse(pkr.GetPublicKeyCredentialCreationOptions())
|
||||
logging.OnError(err).Fatal("create user u2f")
|
||||
|
||||
_, err = s.Client.UserV2.VerifyU2FRegistration(ctx, &user.VerifyU2FRegistrationRequest{
|
||||
_, err = s.Client.UserV2.VerifyU2FRegistration(ctx, &user_v2.VerifyU2FRegistrationRequest{
|
||||
UserId: userID,
|
||||
U2FId: pkr.GetU2FId(),
|
||||
PublicKeyCredential: attestationResponse,
|
||||
@ -394,9 +398,9 @@ func (s *Tester) RegisterUserU2F(ctx context.Context, userID string) {
|
||||
}
|
||||
|
||||
func (s *Tester) SetUserPassword(ctx context.Context, userID, password string, changeRequired bool) *object.Details {
|
||||
resp, err := s.Client.UserV2.SetPassword(ctx, &user.SetPasswordRequest{
|
||||
resp, err := s.Client.UserV2.SetPassword(ctx, &user_v2.SetPasswordRequest{
|
||||
UserId: userID,
|
||||
NewPassword: &user.Password{
|
||||
NewPassword: &user_v2.Password{
|
||||
Password: password,
|
||||
ChangeRequired: changeRequired,
|
||||
},
|
||||
@ -757,24 +761,57 @@ func (s *Tester) SetExecution(ctx context.Context, t *testing.T, cond *action.Co
|
||||
return target
|
||||
}
|
||||
|
||||
func (s *Tester) CreateUserSchema(ctx context.Context, t *testing.T) *schema.CreateUserSchemaResponse {
|
||||
return s.CreateUserSchemaWithType(ctx, t, fmt.Sprint(time.Now().UnixNano()+1))
|
||||
func (s *Tester) CreateUserSchemaEmpty(ctx context.Context) *userschema_v3alpha.CreateUserSchemaResponse {
|
||||
return s.CreateUserSchemaEmptyWithType(ctx, fmt.Sprint(time.Now().UnixNano()+1))
|
||||
}
|
||||
|
||||
func (s *Tester) CreateUserSchemaWithType(ctx context.Context, t *testing.T, schemaType string) *schema.CreateUserSchemaResponse {
|
||||
func (s *Tester) CreateUserSchema(ctx context.Context, schemaData []byte) *userschema_v3alpha.CreateUserSchemaResponse {
|
||||
userSchema := new(structpb.Struct)
|
||||
err := userSchema.UnmarshalJSON(schemaData)
|
||||
logging.OnError(err).Fatal("create userschema unmarshal")
|
||||
schema, err := s.Client.UserSchemaV3.CreateUserSchema(ctx, &userschema_v3alpha.CreateUserSchemaRequest{
|
||||
UserSchema: &userschema_v3alpha.CreateUserSchema{
|
||||
Type: fmt.Sprint(time.Now().UnixNano() + 1),
|
||||
DataType: &userschema_v3alpha.CreateUserSchema_Schema{
|
||||
Schema: userSchema,
|
||||
},
|
||||
},
|
||||
})
|
||||
logging.OnError(err).Fatal("create userschema")
|
||||
return schema
|
||||
}
|
||||
|
||||
func (s *Tester) CreateUserSchemaEmptyWithType(ctx context.Context, schemaType string) *userschema_v3alpha.CreateUserSchemaResponse {
|
||||
userSchema := new(structpb.Struct)
|
||||
err := userSchema.UnmarshalJSON([]byte(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}`))
|
||||
require.NoError(t, err)
|
||||
target, err := s.Client.UserSchemaV3.CreateUserSchema(ctx, &schema.CreateUserSchemaRequest{
|
||||
Type: schemaType,
|
||||
DataType: &schema.CreateUserSchemaRequest_Schema{
|
||||
Schema: userSchema,
|
||||
logging.OnError(err).Fatal("create userschema unmarshal")
|
||||
schema, err := s.Client.UserSchemaV3.CreateUserSchema(ctx, &userschema_v3alpha.CreateUserSchemaRequest{
|
||||
UserSchema: &userschema_v3alpha.CreateUserSchema{
|
||||
Type: schemaType,
|
||||
DataType: &userschema_v3alpha.CreateUserSchema_Schema{
|
||||
Schema: userSchema,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return target
|
||||
logging.OnError(err).Fatal("create userschema")
|
||||
return schema
|
||||
}
|
||||
|
||||
func (s *Tester) CreateSchemaUser(ctx context.Context, orgID string, schemaID string, data []byte) *user_v3alpha.CreateUserResponse {
|
||||
userData := new(structpb.Struct)
|
||||
err := userData.UnmarshalJSON(data)
|
||||
logging.OnError(err).Fatal("create user unmarshal")
|
||||
user, err := s.Client.UserV3Alpha.CreateUser(ctx, &user_v3alpha.CreateUserRequest{
|
||||
Organization: &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}},
|
||||
User: &user_v3alpha.CreateUser{
|
||||
SchemaId: schemaID,
|
||||
Data: userData,
|
||||
},
|
||||
})
|
||||
logging.OnError(err).Fatal("create user")
|
||||
return user
|
||||
}
|
||||
|
@ -12,9 +12,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UserSchemaTable = "projections.user_schemas"
|
||||
UserSchemaTable = "projections.user_schemas1"
|
||||
|
||||
UserSchemaIDCol = "id"
|
||||
UserSchemaCreationDateCol = "creation_date"
|
||||
UserSchemaChangeDateCol = "change_date"
|
||||
UserSchemaSequenceCol = "sequence"
|
||||
UserSchemaInstanceIDCol = "instance_id"
|
||||
@ -39,6 +40,7 @@ func (*userSchemaProjection) Init() *old_handler.Check {
|
||||
return handler.NewTableCheck(
|
||||
handler.NewTable([]*handler.InitColumn{
|
||||
handler.NewColumn(UserSchemaIDCol, handler.ColumnTypeText),
|
||||
handler.NewColumn(UserSchemaCreationDateCol, handler.ColumnTypeTimestamp),
|
||||
handler.NewColumn(UserSchemaChangeDateCol, handler.ColumnTypeTimestamp),
|
||||
handler.NewColumn(UserSchemaSequenceCol, handler.ColumnTypeInt64),
|
||||
handler.NewColumn(UserSchemaStateCol, handler.ColumnTypeEnum),
|
||||
@ -102,6 +104,7 @@ func (p *userSchemaProjection) reduceCreated(event eventstore.Event) (*handler.S
|
||||
event,
|
||||
[]handler.Column{
|
||||
handler.NewCol(UserSchemaIDCol, event.Aggregate().ID),
|
||||
handler.NewCol(UserSchemaCreationDateCol, handler.OnlySetValueOnInsert(UserSchemaTable, e.CreationDate())),
|
||||
handler.NewCol(UserSchemaChangeDateCol, event.CreatedAt()),
|
||||
handler.NewCol(UserSchemaSequenceCol, event.Sequence()),
|
||||
handler.NewCol(UserSchemaInstanceIDCol, event.Aggregate().InstanceID),
|
||||
@ -130,7 +133,10 @@ func (p *userSchemaProjection) reduceUpdated(event eventstore.Event) (*handler.S
|
||||
|
||||
if len(e.Schema) > 0 {
|
||||
cols = append(cols, handler.NewCol(UserSchemaSchemaCol, e.Schema))
|
||||
cols = append(cols, handler.NewIncrementCol(UserSchemaRevisionCol, 1))
|
||||
}
|
||||
|
||||
if e.SchemaRevision != nil {
|
||||
cols = append(cols, handler.NewCol(UserSchemaRevisionCol, *e.SchemaRevision))
|
||||
}
|
||||
|
||||
if len(e.PossibleAuthenticators) > 0 {
|
||||
|
@ -39,10 +39,11 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.user_schemas (id, change_date, sequence, instance_id, state, type, revision, schema, possible_authenticators) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
expectedStmt: "INSERT INTO projections.user_schemas1 (id, creation_date, change_date, sequence, instance_id, state, type, revision, schema, possible_authenticators) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"instance-id",
|
||||
domain.UserSchemaStateActive,
|
||||
@ -61,9 +62,9 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
args: args{
|
||||
event: getEvent(
|
||||
testEvent(
|
||||
schema.CreatedType,
|
||||
schema.UpdatedType,
|
||||
schema.AggregateType,
|
||||
[]byte(`{"schemaType": "type", "schema": {"$schema":"urn:zitadel:schema:v1","properties":{"name":{"type":"string","urn:zitadel:schema:permission":{"self":"rw"}}},"type":"object"}, "possibleAuthenticators": [1,2]}`),
|
||||
[]byte(`{"schemaType": "type", "schemaRevision": 2, "schema": {"$schema":"urn:zitadel:schema:v1","properties":{"name":{"type":"string","urn:zitadel:schema:permission":{"self":"rw"}}},"type":"object"}, "possibleAuthenticators": [1,2]}`),
|
||||
), eventstore.GenericEventMapper[schema.UpdatedEvent]),
|
||||
},
|
||||
reduce: (&userSchemaProjection{}).reduceUpdated,
|
||||
@ -73,13 +74,13 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.user_schemas SET (change_date, sequence, type, schema, revision, possible_authenticators) = ($1, $2, $3, $4, revision + $5, $6) WHERE (id = $7) AND (instance_id = $8)",
|
||||
expectedStmt: "UPDATE projections.user_schemas1 SET (change_date, sequence, type, schema, revision, possible_authenticators) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"type",
|
||||
json.RawMessage(`{"$schema":"urn:zitadel:schema:v1","properties":{"name":{"type":"string","urn:zitadel:schema:permission":{"self":"rw"}}},"type":"object"}`),
|
||||
1,
|
||||
uint64(2),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername, domain.AuthenticatorTypePassword},
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -106,7 +107,7 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.user_schemas SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.user_schemas1 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -136,7 +137,7 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "UPDATE projections.user_schemas SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedStmt: "UPDATE projections.user_schemas1 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
|
||||
expectedArgs: []interface{}{
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
@ -166,7 +167,7 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_schemas WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedStmt: "DELETE FROM projections.user_schemas1 WHERE (id = $1) AND (instance_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
"instance-id",
|
||||
@ -193,7 +194,7 @@ func TestUserSchemaProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "DELETE FROM projections.user_schemas WHERE (instance_id = $1)",
|
||||
expectedStmt: "DELETE FROM projections.user_schemas1 WHERE (instance_id = $1)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
},
|
||||
|
@ -26,7 +26,6 @@ func (e *UserSchemas) SetState(s *State) {
|
||||
}
|
||||
|
||||
type UserSchema struct {
|
||||
ID string
|
||||
domain.ObjectDetails
|
||||
State domain.UserSchemaState
|
||||
Type string
|
||||
@ -49,6 +48,10 @@ var (
|
||||
name: projection.UserSchemaIDCol,
|
||||
table: userSchemaTable,
|
||||
}
|
||||
UserSchemaCreationDateCol = Column{
|
||||
name: projection.UserSchemaCreationDateCol,
|
||||
table: userSchemaTable,
|
||||
}
|
||||
UserSchemaChangeDateCol = Column{
|
||||
name: projection.UserSchemaChangeDateCol,
|
||||
table: userSchemaTable,
|
||||
@ -131,6 +134,7 @@ func NewUserSchemaStateSearchQuery(value domain.UserSchemaState) (SearchQuery, e
|
||||
func prepareUserSchemaQuery() (sq.SelectBuilder, func(*sql.Row) (*UserSchema, error)) {
|
||||
return sq.Select(
|
||||
UserSchemaIDCol.identifier(),
|
||||
UserSchemaCreationDateCol.identifier(),
|
||||
UserSchemaChangeDateCol.identifier(),
|
||||
UserSchemaSequenceCol.identifier(),
|
||||
UserSchemaInstanceIDCol.identifier(),
|
||||
@ -147,6 +151,7 @@ func prepareUserSchemaQuery() (sq.SelectBuilder, func(*sql.Row) (*UserSchema, er
|
||||
var schema database.ByteArray[byte]
|
||||
err := row.Scan(
|
||||
&u.ID,
|
||||
&u.CreationDate,
|
||||
&u.EventDate,
|
||||
&u.Sequence,
|
||||
&u.ResourceOwner,
|
||||
@ -173,6 +178,7 @@ func prepareUserSchemaQuery() (sq.SelectBuilder, func(*sql.Row) (*UserSchema, er
|
||||
func prepareUserSchemasQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserSchemas, error)) {
|
||||
return sq.Select(
|
||||
UserSchemaIDCol.identifier(),
|
||||
UserSchemaCreationDateCol.identifier(),
|
||||
UserSchemaChangeDateCol.identifier(),
|
||||
UserSchemaSequenceCol.identifier(),
|
||||
UserSchemaInstanceIDCol.identifier(),
|
||||
@ -195,6 +201,7 @@ func prepareUserSchemasQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserSchemas,
|
||||
u := new(UserSchema)
|
||||
err := rows.Scan(
|
||||
&u.ID,
|
||||
&u.CreationDate,
|
||||
&u.EventDate,
|
||||
&u.Sequence,
|
||||
&u.ResourceOwner,
|
||||
|
@ -15,19 +15,21 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
prepareUserSchemasStmt = `SELECT projections.user_schemas.id,` +
|
||||
` projections.user_schemas.change_date,` +
|
||||
` projections.user_schemas.sequence,` +
|
||||
` projections.user_schemas.instance_id,` +
|
||||
` projections.user_schemas.state,` +
|
||||
` projections.user_schemas.type,` +
|
||||
` projections.user_schemas.revision,` +
|
||||
` projections.user_schemas.schema,` +
|
||||
` projections.user_schemas.possible_authenticators,` +
|
||||
prepareUserSchemasStmt = `SELECT projections.user_schemas1.id,` +
|
||||
` projections.user_schemas1.creation_date,` +
|
||||
` projections.user_schemas1.change_date,` +
|
||||
` projections.user_schemas1.sequence,` +
|
||||
` projections.user_schemas1.instance_id,` +
|
||||
` projections.user_schemas1.state,` +
|
||||
` projections.user_schemas1.type,` +
|
||||
` projections.user_schemas1.revision,` +
|
||||
` projections.user_schemas1.schema,` +
|
||||
` projections.user_schemas1.possible_authenticators,` +
|
||||
` COUNT(*) OVER ()` +
|
||||
` FROM projections.user_schemas`
|
||||
prepareUserSchemasCols = []string{
|
||||
"id",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"sequence",
|
||||
"instance_id",
|
||||
@ -39,18 +41,20 @@ var (
|
||||
"count",
|
||||
}
|
||||
|
||||
prepareUserSchemaStmt = `SELECT projections.user_schemas.id,` +
|
||||
` projections.user_schemas.change_date,` +
|
||||
` projections.user_schemas.sequence,` +
|
||||
` projections.user_schemas.instance_id,` +
|
||||
` projections.user_schemas.state,` +
|
||||
` projections.user_schemas.type,` +
|
||||
` projections.user_schemas.revision,` +
|
||||
` projections.user_schemas.schema,` +
|
||||
` projections.user_schemas.possible_authenticators` +
|
||||
prepareUserSchemaStmt = `SELECT projections.user_schemas1.id,` +
|
||||
` projections.user_schemas1.creation_date,` +
|
||||
` projections.user_schemas1.change_date,` +
|
||||
` projections.user_schemas1.sequence,` +
|
||||
` projections.user_schemas1.instance_id,` +
|
||||
` projections.user_schemas1.state,` +
|
||||
` projections.user_schemas1.type,` +
|
||||
` projections.user_schemas1.revision,` +
|
||||
` projections.user_schemas1.schema,` +
|
||||
` projections.user_schemas1.possible_authenticators` +
|
||||
` FROM projections.user_schemas`
|
||||
prepareUserSchemaCols = []string{
|
||||
"id",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"sequence",
|
||||
"instance_id",
|
||||
@ -96,6 +100,7 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
uint64(20211109),
|
||||
"instance-id",
|
||||
domain.UserSchemaStateActive,
|
||||
@ -113,9 +118,10 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
},
|
||||
UserSchemas: []*UserSchema{
|
||||
{
|
||||
ID: "id",
|
||||
ObjectDetails: domain.ObjectDetails{
|
||||
ID: "id",
|
||||
EventDate: testNow,
|
||||
CreationDate: testNow,
|
||||
Sequence: 20211109,
|
||||
ResourceOwner: "instance-id",
|
||||
},
|
||||
@ -139,6 +145,7 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
{
|
||||
"id-1",
|
||||
testNow,
|
||||
testNow,
|
||||
uint64(20211109),
|
||||
"instance-id",
|
||||
domain.UserSchemaStateActive,
|
||||
@ -150,6 +157,7 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
{
|
||||
"id-2",
|
||||
testNow,
|
||||
testNow,
|
||||
uint64(20211110),
|
||||
"instance-id",
|
||||
domain.UserSchemaStateInactive,
|
||||
@ -167,9 +175,10 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
},
|
||||
UserSchemas: []*UserSchema{
|
||||
{
|
||||
ID: "id-1",
|
||||
ObjectDetails: domain.ObjectDetails{
|
||||
ID: "id-1",
|
||||
EventDate: testNow,
|
||||
CreationDate: testNow,
|
||||
Sequence: 20211109,
|
||||
ResourceOwner: "instance-id",
|
||||
},
|
||||
@ -180,9 +189,10 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
PossibleAuthenticators: database.NumberArray[domain.AuthenticatorType]{domain.AuthenticatorTypeUsername, domain.AuthenticatorTypePassword},
|
||||
},
|
||||
{
|
||||
ID: "id-2",
|
||||
ObjectDetails: domain.ObjectDetails{
|
||||
ID: "id-2",
|
||||
EventDate: testNow,
|
||||
CreationDate: testNow,
|
||||
Sequence: 20211110,
|
||||
ResourceOwner: "instance-id",
|
||||
},
|
||||
@ -240,6 +250,7 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
uint64(20211109),
|
||||
"instance-id",
|
||||
domain.UserSchemaStateActive,
|
||||
@ -251,9 +262,10 @@ func Test_UserSchemaPrepares(t *testing.T) {
|
||||
),
|
||||
},
|
||||
object: &UserSchema{
|
||||
ID: "id",
|
||||
ObjectDetails: domain.ObjectDetails{
|
||||
ID: "id",
|
||||
EventDate: testNow,
|
||||
CreationDate: testNow,
|
||||
Sequence: 20211109,
|
||||
ResourceOwner: "instance-id",
|
||||
},
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
@ -79,7 +81,9 @@ type UpdatedEvent struct {
|
||||
SchemaType *string `json:"schemaType,omitempty"`
|
||||
Schema json.RawMessage `json:"schema,omitempty"`
|
||||
PossibleAuthenticators []domain.AuthenticatorType `json:"possibleAuthenticators,omitempty"`
|
||||
SchemaRevision *uint64 `json:"schemaRevision,omitempty"`
|
||||
oldSchemaType string
|
||||
oldRevision uint64
|
||||
}
|
||||
|
||||
func (e *UpdatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
|
||||
@ -139,6 +143,13 @@ func ChangePossibleAuthenticators(possibleAuthenticators []domain.AuthenticatorT
|
||||
}
|
||||
}
|
||||
|
||||
func IncreaseRevision(oldRevision uint64) func(event *UpdatedEvent) {
|
||||
return func(e *UpdatedEvent) {
|
||||
e.SchemaRevision = gu.Ptr(oldRevision + 1)
|
||||
e.oldRevision = oldRevision
|
||||
}
|
||||
}
|
||||
|
||||
type DeactivatedEvent struct {
|
||||
*eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
25
internal/repository/user/schemauser/aggregate.go
Normal file
25
internal/repository/user/schemauser/aggregate.go
Normal file
@ -0,0 +1,25 @@
|
||||
package schemauser
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
const (
|
||||
AggregateType = "user"
|
||||
AggregateVersion = "v3"
|
||||
)
|
||||
|
||||
type Aggregate struct {
|
||||
eventstore.Aggregate
|
||||
}
|
||||
|
||||
func NewAggregate(id, resourceOwner string) *Aggregate {
|
||||
return &Aggregate{
|
||||
Aggregate: eventstore.Aggregate{
|
||||
Type: AggregateType,
|
||||
Version: AggregateVersion,
|
||||
ID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
202
internal/repository/user/schemauser/email.go
Normal file
202
internal/repository/user/schemauser/email.go
Normal file
@ -0,0 +1,202 @@
|
||||
package schemauser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
emailEventPrefix = eventPrefix + "email."
|
||||
EmailUpdatedType = emailEventPrefix + "updated"
|
||||
EmailVerifiedType = emailEventPrefix + "verified"
|
||||
EmailVerificationFailedType = emailEventPrefix + "verification.failed"
|
||||
EmailCodeAddedType = emailEventPrefix + "code.added"
|
||||
EmailCodeSentType = emailEventPrefix + "code.sent"
|
||||
)
|
||||
|
||||
type EmailUpdatedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
EmailAddress domain.EmailAddress `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
func (e *EmailUpdatedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *EmailUpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewEmailUpdatedEvent(ctx context.Context, aggregate *eventstore.Aggregate, emailAddress domain.EmailAddress) *EmailUpdatedEvent {
|
||||
return &EmailUpdatedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
EmailUpdatedType,
|
||||
),
|
||||
EmailAddress: emailAddress,
|
||||
}
|
||||
}
|
||||
|
||||
func EmailUpdatedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
emailChangedEvent := &EmailUpdatedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := event.Unmarshal(emailChangedEvent)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed")
|
||||
}
|
||||
|
||||
return emailChangedEvent, nil
|
||||
}
|
||||
|
||||
type EmailVerifiedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
IsEmailVerified bool `json:"-"`
|
||||
}
|
||||
|
||||
func (e *EmailVerifiedEvent) Payload() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmailVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewEmailVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailVerifiedEvent {
|
||||
return &EmailVerifiedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
EmailVerifiedType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func HumanVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
emailVerified := &EmailVerifiedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
IsEmailVerified: true,
|
||||
}
|
||||
return emailVerified, nil
|
||||
}
|
||||
|
||||
type EmailVerificationFailedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *EmailVerificationFailedEvent) Payload() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmailVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHumanEmailVerificationFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailVerificationFailedEvent {
|
||||
return &EmailVerificationFailedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
EmailVerificationFailedType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func EmailVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
return &EmailVerificationFailedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type EmailCodeAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
URLTemplate string `json:"url_template,omitempty"`
|
||||
CodeReturned bool `json:"code_returned,omitempty"`
|
||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||
}
|
||||
|
||||
func (e *EmailCodeAddedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *EmailCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmailCodeAddedEvent) TriggerOrigin() string {
|
||||
return e.TriggeredAtOrigin
|
||||
}
|
||||
|
||||
func NewEmailCodeAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
code *crypto.CryptoValue,
|
||||
expiry time.Duration,
|
||||
urlTemplate string,
|
||||
codeReturned bool,
|
||||
) *EmailCodeAddedEvent {
|
||||
return &EmailCodeAddedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
EmailCodeAddedType,
|
||||
),
|
||||
Code: code,
|
||||
Expiry: expiry,
|
||||
URLTemplate: urlTemplate,
|
||||
CodeReturned: codeReturned,
|
||||
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
|
||||
}
|
||||
}
|
||||
|
||||
func EmailCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
codeAdded := &EmailCodeAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := event.Unmarshal(codeAdded)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "USER-3M0sd", "unable to unmarshal human email code added")
|
||||
}
|
||||
|
||||
return codeAdded, nil
|
||||
}
|
||||
|
||||
type EmailCodeSentEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *EmailCodeSentEvent) Payload() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EmailCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewHumanEmailCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailCodeSentEvent {
|
||||
return &EmailCodeSentEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
EmailCodeSentType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func EmailCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
return &EmailCodeSentEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
9
internal/repository/user/schemauser/eventstore.go
Normal file
9
internal/repository/user/schemauser/eventstore.go
Normal file
@ -0,0 +1,9 @@
|
||||
package schemauser
|
||||
|
||||
import "github.com/zitadel/zitadel/internal/eventstore"
|
||||
|
||||
func init() {
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, CreatedType, eventstore.GenericEventMapper[CreatedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, UpdatedType, eventstore.GenericEventMapper[UpdatedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, DeletedType, eventstore.GenericEventMapper[DeletedEvent])
|
||||
}
|
198
internal/repository/user/schemauser/phone.go
Normal file
198
internal/repository/user/schemauser/phone.go
Normal file
@ -0,0 +1,198 @@
|
||||
package schemauser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/http"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
phoneEventPrefix = eventPrefix + "phone."
|
||||
PhoneUpdatedType = phoneEventPrefix + "updated"
|
||||
PhoneVerifiedType = phoneEventPrefix + "verified"
|
||||
PhoneVerificationFailedType = phoneEventPrefix + "verification.failed"
|
||||
PhoneCodeAddedType = phoneEventPrefix + "code.added"
|
||||
PhoneCodeSentType = phoneEventPrefix + "code.sent"
|
||||
)
|
||||
|
||||
type PhoneChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
|
||||
}
|
||||
|
||||
func (e *PhoneChangedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *PhoneChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone domain.PhoneNumber) *PhoneChangedEvent {
|
||||
return &PhoneChangedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
PhoneUpdatedType,
|
||||
),
|
||||
PhoneNumber: phone,
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
phoneChangedEvent := &PhoneChangedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := event.Unmarshal(phoneChangedEvent)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal phone changed")
|
||||
}
|
||||
|
||||
return phoneChangedEvent, nil
|
||||
}
|
||||
|
||||
type PhoneVerifiedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
IsPhoneVerified bool `json:"-"`
|
||||
}
|
||||
|
||||
func (e *PhoneVerifiedEvent) Payload() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *PhoneVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPhoneVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneVerifiedEvent {
|
||||
return &PhoneVerifiedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
PhoneVerifiedType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
return &PhoneVerifiedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
IsPhoneVerified: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type PhoneVerificationFailedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *PhoneVerificationFailedEvent) Payload() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *PhoneVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPhoneVerificationFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneVerificationFailedEvent {
|
||||
return &PhoneVerificationFailedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
PhoneVerificationFailedType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
return &PhoneVerificationFailedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type PhoneCodeAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Code *crypto.CryptoValue `json:"code,omitempty"`
|
||||
Expiry time.Duration `json:"expiry,omitempty"`
|
||||
CodeReturned bool `json:"code_returned,omitempty"`
|
||||
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
|
||||
}
|
||||
|
||||
func (e *PhoneCodeAddedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *PhoneCodeAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *PhoneCodeAddedEvent) TriggerOrigin() string {
|
||||
return e.TriggeredAtOrigin
|
||||
}
|
||||
|
||||
func NewPhoneCodeAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
code *crypto.CryptoValue,
|
||||
expiry time.Duration,
|
||||
codeReturned bool,
|
||||
) *PhoneCodeAddedEvent {
|
||||
return &PhoneCodeAddedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
PhoneCodeAddedType,
|
||||
),
|
||||
Code: code,
|
||||
Expiry: expiry,
|
||||
CodeReturned: codeReturned,
|
||||
TriggeredAtOrigin: http.DomainContext(ctx).Origin(),
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneCodeAddedEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
codeAdded := &PhoneCodeAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
err := event.Unmarshal(codeAdded)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "USER-6Ms9d", "unable to unmarshal phone code added")
|
||||
}
|
||||
|
||||
return codeAdded, nil
|
||||
}
|
||||
|
||||
type PhoneCodeSentEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *PhoneCodeSentEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *PhoneCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPhoneCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneCodeSentEvent {
|
||||
return &PhoneCodeSentEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
PhoneCodeSentType,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) {
|
||||
return &PhoneCodeSentEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}, nil
|
||||
}
|
144
internal/repository/user/schemauser/user.go
Normal file
144
internal/repository/user/schemauser/user.go
Normal file
@ -0,0 +1,144 @@
|
||||
package schemauser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
const (
|
||||
eventPrefix = "user."
|
||||
CreatedType = eventPrefix + "created"
|
||||
UpdatedType = eventPrefix + "updated"
|
||||
DeletedType = eventPrefix + "deleted"
|
||||
)
|
||||
|
||||
type CreatedEvent struct {
|
||||
*eventstore.BaseEvent `json:"-"`
|
||||
ID string `json:"id"`
|
||||
SchemaID string `json:"schemaID"`
|
||||
SchemaRevision uint64 `json:"schemaRevision"`
|
||||
Data json.RawMessage `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
func (e *CreatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
|
||||
e.BaseEvent = event
|
||||
}
|
||||
|
||||
func (e *CreatedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *CreatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCreatedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
|
||||
schemaID string,
|
||||
schemaRevision uint64,
|
||||
data json.RawMessage,
|
||||
) *CreatedEvent {
|
||||
return &CreatedEvent{
|
||||
BaseEvent: eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
CreatedType,
|
||||
),
|
||||
SchemaID: schemaID,
|
||||
SchemaRevision: schemaRevision,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
type UpdatedEvent struct {
|
||||
*eventstore.BaseEvent `json:"-"`
|
||||
|
||||
SchemaID *string `json:"schemaID,omitempty"`
|
||||
SchemaRevision *uint64 `json:"schemaRevision,omitempty"`
|
||||
Data json.RawMessage `json:"schema,omitempty"`
|
||||
oldSchemaID string
|
||||
oldRevision uint64
|
||||
}
|
||||
|
||||
func (e *UpdatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
|
||||
e.BaseEvent = event
|
||||
}
|
||||
|
||||
func (e *UpdatedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *UpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
func NewUpdatedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
changes []Changes,
|
||||
) *UpdatedEvent {
|
||||
updatedEvent := &UpdatedEvent{
|
||||
BaseEvent: eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
UpdatedType,
|
||||
),
|
||||
}
|
||||
for _, change := range changes {
|
||||
change(updatedEvent)
|
||||
}
|
||||
return updatedEvent
|
||||
}
|
||||
|
||||
type Changes func(event *UpdatedEvent)
|
||||
|
||||
func ChangeSchemaID(oldSchemaID, schemaID string) func(event *UpdatedEvent) {
|
||||
return func(e *UpdatedEvent) {
|
||||
e.SchemaID = &schemaID
|
||||
e.oldSchemaID = oldSchemaID
|
||||
}
|
||||
}
|
||||
func ChangeSchemaRevision(oldSchemaRevision, schemaRevision uint64) func(event *UpdatedEvent) {
|
||||
return func(e *UpdatedEvent) {
|
||||
e.SchemaRevision = &schemaRevision
|
||||
e.oldRevision = oldSchemaRevision
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeData(data json.RawMessage) func(event *UpdatedEvent) {
|
||||
return func(e *UpdatedEvent) {
|
||||
e.Data = data
|
||||
}
|
||||
}
|
||||
|
||||
type DeletedEvent struct {
|
||||
*eventstore.BaseEvent `json:"-"`
|
||||
}
|
||||
|
||||
func (e *DeletedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
|
||||
e.BaseEvent = event
|
||||
}
|
||||
|
||||
func (e *DeletedEvent) Payload() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *DeletedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDeletedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
) *DeletedEvent {
|
||||
return &DeletedEvent{
|
||||
BaseEvent: eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
DeletedType,
|
||||
),
|
||||
}
|
||||
}
|
@ -593,6 +593,11 @@ Errors:
|
||||
NotActive: Потребителската схема не е активна
|
||||
NotInactive: Потребителската схема не е неактивна
|
||||
NotExists: Потребителската схема не съществува
|
||||
ID:
|
||||
Missing: Липсва идентификатор на потребителска схема
|
||||
Invalid: Потребителската схема е невалидна
|
||||
Data:
|
||||
Invalid: Невалидни данни за потребителска схема
|
||||
TokenExchange:
|
||||
FeatureDisabled: Функцията Token Exchange е деактивирана за вашето копие. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1351,6 +1356,26 @@ EventTypes:
|
||||
deactivated: Потребителската схема е деактивирана
|
||||
reactivated: Потребителската схема е активирана отново
|
||||
deleted: Потребителската схема е изтрита
|
||||
user:
|
||||
created: Потребител е създаден
|
||||
updated: Потребителят е актуализиран
|
||||
deleted: Потребителят е изтрит
|
||||
email:
|
||||
updated: Имейл адресът е променен
|
||||
verified: Имейл адресът е потвърден
|
||||
verification:
|
||||
failed: Проверката на имейл адреса не бе успешна
|
||||
code:
|
||||
added: Генериран код за потвърждение на имейл адрес
|
||||
sent: Кодът за потвърждение на имейл адреса е изпратен
|
||||
phone:
|
||||
updated: Телефонният номер е променен
|
||||
verified: Телефонният номер е потвърден
|
||||
verification:
|
||||
failed: Неуспешна проверка на телефонния номер
|
||||
code:
|
||||
added: Генериран код за потвърждение на телефонен номер
|
||||
sent: Кодът за потвърждение на телефонния номер е изпратен
|
||||
web_key:
|
||||
added: Добавен уеб ключ
|
||||
activated: Уеб ключът е активиран
|
||||
|
@ -574,6 +574,11 @@ Errors:
|
||||
NotActive: Uživatelské schéma není aktivní
|
||||
NotInactive: Uživatelské schéma není neaktivní
|
||||
NotExists: Uživatelské schéma neexistuje
|
||||
ID:
|
||||
Missing: Chybí ID schématu uživatele
|
||||
Invalid: Uživatelské schéma je neplatné
|
||||
Data:
|
||||
Invalid: Data neplatná pro uživatelské schéma
|
||||
TokenExchange:
|
||||
FeatureDisabled: Funkce Token Exchange je pro vaši instanci zakázána. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1317,6 +1322,26 @@ EventTypes:
|
||||
deactivated: Uživatelské schéma deaktivováno
|
||||
reactivated: Uživatelské schéma bylo znovu aktivováno
|
||||
deleted: Uživatelské schéma bylo smazáno
|
||||
user:
|
||||
created: Uživatel vytvořen
|
||||
updated: Uživatel aktualizován
|
||||
deleted: Uživatel byl smazán
|
||||
email:
|
||||
updated: E-mailová adresa změněna
|
||||
verified: E-mailová adresa ověřena
|
||||
verification:
|
||||
failed: Ověření e-mailové adresy se nezdařilo
|
||||
code:
|
||||
added: Vygenerován ověřovací kód e-mailové adresy
|
||||
sent: Ověřovací kód e-mailové adresy odeslán
|
||||
phone:
|
||||
updated: Telefonní číslo změněno
|
||||
verified: Telefonní číslo ověřeno
|
||||
verification:
|
||||
failed: Ověření telefonního čísla se nezdařilo
|
||||
code:
|
||||
added: Byl vygenerován ověřovací kód telefonního čísla
|
||||
sent: Ověřovací kód telefonního čísla odeslán
|
||||
web_key:
|
||||
added: Přidán webový klíč
|
||||
activated: Web Key aktivován
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Benutzerschema nicht aktiv
|
||||
NotInactive: Benutzerschema nicht inaktiv
|
||||
NotExists: Benutzerschema existiert nicht
|
||||
ID:
|
||||
Missing: BenutzerschemaID fehlt
|
||||
Invalid: Benutzerschema ist ungültig
|
||||
Data:
|
||||
Invalid: Daten für Benutzerschema ungültig
|
||||
TokenExchange:
|
||||
FeatureDisabled: Die Token-Austauschfunktion ist für Ihre Instanz deaktiviert. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1319,6 +1324,26 @@ EventTypes:
|
||||
deactivated: Benutzerschema deaktiviert
|
||||
reactivated: Benutzerschema reaktiviert
|
||||
deleted: Benutzerschema gelöscht
|
||||
user:
|
||||
created: Benutzer erstellt
|
||||
updated: Benutzer aktualisiert
|
||||
deleted: Benutzer gelöscht
|
||||
email:
|
||||
updated: E-Mail-Adresse geändert
|
||||
verified: E-Mail-Adresse verifiziert
|
||||
verification:
|
||||
failed: E-Mail-Adressenverifizierung fehlgeschlagen
|
||||
code:
|
||||
added: E-Mail-Adressenverifizierungscode generiert
|
||||
sent: E-Mail-Adressenverifizierungscode gesendet
|
||||
phone:
|
||||
updated: Telefonnummer geändert
|
||||
verified: Telefonnummer verifiziert
|
||||
verification:
|
||||
failed: Telefonnummernverifizierung fehlgeschlagen
|
||||
code:
|
||||
added: Telefonnummernverifizierungscode generiert
|
||||
sent: Telefonnummernverifizierungscode gesendet
|
||||
web_key:
|
||||
added: Web Key hinzugefügt
|
||||
activated: Web Key aktiviert
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: User Schema not active
|
||||
NotInactive: User Schema not inactive
|
||||
NotExists: User Schema does not exist
|
||||
ID:
|
||||
Missing: User Schema ID missing
|
||||
Invalid: User Schema invalid
|
||||
Data:
|
||||
Invalid: Data invalid for User Schema
|
||||
TokenExchange:
|
||||
FeatureDisabled: Token Exchange feature is disabled for your instance. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1319,6 +1324,28 @@ EventTypes:
|
||||
deactivated: User Schema deactivated
|
||||
reactivated: User Schema reactivated
|
||||
deleted: User Schema deleted
|
||||
user:
|
||||
created: User created
|
||||
updated: User updated
|
||||
deleted: User deleted
|
||||
email:
|
||||
updated: Email address changed
|
||||
verified: Email address verified
|
||||
verification:
|
||||
failed: Email address verification failed
|
||||
code:
|
||||
added: Email address verification code generated
|
||||
sent: Email address verification code sent
|
||||
phone:
|
||||
updated: Phone number changed
|
||||
verified: Phone number verified
|
||||
verification:
|
||||
failed: Phone number verification failed
|
||||
code:
|
||||
added: Phone number verification code generated
|
||||
sent: Phone number verification code sent
|
||||
|
||||
|
||||
web_key:
|
||||
added: Web Key added
|
||||
activated: Web Key activated
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Esquema de usuario no activo
|
||||
NotInactive: Esquema de usuario no inactivo
|
||||
NotExists: El esquema de usuario no existe
|
||||
ID:
|
||||
Missing: Falta el ID del esquema de usuario
|
||||
Invalid: Esquema de usuario no válido
|
||||
Data:
|
||||
Invalid: Datos no válidos para el esquema de usuario
|
||||
TokenExchange:
|
||||
FeatureDisabled: La función de intercambio de tokens está deshabilitada para su instancia. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1319,6 +1324,26 @@ EventTypes:
|
||||
deactivated: Esquema de usuario desactivado
|
||||
reactivated: Esquema de usuario reactivado
|
||||
deleted: Esquema de usuario eliminado
|
||||
user:
|
||||
created: Usuario creado
|
||||
updated: Usuario actualizado
|
||||
deleted: Usuario eliminado
|
||||
email:
|
||||
updated: Dirección de correo electrónico modificada
|
||||
verified: Dirección de correo electrónico verificada
|
||||
verification:
|
||||
failed: Error en la verificación de la dirección de correo electrónico
|
||||
code:
|
||||
added: Código de verificación de la dirección de correo electrónico generado
|
||||
sent: Código de verificación de la dirección de correo electrónico enviado
|
||||
phone:
|
||||
updated: Número de teléfono modificado
|
||||
verified: Número de teléfono verificado
|
||||
verification:
|
||||
failed: Error en la verificación del número de teléfono
|
||||
code:
|
||||
added: Código de verificación del número de teléfono generado
|
||||
sent: Código de verificación del número de teléfono enviado
|
||||
web_key:
|
||||
added: Clave web añadida
|
||||
activated: Clave web activada
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Schéma utilisateur non actif
|
||||
NotInactive: Le schéma utilisateur n'est pas inactif
|
||||
NotExists: Le schéma utilisateur n'existe pas
|
||||
ID:
|
||||
Missing: ID de schéma utilisateur manquant
|
||||
Invalid: Schéma utilisateur non valide
|
||||
Data:
|
||||
Invalid: Données non valides pour le schéma utilisateur
|
||||
TokenExchange:
|
||||
FeatureDisabled: La fonctionnalité Token Exchange est désactivée pour votre instance. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1314,6 +1319,26 @@ EventTypes:
|
||||
deactivated: Schéma utilisateur désactivé
|
||||
reactivated: Schéma utilisateur réactivé
|
||||
deleted: Schéma utilisateur supprimé
|
||||
user:
|
||||
created: Utilisateur créé
|
||||
updated: Utilisateur mis à jour
|
||||
deleted: Utilisateur supprimé
|
||||
email:
|
||||
updated: Adresse e-mail modifiée
|
||||
verified: Adresse e-mail vérifiée
|
||||
verification:
|
||||
failed: Échec de la vérification de l'adresse e-mail
|
||||
code:
|
||||
added: Code de vérification de l'adresse e-mail généré
|
||||
sent: Code de vérification de l'adresse e-mail envoyé
|
||||
phone:
|
||||
updated: Numéro de téléphone modifié
|
||||
verified: Numéro de téléphone vérifié
|
||||
verification:
|
||||
failed: Échec de la vérification du numéro de téléphone
|
||||
code:
|
||||
added: Code de vérification du numéro de téléphone généré
|
||||
sent: Code de vérification du numéro de téléphone envoyé
|
||||
web_key:
|
||||
added: Clé Web ajoutée
|
||||
activated: Clé Web activée
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Schema utente non attivo
|
||||
NotInactive: Schema utente non inattivo
|
||||
NotExists: Lo schema utente non esiste
|
||||
ID:
|
||||
Missing: ID schema utente mancante
|
||||
Invalid: Schema utente non valido
|
||||
Data:
|
||||
Invalid: Dati non validi per lo schema utente
|
||||
TokenExchange:
|
||||
FeatureDisabled: La funzionalità di scambio token è disabilitata per la tua istanza. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1315,6 +1320,26 @@ EventTypes:
|
||||
deactivated: Schema utente disattivato
|
||||
reactivated: Schema utente riattivato
|
||||
deleted: Schema utente eliminato
|
||||
user:
|
||||
created: Utente creato
|
||||
updated: Utente aggiornato
|
||||
deleted: Utente eliminato
|
||||
email:
|
||||
updated: Indirizzo email modificato
|
||||
verified: Indirizzo email verificato
|
||||
verification:
|
||||
failed: Verifica indirizzo email non riuscita
|
||||
code:
|
||||
added: Codice di verifica indirizzo email generato
|
||||
sent: Codice di verifica indirizzo email inviato
|
||||
phone:
|
||||
updated: Numero di telefono modificato
|
||||
verified: Numero di telefono verificato
|
||||
verification:
|
||||
failed: Verifica numero di telefono non riuscita
|
||||
code:
|
||||
added: Codice di verifica numero di telefono generato
|
||||
sent: Codice di verifica numero di telefono inviato
|
||||
web_key:
|
||||
added: Web Key aggiunto
|
||||
activated: Web Key attivato
|
||||
|
@ -565,6 +565,11 @@ Errors:
|
||||
NotActive: ユーザースキーマがアクティブではありません
|
||||
NotInactive: ユーザースキーマが非アクティブではありません
|
||||
NotExists: ユーザースキーマが存在しません
|
||||
ID:
|
||||
Missing: ユーザー スキーマ ID がありません
|
||||
Invalid: ユーザー スキーマが無効です
|
||||
Data:
|
||||
Invalid: ユーザー スキーマのデータが無効です
|
||||
TokenExchange:
|
||||
FeatureDisabled: インスタンスではトークン交換機能が無効になっています。 https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1305,6 +1310,26 @@ EventTypes:
|
||||
deactivated: ユーザースキーマが非アクティブ化されました
|
||||
reactivated: ユーザースキーマが再アクティブ化されました
|
||||
deleted: ユーザースキーマが削除されました
|
||||
user:
|
||||
created: ユーザーが作成されました
|
||||
updated: ユーザーが更新されました
|
||||
deleted: ユーザーが削除されました
|
||||
email:
|
||||
updated: メールアドレスが変更されました
|
||||
verified: メールアドレスが確認されました
|
||||
verification:
|
||||
failed: メールアドレスの確認に失敗しました
|
||||
code:
|
||||
added: メールアドレスの確認コードが生成されました
|
||||
sent: メールアドレスの確認コードが送信されました
|
||||
phone:
|
||||
updated: 電話番号が変更されました
|
||||
verified: 電話番号が確認されました
|
||||
verification:
|
||||
failed: 電話番号の確認に失敗しました
|
||||
code:
|
||||
added: 電話番号の確認コードが生成されました
|
||||
sent: 電話番号の確認コードが送信されました
|
||||
web_key:
|
||||
added: Web キーが追加されました
|
||||
activated: Web キーが有効化されました
|
||||
|
@ -575,6 +575,11 @@ Errors:
|
||||
NotActive: Корисничката шема не е активна
|
||||
NotInactive: Корисничката шема не е неактивна
|
||||
NotExists: Корисничката шема не постои
|
||||
ID:
|
||||
Missing: Недостасува ID на корисничка шема
|
||||
Invalid: Корисничката шема е неважечка
|
||||
Data:
|
||||
Invalid: Податоците не се валидни за корисничка шема
|
||||
TokenExchange:
|
||||
FeatureDisabled: Функцијата за размена на токени е оневозможена на вашиот пример. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1317,6 +1322,26 @@ EventTypes:
|
||||
deactivated: Корисничката шема е деактивирана
|
||||
reactivated: Корисничката шема е реактивирана
|
||||
deleted: Корисничката шема е избришана
|
||||
user:
|
||||
created: Корисникот е создаден
|
||||
updated: Корисникот е ажуриран
|
||||
deleted: Корисникот е избришан
|
||||
email:
|
||||
updated: Адресата на е-пошта е променета
|
||||
verified: Адресата на е-пошта е потврдена
|
||||
verification:
|
||||
failed: Потврдата на адресата на е-пошта не успеа
|
||||
code:
|
||||
added: Генериран е код за потврда на адресата на е-пошта
|
||||
sent: Испратен е код за потврда на адресата на е-пошта
|
||||
phone:
|
||||
updated: Телефонскиот број е променет
|
||||
verified: Телефонскиот број е потврден
|
||||
verification:
|
||||
failed: Потврдата на телефонскиот број не успеа
|
||||
code:
|
||||
added: Генериран е кодот за потврда на телефонскиот број
|
||||
sent: Кодот за потврда на телефонскиот број е испратен
|
||||
web_key:
|
||||
added: Додаден е веб-клуч
|
||||
activated: Веб-клучот е активиран
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Gebruikersschema niet actief
|
||||
NotInactive: Gebruikersschema niet inactief
|
||||
NotExists: Gebruikersschema bestaat niet
|
||||
ID:
|
||||
Missing: Недостасува ID на корисничка шема
|
||||
Invalid: Корисничката шема е неважечка
|
||||
Data:
|
||||
Invalid: Податоците не се валидни за корисничка шема
|
||||
TokenExchange:
|
||||
FeatureDisabled: De Token Exchange-functie is uitgeschakeld voor uw instantie. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1314,6 +1319,26 @@ EventTypes:
|
||||
deactivated: Gebruikersschema gedeactiveerd
|
||||
reactivated: Gebruikersschema opnieuw geactiveerd
|
||||
deleted: Gebruikersschema verwijderd
|
||||
user:
|
||||
created: Gebruiker aangemaakt
|
||||
updated: Gebruiker bijgewerkt
|
||||
deleted: Gebruiker verwijderd
|
||||
email:
|
||||
updated: E-mailadres gewijzigd
|
||||
verified: E-mailadres geverifieerd
|
||||
verification:
|
||||
failed: E-mailadres verificatie mislukt
|
||||
code:
|
||||
added: E-mailadres verificatiecode gegenereerd
|
||||
sent: E-mailadres verificatiecode verzonden
|
||||
phone:
|
||||
updated: Telefoonnummer gewijzigd
|
||||
verified: Telefoonnummer geverifieerd
|
||||
verification:
|
||||
failed: Telefoonnummer verificatie mislukt
|
||||
code:
|
||||
added: Telefoonnummer verificatiecode gegenereerd
|
||||
sent: Telefoonnummer verificatiecode verzonden
|
||||
web_key:
|
||||
added: Web Key toegevoegd
|
||||
activated: Web Key geactiveerd
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Schemat użytkownika nieaktywny
|
||||
NotInactive: Schemat użytkownika nie jest nieaktywny
|
||||
NotExists: Schemat użytkownika nie istnieje
|
||||
ID:
|
||||
Missing: Brak identyfikatora schematu użytkownika
|
||||
Invalid: Nieprawidłowy schemat użytkownika
|
||||
Data:
|
||||
Invalid: Nieprawidłowe dane dla schematu użytkownika
|
||||
TokenExchange:
|
||||
FeatureDisabled: Funkcja wymiany tokenów jest wyłączona dla Twojej instancji. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1319,6 +1324,26 @@ EventTypes:
|
||||
deactivated: Schemat użytkownika dezaktywowany
|
||||
reactivated: Schemat użytkownika został ponownie aktywowany
|
||||
deleted: Schemat użytkownika został usunięty
|
||||
user:
|
||||
created: Użytkownik utworzony
|
||||
updated: Użytkownik zaktualizowany
|
||||
deleted: Użytkownik usunięty
|
||||
email:
|
||||
updated: Adres e-mail zmieniony
|
||||
verified: Adres e-mail zweryfikowany
|
||||
verification:
|
||||
failed: Weryfikacja adresu e-mail nie powiodła się
|
||||
code:
|
||||
added: Wygenerowano kod weryfikacyjny adresu e-mail
|
||||
sent: Wysłano kod weryfikacyjny adresu e-mail
|
||||
phone:
|
||||
updated: Numer telefonu zmieniony
|
||||
verified: Numer telefonu zweryfikowany
|
||||
verification:
|
||||
failed: Weryfikacja numeru telefonu nie powiodła się
|
||||
code:
|
||||
added: Wygenerowano kod weryfikacyjny numeru telefonu
|
||||
sent: Wysłano kod weryfikacyjny numeru telefonu
|
||||
web_key:
|
||||
added: Dodano klucz internetowy
|
||||
activated: Klucz internetowy aktywowano
|
||||
|
@ -571,6 +571,11 @@ Errors:
|
||||
NotActive: Esquema do usuário não ativo
|
||||
NotInactive: Esquema do usuário não inativo
|
||||
NotExists: O esquema do usuário não existe
|
||||
ID:
|
||||
Missing: ID do esquema do utilizador em falta
|
||||
Invalid: Esquema de utilizador inválido
|
||||
Data:
|
||||
Invalid: Dados inválidos para o esquema do utilizador
|
||||
TokenExchange:
|
||||
FeatureDisabled: O recurso Token Exchange está desabilitado para sua instância. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1311,6 +1316,26 @@ EventTypes:
|
||||
deactivated: Esquema de usuário desativado
|
||||
reactivated: Esquema do usuário reativado
|
||||
deleted: Esquema do usuário excluído
|
||||
user:
|
||||
created: Utilizador criado
|
||||
updated: Utilizador atualizado
|
||||
deleted: Utilizador excluído
|
||||
email:
|
||||
updated: Endereço de e-mail alterado
|
||||
verified: Endereço de e-mail verificado
|
||||
verification:
|
||||
failed: Falha na verificação do endereço de e-mail
|
||||
code:
|
||||
added: Código de verificação do endereço de e-mail gerado
|
||||
sent: Código de verificação do endereço de e-mail enviado
|
||||
phone:
|
||||
updated: Número de telefone alterado
|
||||
verified: Número de telefone verificado
|
||||
verification:
|
||||
failed: Falha na verificação do número de telefone
|
||||
code:
|
||||
added: Código de verificação do número de telefone gerado
|
||||
sent: Código de verificação do número de telefone enviado
|
||||
web_key:
|
||||
added: Chave Web adicionada
|
||||
activated: Chave Web ativada
|
||||
|
@ -565,6 +565,11 @@ Errors:
|
||||
NotActive: Пользовательская схема не активна
|
||||
NotInactive: Пользовательская схема не неактивна
|
||||
NotExists: Пользовательская схема не существует
|
||||
ID:
|
||||
Missing: Отсутствует идентификатор схемы пользователя
|
||||
Invalid: Недействительная схема пользователя
|
||||
Data:
|
||||
Invalid: Данные недействительны для схемы пользователя
|
||||
TokenExchange:
|
||||
FeatureDisabled: Функция обмена токенами отключена для вашего экземпляра. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1305,6 +1310,26 @@ EventTypes:
|
||||
deactivated: Пользовательская схема деактивирована
|
||||
reactivated: Пользовательская схема повторно активирована
|
||||
deleted: Пользовательская схема удалена
|
||||
user:
|
||||
created: Пользователь создан
|
||||
updated: Пользователь обновлен
|
||||
deleted: Пользователь удален
|
||||
email:
|
||||
updated: Адрес электронной почты изменен
|
||||
verified: Адрес электронной почты проверен
|
||||
verification:
|
||||
failed: Проверка адреса электронной почты не удалась
|
||||
code:
|
||||
added: Сгенерирован код проверки адреса электронной почты
|
||||
sent: Отправлен код проверки адреса электронной почты
|
||||
phone:
|
||||
updated: Номер телефона изменен
|
||||
verified: Номер телефона проверен
|
||||
verification:
|
||||
failed: Проверка номера телефона не удалась
|
||||
code:
|
||||
added: Сгенерирован код проверки номера телефона
|
||||
sent: Отправлен код проверки номера телефона
|
||||
web_key:
|
||||
added: Добавлен веб-ключ
|
||||
activated: Веб-ключ активирован
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: Användarschema inte aktivt
|
||||
NotInactive: Användarschema inte inaktivt
|
||||
NotExists: Användarschema existerar inte
|
||||
ID:
|
||||
Missing: Användarschema-ID saknas
|
||||
Invalid: Ogiltigt användarschema
|
||||
Data:
|
||||
Invalid: Data ogiltig för användarschema
|
||||
TokenExchange:
|
||||
FeatureDisabled: Token Exchange-funktionen är inaktiverad för din instans. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1319,6 +1324,26 @@ EventTypes:
|
||||
deactivated: Användarschema avaktiverat
|
||||
reactivated: Användarschema återaktiverat
|
||||
deleted: Användarschema borttaget
|
||||
user:
|
||||
created: Användare skapad
|
||||
updated: Användaren uppdaterad
|
||||
deleted: Användare raderad
|
||||
email:
|
||||
updated: E-postadress ändrad
|
||||
verified: E-postadress verifierad
|
||||
verification:
|
||||
failed: E-postadressverifiering misslyckades
|
||||
code:
|
||||
added: Verifieringskod för e-postadress genererad
|
||||
sent: E-postadressens verifieringskod har skickats
|
||||
phone:
|
||||
updated: Telefonnummer ändrat
|
||||
verified: Telefonnummer verifierat
|
||||
verification:
|
||||
failed: Verifiering av telefonnummer misslyckades
|
||||
code:
|
||||
added: Verifieringskod för telefonnummer genererad
|
||||
sent: Verifieringskoden för telefonnummer har skickats
|
||||
web_key:
|
||||
added: Webbnyckel har lagts till
|
||||
activated: Webbnyckel aktiverad
|
||||
|
@ -576,6 +576,11 @@ Errors:
|
||||
NotActive: 用户架构未激活
|
||||
NotInactive: 用户架构未处于非活动状态
|
||||
NotExists: 用户架构不存在
|
||||
ID:
|
||||
Missing: 缺少用户架构 ID
|
||||
Invalid: 用户架构无效
|
||||
Data:
|
||||
Invalid: 用户架构的数据无效
|
||||
TokenExchange:
|
||||
FeatureDisabled: 您的实例已禁用令牌交换功能。 https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features
|
||||
Token:
|
||||
@ -1318,6 +1323,26 @@ EventTypes:
|
||||
deactivated: 用户架构已停用
|
||||
reactivated: 用户架构已重新激活
|
||||
deleted: 用户架构已删除
|
||||
user:
|
||||
created: 用户已创建
|
||||
updated: 用户已更新
|
||||
deleted: 用户已删除
|
||||
email:
|
||||
updated: 电子邮件地址已更改
|
||||
verified: 电子邮件地址已验证
|
||||
verification:
|
||||
failed: 电子邮件地址验证失败
|
||||
code:
|
||||
added: 电子邮件地址验证码已生成
|
||||
sent: 电子邮件地址验证码已发送
|
||||
phone:
|
||||
updated: 电话号码已更改
|
||||
verified: 电话号码已验证
|
||||
verification:
|
||||
failed: 电话号码验证失败
|
||||
code:
|
||||
added: 电话号码验证码已生成
|
||||
sent: 电话号码验证码已发送
|
||||
web_key:
|
||||
added: 已添加 Web Key
|
||||
activated: 已激活 Web Key
|
||||
|
@ -27,3 +27,11 @@ message Instance {
|
||||
string domain = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Organization {
|
||||
oneof property {
|
||||
option (validate.required) = true;
|
||||
string org_id = 1;
|
||||
string org_domain = 2;
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ import "zitadel/resources/action/v3alpha/query.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
import "zitadel/object/v3alpha/object.proto";
|
||||
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/action/v3alpha;action";
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
|
@ -1,15 +1,15 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.v3alpha;
|
||||
package zitadel.resources.user.v3alpha;
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "zitadel/object/v2/object.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v3alpha";
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha;user";
|
||||
|
||||
message Authenticators {
|
||||
// All of the user's usernames, which will be used for identification during authentication.
|
||||
@ -109,6 +109,50 @@ message WebAuthN {
|
||||
bool user_verified = 4;
|
||||
}
|
||||
|
||||
message StartWebAuthNRegistration {
|
||||
// Domain on which the user currently is or will be authenticated.
|
||||
string domain = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"my-domain.zitadel.cloud\"";
|
||||
}
|
||||
];
|
||||
// Optionally specify the authenticator type of the passkey device (platform or cross-platform).
|
||||
// If none is provided, both values are allowed.
|
||||
WebAuthNAuthenticatorType authenticator_type = 2;
|
||||
// Optionally provide a one time code generated by ZITADEL.
|
||||
// This is required to start the passkey registration without user authentication.
|
||||
optional AuthenticatorRegistrationCode code = 3;
|
||||
}
|
||||
|
||||
message VerifyWebAuthNRegistration {
|
||||
// PublicKeyCredential Interface.
|
||||
// Generated helper methods populate the field from JSON created by a WebAuthN client.
|
||||
// See also: https://www.w3.org/TR/webauthn/#publickeycredential
|
||||
google.protobuf.Struct public_key_credential = 1 [
|
||||
(validate.rules).message.required = true,
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"type\":\"public-key\",\"id\":\"pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00\",\"rawId\":\"pawVarF4xPxLFmfCnRkwXWeTrKGzabcAi92LEI1WC00\",\"response\":{\"attestationObject\":\"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEcwRQIgRKS3VpeE9tfExXRzkoUKnG4rQWPvtSSt4YtDGgTx32oCIQDPey-2YJ4uIg-QCM4jj6aE2U3tgMFM_RP7Efx6xRu3JGhhdXRoRGF0YVikSZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAADju76085Yhmlt1CEOHkwLQAIKWsFWqxeMT8SxZnwp0ZMF1nk6yhs2m3AIvdixCNVgtNpQECAyYgASFYIMGUDSP2FAQn2MIfPMy7cyB_Y30VqixVgGULTBtFjfRiIlggjUGfQo3_-CrMmH3S-ZQkFKWKnNBQEAMkFtG-9A4zqW0\",\"clientDataJSON\":\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiQlhXdHh0WGxJeFZZa0pHT1dVaUVmM25zby02aXZKdWw2YmNmWHdMVlFIayIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAifQ\"}}";
|
||||
min_length: 55;
|
||||
max_length: 1048576; //1 MB
|
||||
}
|
||||
];
|
||||
// Provide a name for the WebAuthN device. This will help identify it in the future.
|
||||
string web_auth_n_name = 2 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
example: "\"fido key\""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message OTPSMS {
|
||||
// unique identifier of the one-time-password (OTP) SMS authenticator.
|
||||
string otp_sms_id = 1 [
|
||||
@ -167,7 +211,7 @@ message AuthenticationKey {
|
||||
example: "\"69629023906488334\"";
|
||||
}
|
||||
];
|
||||
zitadel.object.v2.Details details = 2;
|
||||
zitadel.resources.object.v3alpha.Details details = 2;
|
||||
// the file type of the key
|
||||
AuthNKeyType type = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
@ -246,6 +290,30 @@ message SetPassword {
|
||||
}
|
||||
// Provide if the user needs to change the password on the next use.
|
||||
bool change_required = 3;
|
||||
// If neither, the current password nor a verification code generated by the PasswordReset is provided,
|
||||
// the user must be granted permission to set a password.
|
||||
oneof verification {
|
||||
// Provide the current password to verify you're allowed to change the password.
|
||||
string current_password = 4 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
example: "\"Secr3tP4ssw0rd!\"";
|
||||
}
|
||||
];
|
||||
// Or provider the verification code generated during password reset request.
|
||||
string verification_code = 5 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 20},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 20;
|
||||
example: "\"SKJd342k\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
message SendPasswordResetEmail {
|
@ -1,8 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.v3alpha;
|
||||
package zitadel.resources.user.v3alpha;
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v3alpha";
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha;user";
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
@ -1,72 +1,71 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.v3alpha;
|
||||
package zitadel.resources.user.v3alpha;
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v3alpha";
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha;user";
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "zitadel/user/v3alpha/user.proto";
|
||||
import "zitadel/object/v2/object.proto";
|
||||
import "zitadel/resources/user/v3alpha/user.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
|
||||
message SearchQuery {
|
||||
oneof query {
|
||||
message SearchFilter {
|
||||
oneof Filter {
|
||||
option (validate.required) = true;
|
||||
|
||||
|
||||
// Union the results of each sub query ('OR').
|
||||
OrQuery or_query = 1;
|
||||
// Union the results of each sub filter ('OR').
|
||||
OrFilter or_filter = 1;
|
||||
// Limit the result to match all sub queries ('AND').
|
||||
// Note that if you specify multiple queries, they will be implicitly used as andQueries.
|
||||
// Use the andQuery in combination with orQuery and notQuery.
|
||||
AndQuery and_query = 2;
|
||||
// Use the andFilter in combination with orFilter and notFilter.
|
||||
AndFilter and_filter = 2;
|
||||
// Exclude / Negate the result of the sub query ('NOT').
|
||||
NotQuery not_query = 3;
|
||||
NotFilter not_filter = 3;
|
||||
|
||||
// Limit the result to a specific user ID.
|
||||
UserIDQuery user_id_query = 4;
|
||||
UserIDFilter user_id_filter = 4;
|
||||
// Limit the result to a specific organization.
|
||||
OrganizationIDQuery organization_id_query = 5;
|
||||
OrganizationIDFilter organization_id_filter = 5;
|
||||
// Limit the result to a specific username.
|
||||
UsernameQuery username_query = 6;
|
||||
UsernameFilter username_filter = 6;
|
||||
// Limit the result to a specific contact email.
|
||||
EmailQuery email_query = 7;
|
||||
EmailFilter email_filter = 7;
|
||||
// Limit the result to a specific contact phone.
|
||||
PhoneQuery phone_query = 8;
|
||||
PhoneFilter phone_filter = 8;
|
||||
// Limit the result to a specific state of the user.
|
||||
StateQuery state_query = 9;
|
||||
StateFilter state_filter = 9;
|
||||
// Limit the result to a specific schema ID.
|
||||
SchemaIDQuery schema_ID_query = 10;
|
||||
SchemaIDFilter schema_id_filter = 10;
|
||||
// Limit the result to a specific schema type.
|
||||
SchemaTypeQuery schema_type_query = 11;
|
||||
SchemaTypeFilter schema_type_filter = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message OrQuery {
|
||||
repeated SearchQuery queries = 1 [
|
||||
message OrFilter {
|
||||
repeated SearchFilter queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"userIdQuery\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}},{\"userIdQuery\": {\"id\": \"163840776835943483\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}}]"
|
||||
example: "[{\"userIdFilter\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}},{\"userIdFilter\": {\"id\": \"163840776835943483\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}}]"
|
||||
}
|
||||
];
|
||||
}
|
||||
message AndQuery {
|
||||
repeated SearchQuery queries = 1 [
|
||||
message AndFilter {
|
||||
repeated SearchFilter queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"organizationIdQuery\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}},{\"usernameQuery\": {\"username\": \"gigi\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}}]"
|
||||
example: "[{\"organizationIdFilter\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}},{\"usernameFilter\": {\"username\": \"gigi\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}}]"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message NotQuery {
|
||||
SearchQuery query = 1 [
|
||||
message NotFilter {
|
||||
SearchFilter query = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"schemaIDQuery\": {\"id\": \"163840776835432705\"}}"
|
||||
example: "{\"schemaIDFilter\": {\"id\": \"163840776835432705\"}}"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message UserIDQuery {
|
||||
message UserIDFilter {
|
||||
// Defines the ID of the user to query for.
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -78,12 +77,12 @@ message UserIDQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the id query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
message OrganizationIDQuery {
|
||||
message OrganizationIDFilter {
|
||||
// Defines the ID of the organization to query for.
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -95,12 +94,12 @@ message OrganizationIDQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the id query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
message UsernameQuery {
|
||||
message UsernameFilter {
|
||||
// Defines the username to query for.
|
||||
string username = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -112,14 +111,14 @@ message UsernameQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the username query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
// Defines that the username must only be unique in the organisation.
|
||||
bool is_organization_specific = 3;
|
||||
}
|
||||
|
||||
message EmailQuery {
|
||||
message EmailFilter {
|
||||
// Defines the email of the user to query for.
|
||||
string address = 1 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
@ -131,12 +130,12 @@ message EmailQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the email query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
message PhoneQuery {
|
||||
message PhoneFilter {
|
||||
// Defines the phone of the user to query for.
|
||||
string number = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 20},
|
||||
@ -148,13 +147,13 @@ message PhoneQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the phone query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
message StateQuery {
|
||||
message StateFilter {
|
||||
// Defines the state to query for.
|
||||
State state = 1 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
@ -164,7 +163,7 @@ message StateQuery {
|
||||
];
|
||||
}
|
||||
|
||||
message SchemaIDQuery {
|
||||
message SchemaIDFilter {
|
||||
// Defines the ID of the schema to query for.
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -177,7 +176,7 @@ message SchemaIDQuery {
|
||||
];
|
||||
}
|
||||
|
||||
message SchemaTypeQuery {
|
||||
message SchemaTypeFilter {
|
||||
// Defines which type to query for.
|
||||
string type = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -189,7 +188,7 @@ message SchemaTypeQuery {
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the type query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
112
proto/zitadel/resources/user/v3alpha/user.proto
Normal file
112
proto/zitadel/resources/user/v3alpha/user.proto
Normal file
@ -0,0 +1,112 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.resources.user.v3alpha;
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
import "zitadel/resources/user/v3alpha/authenticator.proto";
|
||||
import "zitadel/resources/user/v3alpha/communication.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha;user";
|
||||
|
||||
message CreateUser {
|
||||
// Define the schema the user's data schema by providing it's ID.
|
||||
string schema_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
google.protobuf.Struct data = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"name\":\"Gigi\",\"description\":\"the giraffe\"}"
|
||||
}
|
||||
];
|
||||
// Set the contact information (email, phone) for the user.
|
||||
SetContact contact = 3;
|
||||
// Set the initial authenticators of the user.
|
||||
SetAuthenticators authenticators = 4;
|
||||
// Optionally set a unique identifier of the user. If unset, ZITADEL will take care of it.
|
||||
optional string user_id = 5 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message PatchUser {
|
||||
optional string schema_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
optional google.protobuf.Struct data = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"name\":\"Gigi\",\"description\":\"the giraffe\"}"
|
||||
}
|
||||
];
|
||||
// Set the contact information (email, phone) for the user.
|
||||
optional SetContact contact = 3;
|
||||
// TODO: No SetAuthenticators?
|
||||
}
|
||||
|
||||
message GetUser{
|
||||
// Details provide some base information (such as the last change date) of the user.
|
||||
zitadel.resources.object.v3alpha.Details details = 1;
|
||||
// The schema the user and it's data is based on.
|
||||
GetSchema schema = 2;
|
||||
google.protobuf.Struct data = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"name\":\"Gigi\",\"description\":\"the giraffe\"}"
|
||||
}
|
||||
];
|
||||
// Contact information for the user. ZITADEL will use this in case of internal notifications.
|
||||
Contact contact = 4;
|
||||
// The user's authenticators. They are used to identify and authenticate the user
|
||||
// during the authentication process.
|
||||
Authenticators authenticators = 5;
|
||||
// State of the user.
|
||||
State state = 6;
|
||||
}
|
||||
|
||||
enum State {
|
||||
USER_STATE_UNSPECIFIED = 0;
|
||||
USER_STATE_ACTIVE = 1;
|
||||
USER_STATE_INACTIVE = 2;
|
||||
USER_STATE_DELETED = 3;
|
||||
USER_STATE_LOCKED = 4;
|
||||
}
|
||||
|
||||
message GetSchema {
|
||||
// The unique identifier of the user schema.
|
||||
string id = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"69629026806489455\""
|
||||
}
|
||||
];
|
||||
// The human readable name of the user schema.
|
||||
string type = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"employees\"";
|
||||
}
|
||||
];
|
||||
// The revision the user's data is based on of the revision.
|
||||
uint32 revision = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "7";
|
||||
}
|
||||
];
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,25 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.schema.v3alpha;
|
||||
package zitadel.resources.userschema.v3alpha;
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "validate/validate.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "zitadel/object/v2/object.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha";
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha;userschema";
|
||||
|
||||
message UserSchema {
|
||||
|
||||
// ID is the read-only unique identifier of the schema.
|
||||
string id = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"69629012906488334\""
|
||||
}
|
||||
];
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 2;
|
||||
zitadel.resources.object.v3alpha.Details details = 2;
|
||||
// Type is a human readable text describing the schema.
|
||||
string type = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
@ -60,55 +53,56 @@ enum FieldName {
|
||||
FIELD_NAME_STATE = 2;
|
||||
FIELD_NAME_REVISION = 3;
|
||||
FIELD_NAME_CHANGE_DATE = 4;
|
||||
FIELD_NAME_CREATION_DATE = 5;
|
||||
}
|
||||
|
||||
message SearchQuery {
|
||||
oneof query {
|
||||
message SearchFilter {
|
||||
oneof Filter {
|
||||
option (validate.required) = true;
|
||||
|
||||
// Union the results of each sub query ('OR').
|
||||
OrQuery or_query = 1;
|
||||
// Union the results of each sub filter ('OR').
|
||||
OrFilter or_filter = 1;
|
||||
// Limit the result to match all sub queries ('AND').
|
||||
// Note that if you specify multiple queries, they will be implicitly used as andQueries.
|
||||
// Use the andQuery in combination with orQuery and notQuery.
|
||||
AndQuery and_query = 2;
|
||||
// Exclude / Negate the result of the sub query ('NOT').
|
||||
NotQuery not_query = 3;
|
||||
// Use the andFilter in combination with orFilter and notFilter.
|
||||
AndFilter and_filter = 2;
|
||||
// Exclude / Negate the result of the sub filter ('NOT').
|
||||
NotFilter not_filter = 3;
|
||||
|
||||
// Limit the result to a specific schema type.
|
||||
TypeQuery type_query = 5;
|
||||
TypeFilter type_filter = 5;
|
||||
// Limit the result to a specific state of the schema.
|
||||
StateQuery state_query = 6;
|
||||
StateFilter state_filter = 6;
|
||||
// Limit the result to a specific schema ID.
|
||||
IDQuery id_query = 7;
|
||||
IDFilter id_filter = 7;
|
||||
}
|
||||
}
|
||||
|
||||
message OrQuery {
|
||||
repeated SearchQuery queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"idQuery\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}},{\"idQuery\": {\"id\": \"163840776835943483\",\"method\": \"TEXT_QUERY_METHOD_EQUALS\"}}]"
|
||||
message OrFilter {
|
||||
repeated SearchFilter queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"idFilter\": {\"id\": \"163840776835432705\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}},{\"idFilter\": {\"id\": \"163840776835943483\",\"method\": \"TEXT_FILTER_METHOD_EQUALS\"}}]"
|
||||
}
|
||||
];
|
||||
}
|
||||
message AndQuery {
|
||||
repeated SearchQuery queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"typeQuery\": {\"id\": \"employees\",\"method\": \"TEXT_QUERY_METHOD_STARTS_WITH\"}},{\"stateQuery\": {\"state\": \"STATE_ACTIVE\"}}]"
|
||||
message AndFilter {
|
||||
repeated SearchFilter queries = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "[{\"typeFilter\": {\"id\": \"employees\",\"method\": \"TEXT_FILTER_METHOD_STARTS_WITH\"}},{\"stateFilter\": {\"state\": \"STATE_ACTIVE\"}}]"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message NotQuery {
|
||||
SearchQuery query = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"stateQuery\": {\"state\": \"STATE_ACTIVE\"}}"
|
||||
message NotFilter {
|
||||
SearchFilter filter = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "{\"stateFilter\": {\"state\": \"STATE_ACTIVE\"}}"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message IDQuery {
|
||||
// Defines the ID of the user schema to query for.
|
||||
message IDFilter {
|
||||
// Defines the ID of the user schema to filter for.
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
@ -118,14 +112,14 @@ message IDQuery {
|
||||
example: "\"163840776835432705\"";
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the id query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
// Defines which text comparison method used for the id filter.
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
message TypeQuery {
|
||||
// Defines which type to query for.
|
||||
message TypeFilter {
|
||||
// Defines which type to filter for.
|
||||
string type = 1 [
|
||||
(validate.rules).string = {max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
@ -134,14 +128,14 @@ message TypeQuery {
|
||||
example: "\"employees\"";
|
||||
}
|
||||
];
|
||||
// Defines which text comparison method used for the type query.
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
// Defines which text comparison method used for the type filter.
|
||||
zitadel.resources.object.v3alpha.TextFilterMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true
|
||||
];
|
||||
}
|
||||
|
||||
message StateQuery {
|
||||
// Defines the state to query for.
|
||||
message StateFilter {
|
||||
// Defines the state to filter for.
|
||||
State state = 1 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(google.api.field_behavior) = REQUIRED,
|
@ -1,6 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.schema.v3alpha;
|
||||
package zitadel.resources.userschema.v3alpha;
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
import "google/api/field_behavior.proto";
|
||||
@ -8,12 +8,12 @@ import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "zitadel/object/v2/object.proto";
|
||||
import "zitadel/object/v3alpha/object.proto";
|
||||
import "zitadel/resources/object/v3alpha/object.proto";
|
||||
import "zitadel/protoc_gen_zitadel/v2/options.proto";
|
||||
import "zitadel/user/schema/v3alpha/user_schema.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/schema/v3alpha";
|
||||
import "zitadel/resources/userschema/v3alpha/user_schema.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha;userschema";
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
info: {
|
||||
@ -103,12 +103,12 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
|
||||
}
|
||||
};
|
||||
|
||||
service UserSchemaService {
|
||||
service ZITADELUserSchemas {
|
||||
|
||||
// List user schemas
|
||||
// Search user schemas
|
||||
//
|
||||
// List all matching user schemas. By default, we will return all user schema of your instance. Make sure to include a limit and sorting for pagination.
|
||||
rpc ListUserSchemas (ListUserSchemasRequest) returns (ListUserSchemasResponse) {
|
||||
// Search all matching user schemas. By default, we will return all user schema of your instance. Make sure to include a limit and sorting for pagination.
|
||||
rpc SearchUserSchemas (SearchUserSchemasRequest) returns (SearchUserSchemasResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/user_schemas/search"
|
||||
body: "*"
|
||||
@ -124,13 +124,13 @@ service UserSchemaService {
|
||||
responses: {
|
||||
key: "200";
|
||||
value: {
|
||||
description: "A list of all user schema matching the query";
|
||||
description: "A list of all user schema matching the search";
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
key: "400";
|
||||
value: {
|
||||
description: "invalid list query";
|
||||
description: "invalid search";
|
||||
schema: {
|
||||
json_schema: {
|
||||
ref: "#/definitions/rpcStatus";
|
||||
@ -198,12 +198,12 @@ service UserSchemaService {
|
||||
};
|
||||
}
|
||||
|
||||
// Update a user schema
|
||||
// Patch a user schema
|
||||
//
|
||||
// Update an existing user schema to a new revision. Users based on the current revision will not be affected until they are updated.
|
||||
rpc UpdateUserSchema (UpdateUserSchemaRequest) returns (UpdateUserSchemaResponse) {
|
||||
// Patch an existing user schema to a new revision. Users based on the current revision will not be affected until they are updated.
|
||||
rpc PatchUserSchema (PatchUserSchemaRequest) returns (PatchUserSchemaResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/v3alpha/user_schemas/{id}"
|
||||
patch: "/v3alpha/user_schemas/{id}"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
@ -297,26 +297,31 @@ service UserSchemaService {
|
||||
|
||||
}
|
||||
|
||||
message ListUserSchemasRequest {
|
||||
// list limitations and ordering.
|
||||
zitadel.object.v2.ListQuery query = 1;
|
||||
// the field the result is sorted.
|
||||
zitadel.user.schema.v3alpha.FieldName sorting_column = 2 [
|
||||
message SearchUserSchemasRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"FIELD_NAME_TYPE\"";
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
// Define the criteria to query for.
|
||||
repeated zitadel.user.schema.v3alpha.SearchQuery queries = 3;
|
||||
// list limitations and ordering.
|
||||
optional zitadel.resources.object.v3alpha.SearchQuery query = 2;
|
||||
// The field the result is sorted by. The default is the creation date. Beware that if you change this, your result pagination might be inconsistent.
|
||||
optional FieldName sorting_column = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"TARGET_FIELD_NAME_CREATION_DATE\""
|
||||
}
|
||||
];
|
||||
// Define the criteria to filter for.
|
||||
repeated SearchFilter filters = 4;
|
||||
}
|
||||
|
||||
message ListUserSchemasResponse {
|
||||
message SearchUserSchemasResponse {
|
||||
// Details provides information about the returned result including total amount found.
|
||||
zitadel.object.v2.ListDetails details = 1;
|
||||
zitadel.resources.object.v3alpha.ListDetails details = 1;
|
||||
// States by which field the results are sorted.
|
||||
zitadel.user.schema.v3alpha.FieldName sorting_column = 2;
|
||||
FieldName sorting_column = 2;
|
||||
// The result contains the user schemas, which matched the queries.
|
||||
repeated zitadel.user.schema.v3alpha.UserSchema result = 3;
|
||||
repeated UserSchema result = 3;
|
||||
}
|
||||
|
||||
|
||||
@ -334,11 +339,29 @@ message GetUserSchemaByIDRequest {
|
||||
}
|
||||
|
||||
message GetUserSchemaByIDResponse {
|
||||
zitadel.user.schema.v3alpha.UserSchema schema = 1;
|
||||
zitadel.resources.userschema.v3alpha.UserSchema schema = 1;
|
||||
}
|
||||
|
||||
|
||||
message CreateUserSchemaRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
CreateUserSchema user_schema = 2 [
|
||||
(validate.rules).message = {
|
||||
required: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message CreateUserSchema{
|
||||
optional zitadel.object.v3alpha.Instance instance = 4 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
|
||||
// Type is a human readable word describing the schema.
|
||||
string type = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
@ -373,14 +396,17 @@ message CreateUserSchemaRequest {
|
||||
}
|
||||
|
||||
message CreateUserSchemaResponse {
|
||||
// ID is the read-only unique identifier of the schema.
|
||||
string id = 1;
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 2;
|
||||
zitadel.resources.object.v3alpha.Details details = 2;
|
||||
}
|
||||
|
||||
|
||||
message UpdateUserSchemaRequest {
|
||||
message PatchUserSchemaRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 5 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
// unique identifier of the schema.
|
||||
string id = 1;
|
||||
// Type is a human readable word describing the schema.
|
||||
@ -414,39 +440,75 @@ message UpdateUserSchemaRequest {
|
||||
];
|
||||
}
|
||||
|
||||
message UpdateUserSchemaResponse {
|
||||
message PatchUserSchemaResponse {
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 1;
|
||||
zitadel.resources.object.v3alpha.Details details = 1;
|
||||
}
|
||||
|
||||
message DeactivateUserSchemaRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
// unique identifier of the schema.
|
||||
string id = 1;
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message DeactivateUserSchemaResponse {
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 1;
|
||||
zitadel.resources.object.v3alpha.Details details = 1;
|
||||
}
|
||||
|
||||
message ReactivateUserSchemaRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
// unique identifier of the schema.
|
||||
string id = 1;
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message ReactivateUserSchemaResponse {
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 1;
|
||||
zitadel.resources.object.v3alpha.Details details = 1;
|
||||
}
|
||||
|
||||
message DeleteUserSchemaRequest {
|
||||
optional zitadel.object.v3alpha.Instance instance = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
default: "\"domain from HOST or :authority header\""
|
||||
}
|
||||
];
|
||||
// unique identifier of the schema.
|
||||
string id = 1;
|
||||
string id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message DeleteUserSchemaResponse {
|
||||
// Details provide some base information (such as the last change date) of the schema.
|
||||
zitadel.object.v2.Details details = 1;
|
||||
zitadel.resources.object.v3alpha.Details details = 1;
|
||||
}
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package zitadel.user.v3alpha;
|
||||
|
||||
import "google/api/field_behavior.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "protoc-gen-openapiv2/options/annotations.proto";
|
||||
import "validate/validate.proto";
|
||||
import "zitadel/object/v2/object.proto";
|
||||
import "zitadel/user/v3alpha/authenticator.proto";
|
||||
import "zitadel/user/v3alpha/communication.proto";
|
||||
|
||||
option go_package = "github.com/zitadel/zitadel/pkg/grpc/user/v3alpha";
|
||||
|
||||
message User {
|
||||
|
||||
// ID is the read-only unique identifier of the user.
|
||||
string user_id = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"69629012906488334\"";
|
||||
}
|
||||
];
|
||||
// Details provide some base information (such as the last change date) of the user.
|
||||
zitadel.object.v2.Details details = 2;
|
||||
// The user's authenticators. They are used to identify and authenticate the user
|
||||
// during the authentication process.
|
||||
Authenticators authenticators = 3;
|
||||
// Contact information for the user. ZITADEL will use this in case of internal notifications.
|
||||
Contact contact = 4;
|
||||
// State of the user.
|
||||
State state = 5;
|
||||
// The schema the user and it's data is based on.
|
||||
Schema schema = 6;
|
||||
// The user's data based on the provided schema.
|
||||
google.protobuf.Struct data = 7;
|
||||
}
|
||||
|
||||
enum State {
|
||||
USER_STATE_UNSPECIFIED = 0;
|
||||
USER_STATE_ACTIVE = 1;
|
||||
USER_STATE_INACTIVE = 2;
|
||||
USER_STATE_DELETED = 3;
|
||||
USER_STATE_LOCKED = 4;
|
||||
}
|
||||
|
||||
message Schema {
|
||||
// The unique identifier of the user schema.
|
||||
string id = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"69629026806489455\""
|
||||
}
|
||||
];
|
||||
// The human readable name of the user schema.
|
||||
string type = 2 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "\"employees\"";
|
||||
}
|
||||
];
|
||||
// The revision the user's data is based on of the revision.
|
||||
uint32 revision = 3 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
example: "7";
|
||||
}
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue
Block a user