feat: user v3 api update (#8582)

# Which Problems Are Solved

Users are not yet able to update their information an status in user API
v3.

# How the Problems Are Solved

Add endpoints and functionality to update users and their status in user
API v3.

# Additional Changes

Aggregate_type and event_types are updated with "userschema" to avoid
conflicts with old events.

# Additional Context

closes #7898
This commit is contained in:
Stefan Benz 2024-09-17 10:27:48 +02:00 committed by GitHub
parent c297a62c4f
commit 5fdad7b8f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 4265 additions and 412 deletions

View File

@ -250,16 +250,26 @@ func TestServer_ExecutionTarget(t *testing.T) {
require.NoError(t, err)
defer close()
}
got, err := instance.Client.ActionV3Alpha.GetTarget(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
retryDuration := 5 * time.Second
if ctxDeadline, ok := isolatedIAMOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.NoError(t, err)
integration.AssertResourceDetails(t, tt.want.GetTarget().GetDetails(), got.GetTarget().GetDetails())
assert.Equal(t, tt.want.GetTarget().GetConfig(), got.GetTarget().GetConfig())
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, err := instance.Client.ActionV3Alpha.GetTarget(tt.ctx, tt.req)
if tt.wantErr {
assert.Error(ttt, err, "Error: "+err.Error())
} else {
assert.NoError(ttt, err)
}
if err != nil {
return
}
integration.AssertResourceDetails(t, tt.want.GetTarget().GetDetails(), got.GetTarget().GetDetails())
assert.Equal(t, tt.want.GetTarget().GetConfig(), got.GetTarget().GetConfig())
}, retryDuration, time.Millisecond*100, "timeout waiting for expected execution result")
if tt.clean != nil {
tt.clean(tt.ctx)
}

View File

@ -75,3 +75,18 @@ func SearchQueryPbToQuery(defaults systemdefaults.SystemDefaults, query *resourc
}
return offset, limit, asc, nil
}
func ResourceOwnerFromOrganization(organization *object.Organization) string {
if organization == nil {
return ""
}
if organization.GetOrgId() != "" {
return organization.GetOrgId()
}
if organization.GetOrgDomain() != "" {
// TODO get org from domain
return ""
}
return ""
}

View File

@ -14,50 +14,41 @@ import (
"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
Instance *integration.Instance
Client user.ZITADELUsersClient
CTX context.Context
)
func TestMain(m *testing.M) {
os.Exit(func() int {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
Instance = integration.NewInstance(ctx)
IAMOwnerCTX = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner)
SystemCTX = integration.WithSystemAuthorization(ctx)
UserCTX = Instance.WithAuthorization(ctx, integration.UserTypeLogin)
Client = Instance.Client.UserV3Alpha
CTX = ctx
return m.Run()
}())
}
func ensureFeatureEnabled(t *testing.T, iamOwnerCTX context.Context) {
f, err := Instance.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
func ensureFeatureEnabled(t *testing.T, instance *integration.Instance) {
ctx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(t, err)
if f.UserSchema.GetEnabled() {
return
}
_, err = Instance.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
_, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
UserSchema: gu.Ptr(true),
})
require.NoError(t, err)
retryDuration := time.Minute
if ctxDeadline, ok := iamOwnerCTX.Deadline(); ok {
if ctxDeadline, ok := ctx.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t,
func(ttt *assert.CollectT) {
f, err := Instance.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(ttt, err)

View File

@ -8,6 +8,7 @@ import (
"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"
"github.com/zitadel/zitadel/internal/zerrors"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
"github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
@ -26,7 +27,7 @@ func (s *Server) CreateUser(ctx context.Context, req *user.CreateUserRequest) (_
return nil, err
}
return &user.CreateUserResponse{
Details: resource_object.DomainToDetailsPb(schemauser.Details, object.OwnerType_OWNER_TYPE_ORG, schemauser.ResourceOwner),
Details: resource_object.DomainToDetailsPb(schemauser.Details, object.OwnerType_OWNER_TYPE_ORG, schemauser.Details.ResourceOwner),
EmailCode: gu.Ptr(schemauser.ReturnCodeEmail),
PhoneCode: gu.Ptr(schemauser.ReturnCodePhone),
}, nil
@ -37,19 +38,35 @@ func createUserRequestToCreateSchemaUser(ctx context.Context, req *user.CreateUs
if err != nil {
return nil, err
}
return &command.CreateSchemaUser{
ResourceOwner: authz.GetCtxData(ctx).OrgID,
ResourceOwner: organizationToCreateResourceOwner(ctx, req.Organization),
SchemaID: req.GetUser().GetSchemaId(),
ID: req.GetUser().GetUserId(),
Data: data,
}, nil
}
func organizationToCreateResourceOwner(ctx context.Context, org *object.Organization) string {
resourceOwner := authz.GetCtxData(ctx).OrgID
if resourceOwnerReq := resource_object.ResourceOwnerFromOrganization(org); resourceOwnerReq != "" {
return resourceOwnerReq
}
return resourceOwner
}
func organizationToUpdateResourceOwner(org *object.Organization) string {
if resourceOwnerReq := resource_object.ResourceOwnerFromOrganization(org); resourceOwnerReq != "" {
return resourceOwnerReq
}
return ""
}
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())
details, err := s.command.DeleteSchemaUser(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
if err != nil {
return nil, err
}
@ -64,3 +81,126 @@ func checkUserSchemaEnabled(ctx context.Context) error {
}
return zerrors.ThrowPreconditionFailed(nil, "TODO", "Errors.UserSchema.NotEnabled")
}
func (s *Server) PatchUser(ctx context.Context, req *user.PatchUserRequest) (_ *user.PatchUserResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
schemauser, err := patchUserRequestToChangeSchemaUser(req)
if err != nil {
return nil, err
}
if err := s.command.ChangeSchemaUser(ctx, schemauser, s.userCodeAlg); err != nil {
return nil, err
}
return &user.PatchUserResponse{
Details: resource_object.DomainToDetailsPb(schemauser.Details, object.OwnerType_OWNER_TYPE_ORG, schemauser.Details.ResourceOwner),
EmailCode: gu.Ptr(schemauser.ReturnCodeEmail),
PhoneCode: gu.Ptr(schemauser.ReturnCodePhone),
}, nil
}
func patchUserRequestToChangeSchemaUser(req *user.PatchUserRequest) (_ *command.ChangeSchemaUser, err error) {
var data []byte
if req.GetUser().Data != nil {
data, err = req.GetUser().GetData().MarshalJSON()
if err != nil {
return nil, err
}
}
var email *command.Email
var phone *command.Phone
if req.GetUser().GetContact() != nil {
if req.GetUser().GetContact().GetEmail() != nil {
email = &command.Email{
Address: domain.EmailAddress(req.GetUser().GetContact().Email.Address),
}
if req.GetUser().GetContact().Email.GetIsVerified() {
email.Verified = true
}
if req.GetUser().GetContact().Email.GetReturnCode() != nil {
email.ReturnCode = true
}
if req.GetUser().GetContact().Email.GetSendCode() != nil {
email.URLTemplate = req.GetUser().GetContact().Email.GetSendCode().GetUrlTemplate()
}
}
if req.GetUser().GetContact().Phone != nil {
phone = &command.Phone{
Number: domain.PhoneNumber(req.GetUser().GetContact().Phone.Number),
}
if req.GetUser().GetContact().Phone.GetIsVerified() {
phone.Verified = true
}
if req.GetUser().GetContact().Phone.GetReturnCode() != nil {
phone.ReturnCode = true
}
}
}
return &command.ChangeSchemaUser{
ResourceOwner: organizationToUpdateResourceOwner(req.Organization),
ID: req.GetId(),
SchemaID: req.GetUser().SchemaId,
Data: data,
Email: email,
Phone: phone,
}, nil
}
func (s *Server) DeactivateUser(ctx context.Context, req *user.DeactivateUserRequest) (_ *user.DeactivateUserResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.DeactivateSchemaUser(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
if err != nil {
return nil, err
}
return &user.DeactivateUserResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
}, nil
}
func (s *Server) ActivateUser(ctx context.Context, req *user.ActivateUserRequest) (_ *user.ActivateUserResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.ActivateSchemaUser(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
if err != nil {
return nil, err
}
return &user.ActivateUserResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
}, nil
}
func (s *Server) LockUser(ctx context.Context, req *user.LockUserRequest) (_ *user.LockUserResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.LockSchemaUser(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
if err != nil {
return nil, err
}
return &user.LockUserResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
}, nil
}
func (s *Server) UnlockUser(ctx context.Context, req *user.UnlockUserRequest) (_ *user.UnlockUserResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.UnlockSchemaUser(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
if err != nil {
return nil, err
}
return &user.UnlockUserResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
}, nil
}

View File

@ -20,7 +20,10 @@ import (
)
func TestServer_ListUserSchemas(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
userSchema := new(structpb.Struct)
err := userSchema.UnmarshalJSON([]byte(`{
@ -43,7 +46,7 @@ func TestServer_ListUserSchemas(t *testing.T) {
{
name: "missing permission",
args: args{
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &schema.SearchUserSchemasRequest{},
},
wantErr: true,
@ -51,7 +54,7 @@ func TestServer_ListUserSchemas(t *testing.T) {
{
name: "not found, error",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.SearchUserSchemasRequest{
Filters: []*schema.SearchFilter{
{
@ -75,11 +78,11 @@ func TestServer_ListUserSchemas(t *testing.T) {
{
name: "single (id), ok",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.SearchUserSchemasRequest{},
prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error {
schemaType := gofakeit.Name()
createResp := Instance.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
createResp := instance.CreateUserSchemaEmptyWithType(isolatedIAMOwnerCTX, schemaType)
request.Filters = []*schema.SearchFilter{
{
Filter: &schema.SearchFilter_IdFilter{
@ -121,14 +124,14 @@ func TestServer_ListUserSchemas(t *testing.T) {
{
name: "multiple (type), ok",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.SearchUserSchemasRequest{},
prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error {
schemaType := gofakeit.Name()
schemaType1 := schemaType + "_1"
schemaType2 := schemaType + "_2"
createResp := Instance.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType1)
createResp2 := Instance.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType2)
createResp := instance.CreateUserSchemaEmptyWithType(isolatedIAMOwnerCTX, schemaType1)
createResp2 := instance.CreateUserSchemaEmptyWithType(isolatedIAMOwnerCTX, schemaType2)
request.SortingColumn = gu.Ptr(schema.FieldName_FIELD_NAME_TYPE)
request.Query = &object.SearchQuery{Desc: false}
@ -186,12 +189,12 @@ func TestServer_ListUserSchemas(t *testing.T) {
}
retryDuration := 20 * time.Second
if ctxDeadline, ok := IAMOwnerCTX.Deadline(); ok {
if ctxDeadline, ok := isolatedIAMOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, err := Client.SearchUserSchemas(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.SearchUserSchemas(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(ttt, err)
return
@ -215,7 +218,10 @@ func TestServer_ListUserSchemas(t *testing.T) {
}
func TestServer_GetUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
userSchema := new(structpb.Struct)
err := userSchema.UnmarshalJSON([]byte(`{
@ -238,11 +244,11 @@ func TestServer_GetUserSchema(t *testing.T) {
{
name: "missing permission",
args: args{
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &schema.GetUserSchemaRequest{},
prepare: func(request *schema.GetUserSchemaRequest, resp *schema.GetUserSchemaResponse) error {
schemaType := gofakeit.Name()
createResp := Instance.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
createResp := instance.CreateUserSchemaEmptyWithType(isolatedIAMOwnerCTX, schemaType)
request.Id = createResp.GetDetails().GetId()
return nil
},
@ -252,7 +258,7 @@ func TestServer_GetUserSchema(t *testing.T) {
{
name: "not existing, error",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.GetUserSchemaRequest{
Id: "notexisting",
},
@ -262,11 +268,11 @@ func TestServer_GetUserSchema(t *testing.T) {
{
name: "get, ok",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.GetUserSchemaRequest{},
prepare: func(request *schema.GetUserSchemaRequest, resp *schema.GetUserSchemaResponse) error {
schemaType := gofakeit.Name()
createResp := Instance.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType)
createResp := instance.CreateUserSchemaEmptyWithType(isolatedIAMOwnerCTX, schemaType)
request.Id = createResp.GetDetails().GetId()
resp.UserSchema.Config.Type = schemaType
@ -295,12 +301,12 @@ func TestServer_GetUserSchema(t *testing.T) {
}
retryDuration := 5 * time.Second
if ctxDeadline, ok := IAMOwnerCTX.Deadline(); ok {
if ctxDeadline, ok := isolatedIAMOwnerCTX.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, err := Client.GetUserSchema(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.GetUserSchema(tt.args.ctx, tt.args.req)
if tt.wantErr {
assert.Error(t, err, "Error: "+err.Error())
} else {

View File

@ -14,49 +14,41 @@ import (
"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
Instance *integration.Instance
Client schema.ZITADELUserSchemasClient
CTX context.Context
)
func TestMain(m *testing.M) {
os.Exit(func() int {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
Instance = integration.NewInstance(ctx)
IAMOwnerCTX = Instance.WithAuthorization(ctx, integration.UserTypeIAMOwner)
SystemCTX = integration.WithSystemAuthorization(ctx)
Client = Instance.Client.UserSchemaV3
CTX = ctx
return m.Run()
}())
}
func ensureFeatureEnabled(t *testing.T, iamOwnerCTX context.Context) {
f, err := Instance.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
func ensureFeatureEnabled(t *testing.T, instance *integration.Instance) {
ctx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(t, err)
if f.UserSchema.GetEnabled() {
return
}
_, err = Instance.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
_, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
UserSchema: gu.Ptr(true),
})
require.NoError(t, err)
retryDuration := time.Minute
if ctxDeadline, ok := iamOwnerCTX.Deadline(); ok {
if ctxDeadline, ok := ctx.Deadline(); ok {
retryDuration = time.Until(ctxDeadline)
}
require.EventuallyWithT(t,
func(ttt *assert.CollectT) {
f, err := Instance.Client.FeatureV2.GetInstanceFeatures(iamOwnerCTX, &feature.GetInstanceFeaturesRequest{
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(ttt, err)

View File

@ -19,7 +19,10 @@ import (
)
func TestServer_CreateUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
tests := []struct {
name string
@ -30,7 +33,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
}{
{
name: "missing permission, error",
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -40,7 +43,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "empty type",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: "",
@ -50,7 +53,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "empty schema, error",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -60,7 +63,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "invalid schema, error",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -91,7 +94,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "no authenticators, ok",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -123,14 +126,14 @@ func TestServer_CreateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
},
{
name: "invalid authenticator, error",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -164,7 +167,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "with authenticator, ok",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -199,14 +202,14 @@ func TestServer_CreateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
},
{
name: "with invalid permission, error",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -241,7 +244,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
},
{
name: "with valid permission, ok",
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.CreateUserSchemaRequest{
UserSchema: &schema.UserSchema{
Type: gofakeit.Name(),
@ -280,7 +283,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -288,7 +291,7 @@ func TestServer_CreateUserSchema(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Client.CreateUserSchema(tt.ctx, tt.req)
got, err := instance.Client.UserSchemaV3.CreateUserSchema(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
@ -301,7 +304,10 @@ func TestServer_CreateUserSchema(t *testing.T) {
}
func TestServer_UpdateUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@ -317,12 +323,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "missing permission, error",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
Type: gu.Ptr(gofakeit.Name()),
@ -337,7 +343,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{},
},
wantErr: true,
@ -349,7 +355,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{},
},
wantErr: true,
@ -357,12 +363,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "empty type, error",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
Type: gu.Ptr(""),
@ -374,12 +380,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "update type, ok",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
Type: gu.Ptr(gofakeit.Name()),
@ -391,7 +397,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -399,12 +405,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "empty schema, ok",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
DataType: &schema.PatchUserSchema_Schema{},
@ -416,7 +422,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -424,12 +430,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "invalid schema, error",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
DataType: &schema.PatchUserSchema_Schema{
@ -462,12 +468,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "update schema, ok",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
DataType: &schema.PatchUserSchema_Schema{
@ -500,7 +506,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -508,12 +514,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "invalid authenticator, error",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
PossibleAuthenticators: []schema.AuthenticatorType{
@ -527,12 +533,12 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "update authenticator, ok",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
PossibleAuthenticators: []schema.AuthenticatorType{
@ -546,7 +552,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -554,8 +560,8 @@ func TestServer_UpdateUserSchema(t *testing.T) {
{
name: "inactive, error",
prepare: func(request *schema.PatchUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
_, err := instance.Client.UserSchemaV3.DeactivateUserSchema(isolatedIAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
Id: schemaID,
})
require.NoError(t, err)
@ -563,7 +569,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
return nil
},
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.PatchUserSchemaRequest{
UserSchema: &schema.PatchUserSchema{
Type: gu.Ptr(gofakeit.Name()),
@ -578,7 +584,7 @@ func TestServer_UpdateUserSchema(t *testing.T) {
err := tt.prepare(tt.args.req)
require.NoError(t, err)
got, err := Client.PatchUserSchema(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.PatchUserSchema(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(t, err)
return
@ -590,7 +596,10 @@ func TestServer_UpdateUserSchema(t *testing.T) {
}
func TestServer_DeactivateUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@ -606,7 +615,7 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
{
name: "not existing, error",
args: args{
IAMOwnerCTX,
isolatedIAMOwnerCTX,
&schema.DeactivateUserSchemaRequest{
Id: "notexisting",
},
@ -617,10 +626,10 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
{
name: "active, ok",
args: args{
IAMOwnerCTX,
isolatedIAMOwnerCTX,
&schema.DeactivateUserSchemaRequest{},
func(request *schema.DeactivateUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
@ -630,7 +639,7 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -638,12 +647,12 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
{
name: "inactive, error",
args: args{
IAMOwnerCTX,
isolatedIAMOwnerCTX,
&schema.DeactivateUserSchemaRequest{},
func(request *schema.DeactivateUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
_, err := instance.Client.UserSchemaV3.DeactivateUserSchema(isolatedIAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
Id: schemaID,
})
return err
@ -657,7 +666,7 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
err := tt.args.prepare(tt.args.req)
require.NoError(t, err)
got, err := Client.DeactivateUserSchema(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.DeactivateUserSchema(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(t, err)
return
@ -669,7 +678,10 @@ func TestServer_DeactivateUserSchema(t *testing.T) {
}
func TestServer_ReactivateUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@ -685,7 +697,7 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
{
name: "not existing, error",
args: args{
IAMOwnerCTX,
isolatedIAMOwnerCTX,
&schema.ReactivateUserSchemaRequest{
Id: "notexisting",
},
@ -696,10 +708,10 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
{
name: "active, error",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.ReactivateUserSchemaRequest{},
prepare: func(request *schema.ReactivateUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
@ -709,12 +721,12 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
{
name: "inactive, ok",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.ReactivateUserSchemaRequest{},
prepare: func(request *schema.ReactivateUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
_, err := Client.DeactivateUserSchema(IAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
_, err := instance.Client.UserSchemaV3.DeactivateUserSchema(isolatedIAMOwnerCTX, &schema.DeactivateUserSchemaRequest{
Id: schemaID,
})
return err
@ -725,7 +737,7 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -736,7 +748,7 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
err := tt.args.prepare(tt.args.req)
require.NoError(t, err)
got, err := Client.ReactivateUserSchema(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.ReactivateUserSchema(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(t, err)
return
@ -748,7 +760,10 @@ func TestServer_ReactivateUserSchema(t *testing.T) {
}
func TestServer_DeleteUserSchema(t *testing.T) {
ensureFeatureEnabled(t, IAMOwnerCTX)
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@ -764,7 +779,7 @@ func TestServer_DeleteUserSchema(t *testing.T) {
{
name: "not existing, error",
args: args{
IAMOwnerCTX,
isolatedIAMOwnerCTX,
&schema.DeleteUserSchemaRequest{
Id: "notexisting",
},
@ -775,10 +790,10 @@ func TestServer_DeleteUserSchema(t *testing.T) {
{
name: "delete, ok",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.DeleteUserSchemaRequest{},
prepare: func(request *schema.DeleteUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
return nil
},
@ -788,7 +803,7 @@ func TestServer_DeleteUserSchema(t *testing.T) {
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_INSTANCE,
Id: Instance.ID(),
Id: instance.ID(),
},
},
},
@ -796,12 +811,12 @@ func TestServer_DeleteUserSchema(t *testing.T) {
{
name: "deleted, error",
args: args{
ctx: IAMOwnerCTX,
ctx: isolatedIAMOwnerCTX,
req: &schema.DeleteUserSchemaRequest{},
prepare: func(request *schema.DeleteUserSchemaRequest) error {
schemaID := Instance.CreateUserSchemaEmpty(IAMOwnerCTX).GetDetails().GetId()
schemaID := instance.CreateUserSchemaEmpty(isolatedIAMOwnerCTX).GetDetails().GetId()
request.Id = schemaID
_, err := Client.DeleteUserSchema(IAMOwnerCTX, &schema.DeleteUserSchemaRequest{
_, err := instance.Client.UserSchemaV3.DeleteUserSchema(isolatedIAMOwnerCTX, &schema.DeleteUserSchemaRequest{
Id: schemaID,
})
return err
@ -815,7 +830,7 @@ func TestServer_DeleteUserSchema(t *testing.T) {
err := tt.args.prepare(tt.args.req)
require.NoError(t, err)
got, err := Client.DeleteUserSchema(tt.args.ctx, tt.args.req)
got, err := instance.Client.UserSchemaV3.DeleteUserSchema(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(t, err)
return

View File

@ -186,7 +186,7 @@ func validateUserSchema(userSchema json.RawMessage) error {
}
func (c *Commands) getSchemaWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserSchemaWriteModel, error) {
writeModel := NewUserSchemaWriteModel(resourceOwner, id, "")
writeModel := NewUserSchemaWriteModel(resourceOwner, id)
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
return nil, err
}

View File

@ -19,16 +19,15 @@ type UserSchemaWriteModel struct {
Schema json.RawMessage
PossibleAuthenticators []domain.AuthenticatorType
State domain.UserSchemaState
Revision uint64
SchemaRevision uint64
}
func NewUserSchemaWriteModel(resourceOwner, schemaID, ty string) *UserSchemaWriteModel {
func NewUserSchemaWriteModel(resourceOwner, schemaID string) *UserSchemaWriteModel {
return &UserSchemaWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: schemaID,
ResourceOwner: resourceOwner,
},
SchemaType: ty,
}
}
@ -40,13 +39,13 @@ func (wm *UserSchemaWriteModel) Reduce() error {
wm.Schema = e.Schema
wm.PossibleAuthenticators = e.PossibleAuthenticators
wm.State = domain.UserSchemaStateActive
wm.Revision = 1
wm.SchemaRevision = 1
case *schema.UpdatedEvent:
if e.SchemaType != nil {
wm.SchemaType = *e.SchemaType
}
if e.SchemaRevision != nil {
wm.Revision = *e.SchemaRevision
wm.SchemaRevision = *e.SchemaRevision
}
if len(e.Schema) > 0 {
wm.Schema = e.Schema
@ -79,10 +78,6 @@ func (wm *UserSchemaWriteModel) Query() *eventstore.SearchQueryBuilder {
schema.DeletedType,
)
if wm.SchemaType != "" {
query = query.EventData(map[string]interface{}{"schemaType": wm.SchemaType})
}
return query.Builder()
}
func (wm *UserSchemaWriteModel) NewUpdatedEvent(
@ -99,7 +94,7 @@ 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))
changes = append(changes, schema.IncreaseRevision(wm.SchemaRevision))
}
if len(possibleAuthenticators) > 0 && slices.Compare(wm.PossibleAuthenticators, possibleAuthenticators) != 0 {
changes = append(changes, schema.ChangePossibleAuthenticators(possibleAuthenticators))

View File

@ -15,14 +15,14 @@ import (
)
type CreateSchemaUser struct {
Details *domain.ObjectDetails
ResourceOwner string
Details *domain.ObjectDetails
SchemaID string
schemaRevision uint64
ID string
Data json.RawMessage
ResourceOwner string
ID string
Data json.RawMessage
Email *Email
ReturnCodeEmail string
@ -45,7 +45,7 @@ func (s *CreateSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
if !schemaWriteModel.Exists() {
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-N9QOuN4F7o", "Errors.UserSchema.NotExists")
}
s.schemaRevision = schemaWriteModel.Revision
s.schemaRevision = schemaWriteModel.SchemaRevision
if s.ID == "" {
s.ID, err = c.idGenerator.Next()
@ -120,13 +120,13 @@ func (c *Commands) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser,
),
}
if user.Email != nil {
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, events, userAgg, user.Email, alg)
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, writeModel, 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)
events, user.ReturnCodePhone, err = c.updateSchemaUserPhone(ctx, writeModel, events, userAgg, user.Phone, alg)
if err != nil {
return err
}
@ -139,11 +139,11 @@ func (c *Commands) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser,
return nil
}
func (c *Commands) DeleteSchemaUser(ctx context.Context, id string) (*domain.ObjectDetails, error) {
func (c *Commands) DeleteSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Vs4wJCME7T", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserExists(ctx, "", id)
writeModel, err := c.getSchemaUserExists(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
@ -161,7 +161,235 @@ func (c *Commands) DeleteSchemaUser(ctx context.Context, id string) (*domain.Obj
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) {
type ChangeSchemaUser struct {
Details *domain.ObjectDetails
SchemaID *string
schemaWriteModel *UserSchemaWriteModel
ResourceOwner string
ID string
Data json.RawMessage
Email *Email
ReturnCodeEmail string
Phone *Phone
ReturnCodePhone string
}
func (s *ChangeSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
if s.ID == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-gEJR1QOGHb", "Errors.IDMissing")
}
if s.SchemaID != nil {
s.schemaWriteModel, err = c.getSchemaWriteModelByID(ctx, "", *s.SchemaID)
if err != nil {
return err
}
if !s.schemaWriteModel.Exists() {
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-VLDTtxT3If", "Errors.UserSchema.NotExists")
}
}
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 (s *ChangeSchemaUser) ValidData(ctx context.Context, c *Commands, existingUser *UserV3WriteModel) (err error) {
// get role for permission check in schema through extension
role, err := c.getSchemaRoleForWrite(ctx, existingUser.ResourceOwner, existingUser.AggregateID)
if err != nil {
return err
}
if s.schemaWriteModel == nil {
s.schemaWriteModel, err = c.getSchemaWriteModelByID(ctx, "", existingUser.SchemaID)
if err != nil {
return err
}
}
schema, err := domain_schema.NewSchema(role, bytes.NewReader(s.schemaWriteModel.Schema))
if err != nil {
return err
}
// if data not changed but a new schema or revision should be used
data := s.Data
if s.Data == nil {
data = existingUser.Data
}
var v interface{}
if err := json.Unmarshal(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")
}
return nil
}
func (c *Commands) ChangeSchemaUser(ctx context.Context, user *ChangeSchemaUser, alg crypto.EncryptionAlgorithm) (err error) {
if err := user.Valid(ctx, c); err != nil {
return err
}
writeModel, err := c.getSchemaUserWriteModelByID(ctx, user.ResourceOwner, user.ID)
if err != nil {
return err
}
if !writeModel.Exists() {
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.NotFound")
}
userAgg := UserV3AggregateFromWriteModel(&writeModel.WriteModel)
events := make([]eventstore.Command, 0)
if user.Data != nil || user.SchemaID != nil {
if err := user.ValidData(ctx, c, writeModel); err != nil {
return err
}
updateEvent := writeModel.NewUpdatedEvent(ctx,
userAgg,
user.schemaWriteModel.AggregateID,
user.schemaWriteModel.SchemaRevision,
user.Data,
)
if updateEvent != nil {
events = append(events, updateEvent)
}
}
if user.Email != nil {
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, writeModel, events, userAgg, user.Email, alg)
if err != nil {
return err
}
}
if user.Phone != nil {
events, user.ReturnCodePhone, err = c.updateSchemaUserPhone(ctx, writeModel, events, userAgg, user.Phone, alg)
if err != nil {
return err
}
}
if len(events) == 0 {
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
return nil
}
if err := c.pushAppendAndReduce(ctx, writeModel, events...); err != nil {
return err
}
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
return nil
}
func (c *Commands) checkPermissionUpdateUserState(ctx context.Context, resourceOwner, userID string) error {
return c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, userID)
}
func (c *Commands) LockSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Eu8I2VAfjF", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserExists(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if !writeModel.Exists() || writeModel.Locked {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-G4LOrnjY7q", "Errors.User.NotFound")
}
if err := c.checkPermissionUpdateUserState(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
return nil, err
}
if err := c.pushAppendAndReduce(ctx, writeModel,
schemauser.NewLockedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
); err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.WriteModel), nil
}
func (c *Commands) UnlockSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-krXtYscQZh", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserExists(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if !writeModel.Exists() || !writeModel.Locked {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-gpBv46Lh9m", "Errors.User.NotFound")
}
if err := c.checkPermissionUpdateUserState(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
return nil, err
}
if err := c.pushAppendAndReduce(ctx, writeModel,
schemauser.NewUnlockedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
); err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.WriteModel), nil
}
func (c *Commands) DeactivateSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-pjJhge86ZV", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserExists(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if writeModel.State != domain.UserStateActive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Ob6lR5iFTe", "Errors.User.NotFound")
}
if err := c.checkPermissionUpdateUserState(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
return nil, err
}
if err := c.pushAppendAndReduce(ctx, writeModel,
schemauser.NewDeactivatedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
); err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.WriteModel), nil
}
func (c *Commands) ActivateSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-17XupGvxBJ", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserExists(ctx, "", id)
if err != nil {
return nil, err
}
if writeModel.State != domain.UserStateInactive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-rQjbBr4J3j", "Errors.User.NotFound")
}
if err := c.checkPermissionUpdateUserState(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
return nil, err
}
if err := c.pushAppendAndReduce(ctx, writeModel,
schemauser.NewActivatedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
); err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.WriteModel), nil
}
func (c *Commands) updateSchemaUserEmail(ctx context.Context, existing *UserV3WriteModel, events []eventstore.Command, agg *eventstore.Aggregate, email *Email, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
if existing.Email == string(email.Address) {
return events, plainCode, nil
}
events = append(events, schemauser.NewEmailUpdatedEvent(ctx,
agg,
@ -187,8 +415,12 @@ func (c *Commands) updateSchemaUserEmail(ctx context.Context, events []eventstor
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,
func (c *Commands) updateSchemaUserPhone(ctx context.Context, existing *UserV3WriteModel, events []eventstore.Command, agg *eventstore.Aggregate, phone *Phone, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
if existing.Phone == string(phone.Number) {
return events, plainCode, nil
}
events = append(events, schemauser.NewPhoneUpdatedEvent(ctx,
agg,
phone.Number,
))
@ -218,3 +450,11 @@ func (c *Commands) getSchemaUserExists(ctx context.Context, resourceOwner, id st
}
return writeModel, nil
}
func (c *Commands) getSchemaUserWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
writeModel := NewUserV3WriteModel(resourceOwner, id)
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -29,7 +29,8 @@ type UserV3WriteModel struct {
Data json.RawMessage
State domain.UserState
Locked bool
State domain.UserState
}
func NewExistsUserV3WriteModel(resourceOwner, userID string) *UserV3WriteModel {
@ -61,8 +62,9 @@ func (wm *UserV3WriteModel) Reduce() error {
switch e := event.(type) {
case *schemauser.CreatedEvent:
wm.SchemaID = e.SchemaID
wm.SchemaRevision = 1
wm.SchemaRevision = e.SchemaRevision
wm.Data = e.Data
wm.Locked = false
wm.State = domain.UserStateActive
case *schemauser.UpdatedEvent:
@ -79,6 +81,8 @@ func (wm *UserV3WriteModel) Reduce() error {
wm.State = domain.UserStateDeleted
case *schemauser.EmailUpdatedEvent:
wm.Email = string(e.EmailAddress)
wm.IsEmailVerified = false
wm.EmailVerifiedFailedCount = 0
case *schemauser.EmailCodeAddedEvent:
wm.IsEmailVerified = false
wm.EmailVerifiedFailedCount = 0
@ -87,8 +91,10 @@ func (wm *UserV3WriteModel) Reduce() error {
wm.EmailVerifiedFailedCount = 0
case *schemauser.EmailVerificationFailedEvent:
wm.EmailVerifiedFailedCount += 1
case *schemauser.PhoneChangedEvent:
case *schemauser.PhoneUpdatedEvent:
wm.Phone = string(e.PhoneNumber)
wm.IsPhoneVerified = false
wm.PhoneVerifiedFailedCount = 0
case *schemauser.PhoneCodeAddedEvent:
wm.IsPhoneVerified = false
wm.PhoneVerifiedFailedCount = 0
@ -97,28 +103,39 @@ func (wm *UserV3WriteModel) Reduce() error {
wm.IsPhoneVerified = true
case *schemauser.PhoneVerificationFailedEvent:
wm.PhoneVerifiedFailedCount += 1
case *schemauser.LockedEvent:
wm.Locked = true
case *schemauser.UnlockedEvent:
wm.Locked = false
case *schemauser.DeactivatedEvent:
wm.State = domain.UserStateInactive
case *schemauser.ActivatedEvent:
wm.State = domain.UserStateActive
}
}
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,
)
builder := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent)
if wm.ResourceOwner != "" {
builder = builder.ResourceOwner(wm.ResourceOwner)
}
eventtypes := []eventstore.EventType{
schemauser.CreatedType,
schemauser.DeletedType,
schemauser.ActivatedType,
schemauser.DeactivatedType,
schemauser.LockedType,
schemauser.UnlockedType,
}
if wm.DataWM {
query = query.EventTypes(
eventtypes = append(eventtypes,
schemauser.UpdatedType,
)
}
if wm.EmailWM {
query = query.EventTypes(
eventtypes = append(eventtypes,
schemauser.EmailUpdatedType,
schemauser.EmailVerifiedType,
schemauser.EmailCodeAddedType,
@ -126,31 +143,34 @@ func (wm *UserV3WriteModel) Query() *eventstore.SearchQueryBuilder {
)
}
if wm.PhoneWM {
query = query.EventTypes(
eventtypes = append(eventtypes,
schemauser.PhoneUpdatedType,
schemauser.PhoneVerifiedType,
schemauser.PhoneCodeAddedType,
schemauser.PhoneVerificationFailedType,
)
}
return query.Builder()
return builder.AddQuery().
AggregateTypes(schemauser.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(eventtypes...).Builder()
}
func (wm *UserV3WriteModel) NewUpdatedEvent(
ctx context.Context,
agg *eventstore.Aggregate,
schemaID *string,
schemaRevision *uint64,
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 wm.SchemaID != schemaID {
changes = append(changes, schemauser.ChangeSchemaID(schemaID))
}
if schemaRevision != nil && wm.SchemaRevision != *schemaRevision {
changes = append(changes, schemauser.ChangeSchemaRevision(wm.SchemaRevision, *schemaRevision))
if wm.SchemaRevision != schemaRevision {
changes = append(changes, schemauser.ChangeSchemaRevision(schemaRevision))
}
if !bytes.Equal(wm.Data, data) {
if data != nil && !bytes.Equal(wm.Data, data) {
changes = append(changes, schemauser.ChangeData(data))
}
if len(changes) == 0 {

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ import (
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"
org "github.com/zitadel/zitadel/pkg/grpc/org/v2"
"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"
@ -784,3 +784,55 @@ func (i *Instance) CreateInviteCode(ctx context.Context, userID string) *user_v2
logging.OnError(err).Fatal("create invite code")
return user
}
func (i *Instance) LockSchemaUser(ctx context.Context, orgID string, userID string) *user_v3alpha.LockUserResponse {
var org *object_v3alpha.Organization
if orgID != "" {
org = &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}}
}
user, err := i.Client.UserV3Alpha.LockUser(ctx, &user_v3alpha.LockUserRequest{
Organization: org,
Id: userID,
})
logging.OnError(err).Fatal("lock user")
return user
}
func (i *Instance) UnlockSchemaUser(ctx context.Context, orgID string, userID string) *user_v3alpha.UnlockUserResponse {
var org *object_v3alpha.Organization
if orgID != "" {
org = &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}}
}
user, err := i.Client.UserV3Alpha.UnlockUser(ctx, &user_v3alpha.UnlockUserRequest{
Organization: org,
Id: userID,
})
logging.OnError(err).Fatal("unlock user")
return user
}
func (i *Instance) DeactivateSchemaUser(ctx context.Context, orgID string, userID string) *user_v3alpha.DeactivateUserResponse {
var org *object_v3alpha.Organization
if orgID != "" {
org = &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}}
}
user, err := i.Client.UserV3Alpha.DeactivateUser(ctx, &user_v3alpha.DeactivateUserRequest{
Organization: org,
Id: userID,
})
logging.OnError(err).Fatal("deactivate user")
return user
}
func (i *Instance) ActivateSchemaUser(ctx context.Context, orgID string, userID string) *user_v3alpha.ActivateUserResponse {
var org *object_v3alpha.Organization
if orgID != "" {
org = &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}}
}
user, err := i.Client.UserV3Alpha.ActivateUser(ctx, &user_v3alpha.ActivateUserRequest{
Organization: org,
Id: userID,
})
logging.OnError(err).Fatal("reactivate user")
return user
}

View File

@ -5,8 +5,8 @@ import (
)
const (
AggregateType = "user"
AggregateVersion = "v3"
AggregateType = "schemauser"
AggregateVersion = "v1"
)
type Aggregate struct {

View File

@ -8,7 +8,6 @@ import (
"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 (
@ -21,11 +20,15 @@ const (
)
type EmailUpdatedEvent struct {
eventstore.BaseEvent `json:"-"`
*eventstore.BaseEvent `json:"-"`
EmailAddress domain.EmailAddress `json:"email,omitempty"`
}
func (e *EmailUpdatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *EmailUpdatedEvent) Payload() interface{} {
return e
}
@ -36,7 +39,7 @@ func (e *EmailUpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
func NewEmailUpdatedEvent(ctx context.Context, aggregate *eventstore.Aggregate, emailAddress domain.EmailAddress) *EmailUpdatedEvent {
return &EmailUpdatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
EmailUpdatedType,
@ -45,24 +48,16 @@ func NewEmailUpdatedEvent(ctx context.Context, aggregate *eventstore.Aggregate,
}
}
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:"-"`
*eventstore.BaseEvent `json:"-"`
IsEmailVerified bool `json:"-"`
}
func (e *EmailVerifiedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *EmailVerifiedEvent) Payload() interface{} {
return nil
}
@ -73,7 +68,7 @@ func (e *EmailVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint
func NewEmailVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailVerifiedEvent {
return &EmailVerifiedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
EmailVerifiedType,
@ -81,18 +76,13 @@ func NewEmailVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate)
}
}
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:"-"`
*eventstore.BaseEvent `json:"-"`
}
func (e *EmailVerificationFailedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *EmailVerificationFailedEvent) Payload() interface{} {
return nil
}
@ -101,9 +91,9 @@ func (e *EmailVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueC
return nil
}
func NewHumanEmailVerificationFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailVerificationFailedEvent {
func NewEmailVerificationFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailVerificationFailedEvent {
return &EmailVerificationFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
EmailVerificationFailedType,
@ -111,14 +101,8 @@ func NewHumanEmailVerificationFailedEvent(ctx context.Context, aggregate *events
}
}
func EmailVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &EmailVerificationFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
type EmailCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
*eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
@ -127,6 +111,10 @@ type EmailCodeAddedEvent struct {
TriggeredAtOrigin string `json:"triggerOrigin,omitempty"`
}
func (e *EmailCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *EmailCodeAddedEvent) Payload() interface{} {
return e
}
@ -148,7 +136,7 @@ func NewEmailCodeAddedEvent(
codeReturned bool,
) *EmailCodeAddedEvent {
return &EmailCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
EmailCodeAddedType,
@ -161,22 +149,13 @@ func NewEmailCodeAddedEvent(
}
}
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:"-"`
*eventstore.BaseEvent `json:"-"`
}
func (e *EmailCodeSentEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *EmailCodeSentEvent) Payload() interface{} {
return nil
}
@ -185,18 +164,12 @@ func (e *EmailCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint
return nil
}
func NewHumanEmailCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailCodeSentEvent {
func NewEmailCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *EmailCodeSentEvent {
return &EmailCodeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
EmailCodeSentType,
),
}
}
func EmailCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &EmailCodeSentEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}

View File

@ -6,4 +6,18 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, CreatedType, eventstore.GenericEventMapper[CreatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, UpdatedType, eventstore.GenericEventMapper[UpdatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, DeletedType, eventstore.GenericEventMapper[DeletedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, LockedType, eventstore.GenericEventMapper[LockedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, UnlockedType, eventstore.GenericEventMapper[UnlockedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, ActivatedType, eventstore.GenericEventMapper[ActivatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, DeactivatedType, eventstore.GenericEventMapper[DeactivatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, EmailUpdatedType, eventstore.GenericEventMapper[EmailUpdatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, EmailCodeAddedType, eventstore.GenericEventMapper[EmailCodeAddedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, EmailCodeSentType, eventstore.GenericEventMapper[EmailCodeSentEvent])
eventstore.RegisterFilterEventMapper(AggregateType, EmailVerifiedType, eventstore.GenericEventMapper[EmailVerifiedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, EmailVerificationFailedType, eventstore.GenericEventMapper[EmailVerificationFailedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, PhoneUpdatedType, eventstore.GenericEventMapper[PhoneUpdatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, PhoneCodeAddedType, eventstore.GenericEventMapper[PhoneCodeAddedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, PhoneCodeSentType, eventstore.GenericEventMapper[PhoneCodeSentEvent])
eventstore.RegisterFilterEventMapper(AggregateType, PhoneVerifiedType, eventstore.GenericEventMapper[PhoneVerifiedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, PhoneVerificationFailedType, eventstore.GenericEventMapper[PhoneVerificationFailedEvent])
}

View File

@ -8,7 +8,6 @@ import (
"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 (
@ -20,23 +19,27 @@ const (
PhoneCodeSentType = phoneEventPrefix + "code.sent"
)
type PhoneChangedEvent struct {
eventstore.BaseEvent `json:"-"`
type PhoneUpdatedEvent struct {
*eventstore.BaseEvent `json:"-"`
PhoneNumber domain.PhoneNumber `json:"phone,omitempty"`
}
func (e *PhoneChangedEvent) Payload() interface{} {
func (e *PhoneUpdatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *PhoneUpdatedEvent) Payload() interface{} {
return e
}
func (e *PhoneChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
func (e *PhoneUpdatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone domain.PhoneNumber) *PhoneChangedEvent {
return &PhoneChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
func NewPhoneUpdatedEvent(ctx context.Context, aggregate *eventstore.Aggregate, phone domain.PhoneNumber) *PhoneUpdatedEvent {
return &PhoneUpdatedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
PhoneUpdatedType,
@ -45,24 +48,15 @@ func NewPhoneChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate,
}
}
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:"-"`
*eventstore.BaseEvent `json:"-"`
IsPhoneVerified bool `json:"-"`
}
func (e *PhoneVerifiedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *PhoneVerifiedEvent) Payload() interface{} {
return nil
}
@ -73,7 +67,7 @@ func (e *PhoneVerifiedEvent) UniqueConstraints() []*eventstore.UniqueConstraint
func NewPhoneVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneVerifiedEvent {
return &PhoneVerifiedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
PhoneVerifiedType,
@ -81,15 +75,12 @@ func NewPhoneVerifiedEvent(ctx context.Context, aggregate *eventstore.Aggregate)
}
}
func PhoneVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &PhoneVerifiedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
IsPhoneVerified: true,
}, nil
type PhoneVerificationFailedEvent struct {
*eventstore.BaseEvent `json:"-"`
}
type PhoneVerificationFailedEvent struct {
eventstore.BaseEvent `json:"-"`
func (e *PhoneVerificationFailedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *PhoneVerificationFailedEvent) Payload() interface{} {
@ -102,7 +93,7 @@ func (e *PhoneVerificationFailedEvent) UniqueConstraints() []*eventstore.UniqueC
func NewPhoneVerificationFailedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneVerificationFailedEvent {
return &PhoneVerificationFailedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
PhoneVerificationFailedType,
@ -110,14 +101,8 @@ func NewPhoneVerificationFailedEvent(ctx context.Context, aggregate *eventstore.
}
}
func PhoneVerificationFailedEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &PhoneVerificationFailedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
type PhoneCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
*eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
@ -137,6 +122,10 @@ func (e *PhoneCodeAddedEvent) TriggerOrigin() string {
return e.TriggeredAtOrigin
}
func (e *PhoneCodeAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func NewPhoneCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
@ -145,7 +134,7 @@ func NewPhoneCodeAddedEvent(
codeReturned bool,
) *PhoneCodeAddedEvent {
return &PhoneCodeAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
PhoneCodeAddedType,
@ -157,20 +146,8 @@ func NewPhoneCodeAddedEvent(
}
}
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:"-"`
*eventstore.BaseEvent `json:"-"`
}
func (e *PhoneCodeSentEvent) Payload() interface{} {
@ -181,18 +158,16 @@ func (e *PhoneCodeSentEvent) UniqueConstraints() []*eventstore.UniqueConstraint
return nil
}
func (e *PhoneCodeSentEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func NewPhoneCodeSentEvent(ctx context.Context, aggregate *eventstore.Aggregate) *PhoneCodeSentEvent {
return &PhoneCodeSentEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
PhoneCodeSentType,
),
}
}
func PhoneCodeSentEventMapper(event eventstore.Event) (eventstore.Event, error) {
return &PhoneCodeSentEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}

View File

@ -8,10 +8,14 @@ import (
)
const (
eventPrefix = "user."
CreatedType = eventPrefix + "created"
UpdatedType = eventPrefix + "updated"
DeletedType = eventPrefix + "deleted"
eventPrefix = "schemauser."
CreatedType = eventPrefix + "created"
UpdatedType = eventPrefix + "updated"
DeletedType = eventPrefix + "deleted"
LockedType = eventPrefix + "locked"
UnlockedType = eventPrefix + "unlocked"
DeactivatedType = eventPrefix + "deactivated"
ActivatedType = eventPrefix + "activated"
)
type CreatedEvent struct {
@ -60,8 +64,6 @@ type UpdatedEvent struct {
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) {
@ -95,16 +97,14 @@ func NewUpdatedEvent(
type Changes func(event *UpdatedEvent)
func ChangeSchemaID(oldSchemaID, schemaID string) func(event *UpdatedEvent) {
func ChangeSchemaID(schemaID string) func(event *UpdatedEvent) {
return func(e *UpdatedEvent) {
e.SchemaID = &schemaID
e.oldSchemaID = oldSchemaID
}
}
func ChangeSchemaRevision(oldSchemaRevision, schemaRevision uint64) func(event *UpdatedEvent) {
func ChangeSchemaRevision(schemaRevision uint64) func(event *UpdatedEvent) {
return func(e *UpdatedEvent) {
e.SchemaRevision = &schemaRevision
e.oldRevision = oldSchemaRevision
}
}
@ -142,3 +142,119 @@ func NewDeletedEvent(
),
}
}
type LockedEvent struct {
*eventstore.BaseEvent `json:"-"`
}
func (e *LockedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *LockedEvent) Payload() interface{} {
return e
}
func (e *LockedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewLockedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *LockedEvent {
return &LockedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
LockedType,
),
}
}
type UnlockedEvent struct {
*eventstore.BaseEvent `json:"-"`
}
func (e *UnlockedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *UnlockedEvent) Payload() interface{} {
return e
}
func (e *UnlockedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewUnlockedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *UnlockedEvent {
return &UnlockedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
UnlockedType,
),
}
}
type DeactivatedEvent struct {
*eventstore.BaseEvent `json:"-"`
}
func (e *DeactivatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *DeactivatedEvent) Payload() interface{} {
return e
}
func (e *DeactivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewDeactivatedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *DeactivatedEvent {
return &DeactivatedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
DeactivatedType,
),
}
}
type ActivatedEvent struct {
*eventstore.BaseEvent `json:"-"`
}
func (e *ActivatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *ActivatedEvent) Payload() interface{} {
return e
}
func (e *ActivatedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewActivatedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *ActivatedEvent {
return &ActivatedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
ActivatedType,
),
}
}

View File

@ -150,7 +150,7 @@ service ZITADELUsers {
// Returns the user identified by the requested ID.
rpc GetUser (GetUserRequest) returns (GetUserResponse) {
option (google.api.http) = {
get: "/resources/v3alpha/users/{user_id}"
get: "/resources/v3alpha/users/{id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -208,7 +208,7 @@ service ZITADELUsers {
// Patch an existing user with data based on a user schema.
rpc PatchUser (PatchUserRequest) returns (PatchUserResponse) {
option (google.api.http) = {
patch: "/resources/v3alpha/users/{user_id}"
patch: "/resources/v3alpha/users/{id}"
body: "user"
};
@ -238,7 +238,7 @@ service ZITADELUsers {
// The endpoint returns an error if the user is already in the state 'deactivated'.
rpc DeactivateUser (DeactivateUserRequest) returns (DeactivateUserResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/_deactivate"
post: "/resources/v3alpha/users/{id}/_deactivate"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -257,15 +257,15 @@ service ZITADELUsers {
};
}
// Reactivate a user
// Activate a user
//
// Reactivate a previously deactivated user and change the state to 'active'.
// Activate a previously deactivated user and change the state to 'active'.
// The user will be able to log in again.
//
// The endpoint returns an error if the user is not in the state 'deactivated'.
rpc ReactivateUser (ReactivateUserRequest) returns (ReactivateUserResponse) {
rpc ActivateUser (ActivateUserRequest) returns (ActivateUserResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/_reactivate"
post: "/resources/v3alpha/users/{id}/_activate"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -278,7 +278,7 @@ service ZITADELUsers {
responses: {
key: "200";
value: {
description: "User successfully reactivated";
description: "User successfully activated";
};
};
};
@ -294,7 +294,7 @@ service ZITADELUsers {
// The endpoint returns an error if the user is already in the state 'locked'.
rpc LockUser (LockUserRequest) returns (LockUserResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/_lock"
post: "/resources/v3alpha/users/{id}/_lock"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -321,7 +321,7 @@ service ZITADELUsers {
// The endpoint returns an error if the user is not in the state 'locked'.
rpc UnlockUser (UnlockUserRequest) returns (UnlockUserResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/_unlock"
post: "/resources/v3alpha/users/{id}/_unlock"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -346,7 +346,7 @@ service ZITADELUsers {
// The user will be able to log in anymore.
rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}"
delete: "/resources/v3alpha/users/{id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -372,7 +372,7 @@ service ZITADELUsers {
// which can be either returned or will be sent to the user by email.
rpc SetContactEmail (SetContactEmailRequest) returns (SetContactEmailResponse) {
option (google.api.http) = {
put: "/resources/v3alpha/users/{user_id}/email"
put: "/resources/v3alpha/users/{id}/email"
body: "email"
};
@ -397,7 +397,7 @@ service ZITADELUsers {
// Verify the contact email with the provided code.
rpc VerifyContactEmail (VerifyContactEmailRequest) returns (VerifyContactEmailResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/email/_verify"
post: "/resources/v3alpha/users/{id}/email/_verify"
body: "verification_code"
};
@ -422,7 +422,7 @@ service ZITADELUsers {
// Resend the email with the verification code for the contact email address.
rpc ResendContactEmailCode (ResendContactEmailCodeRequest) returns (ResendContactEmailCodeResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/email/_resend"
post: "/resources/v3alpha/users/{id}/email/_resend"
body: "*"
};
@ -449,7 +449,7 @@ service ZITADELUsers {
// which can be either returned or will be sent to the user by SMS.
rpc SetContactPhone (SetContactPhoneRequest) returns (SetContactPhoneResponse) {
option (google.api.http) = {
put: "/resources/v3alpha/users/{user_id}/phone"
put: "/resources/v3alpha/users/{id}/phone"
body: "phone"
};
@ -474,7 +474,7 @@ service ZITADELUsers {
// Verify the contact phone with the provided code.
rpc VerifyContactPhone (VerifyContactPhoneRequest) returns (VerifyContactPhoneResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/phone/_verify"
post: "/resources/v3alpha/users/{id}/phone/_verify"
body: "verification_code"
};
@ -499,7 +499,7 @@ service ZITADELUsers {
// Resend the phone with the verification code for the contact phone number.
rpc ResendContactPhoneCode (ResendContactPhoneCodeRequest) returns (ResendContactPhoneCodeResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/phone/_resend"
post: "/resources/v3alpha/users/{id}/phone/_resend"
body: "*"
};
@ -524,7 +524,7 @@ service ZITADELUsers {
// Add a new unique username to a user. The username will be used to identify the user on authentication.
rpc AddUsername (AddUsernameRequest) returns (AddUsernameResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/username"
post: "/resources/v3alpha/users/{id}/username"
body: "username"
};
@ -549,7 +549,7 @@ service ZITADELUsers {
// Remove an existing username of a user, so it cannot be used for authentication anymore.
rpc RemoveUsername (RemoveUsernameRequest) returns (RemoveUsernameResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/username/{username_id}"
delete: "/resources/v3alpha/users/{id}/username/{username_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -573,7 +573,7 @@ service ZITADELUsers {
// Add, update or reset a user's password with either a verification code or the current password.
rpc SetPassword (SetPasswordRequest) returns (SetPasswordResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/password"
post: "/resources/v3alpha/users/{id}/password"
body: "new_password"
};
@ -598,7 +598,7 @@ service ZITADELUsers {
// Request a code to be able to set a new password.
rpc RequestPasswordReset (RequestPasswordResetRequest) returns (RequestPasswordResetResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/password/_reset"
post: "/resources/v3alpha/users/{id}/password/_reset"
body: "*"
};
@ -625,7 +625,7 @@ service ZITADELUsers {
// which are used to verify the device.
rpc StartWebAuthNRegistration (StartWebAuthNRegistrationRequest) returns (StartWebAuthNRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/webauthn"
post: "/resources/v3alpha/users/{id}/webauthn"
body: "registration"
};
@ -650,7 +650,7 @@ service ZITADELUsers {
// Verify the WebAuthN registration started by StartWebAuthNRegistration with the public key credential.
rpc VerifyWebAuthNRegistration (VerifyWebAuthNRegistrationRequest) returns (VerifyWebAuthNRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/webauthn/{web_auth_n_id}/_verify"
post: "/resources/v3alpha/users/{id}/webauthn/{web_auth_n_id}/_verify"
body: "verify"
};
@ -675,7 +675,7 @@ service ZITADELUsers {
// The code will allow the user to start a new WebAuthN registration.
rpc CreateWebAuthNRegistrationLink (CreateWebAuthNRegistrationLinkRequest) returns (CreateWebAuthNRegistrationLinkResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/webauthn/registration_link"
post: "/resources/v3alpha/users/{id}/webauthn/registration_link"
body: "*"
};
@ -699,7 +699,7 @@ service ZITADELUsers {
// Remove an existing WebAuthN authenticator from a user, so it cannot be used for authentication anymore.
rpc RemoveWebAuthNAuthenticator (RemoveWebAuthNAuthenticatorRequest) returns (RemoveWebAuthNAuthenticatorResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/webauthn/{web_auth_n_id}"
delete: "/resources/v3alpha/users/{id}/webauthn/{web_auth_n_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -723,7 +723,7 @@ service ZITADELUsers {
// As a response a secret is returned, which is used to initialize a TOTP app or device.
rpc StartTOTPRegistration (StartTOTPRegistrationRequest) returns (StartTOTPRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/totp"
post: "/resources/v3alpha/users/{id}/totp"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -746,7 +746,7 @@ service ZITADELUsers {
// Verify the time-based one-time-password (TOTP) registration with the generated code.
rpc VerifyTOTPRegistration (VerifyTOTPRegistrationRequest) returns (VerifyTOTPRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/totp/{totp_id}/_verify"
post: "/resources/v3alpha/users/{id}/totp/{totp_id}/_verify"
body: "code"
};
@ -770,7 +770,7 @@ service ZITADELUsers {
// Remove an existing time-based one-time-password (TOTP) authenticator from a user, so it cannot be used for authentication anymore.
rpc RemoveTOTPAuthenticator (RemoveTOTPAuthenticatorRequest) returns (RemoveTOTPAuthenticatorResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/totp/{totp_id}"
delete: "/resources/v3alpha/users/{id}/totp/{totp_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -795,7 +795,7 @@ service ZITADELUsers {
// which can be either returned or will be sent to the user by SMS.
rpc AddOTPSMSAuthenticator (AddOTPSMSAuthenticatorRequest) returns (AddOTPSMSAuthenticatorResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/otp_sms"
post: "/resources/v3alpha/users/{id}/otp_sms"
body: "phone"
};
@ -819,7 +819,7 @@ service ZITADELUsers {
// Verify the OTP SMS registration with the provided code.
rpc VerifyOTPSMSRegistration (VerifyOTPSMSRegistrationRequest) returns (VerifyOTPSMSRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/otp_sms/{otp_sms_id}/_verify"
post: "/resources/v3alpha/users/{id}/otp_sms/{otp_sms_id}/_verify"
body: "code"
};
@ -844,7 +844,7 @@ service ZITADELUsers {
// Remove an existing one-time-password (OTP) SMS authenticator from a user, so it cannot be used for authentication anymore.
rpc RemoveOTPSMSAuthenticator (RemoveOTPSMSAuthenticatorRequest) returns (RemoveOTPSMSAuthenticatorResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/otp_sms/{otp_sms_id}"
delete: "/resources/v3alpha/users/{id}/otp_sms/{otp_sms_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -869,7 +869,7 @@ service ZITADELUsers {
// which can be either returned or will be sent to the user by email.
rpc AddOTPEmailAuthenticator (AddOTPEmailAuthenticatorRequest) returns (AddOTPEmailAuthenticatorResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/otp_email"
post: "/resources/v3alpha/users/{id}/otp_email"
body: "email"
};
@ -893,7 +893,7 @@ service ZITADELUsers {
// Verify the OTP Email registration with the provided code.
rpc VerifyOTPEmailRegistration (VerifyOTPEmailRegistrationRequest) returns (VerifyOTPEmailRegistrationResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/otp_email/{otp_email_id}/_verify"
post: "/resources/v3alpha/users/{id}/otp_email/{otp_email_id}/_verify"
body: "code"
};
@ -918,7 +918,7 @@ service ZITADELUsers {
// Remove an existing one-time-password (OTP) Email authenticator from a user, so it cannot be used for authentication anymore.
rpc RemoveOTPEmailAuthenticator (RemoveOTPEmailAuthenticatorRequest) returns (RemoveOTPEmailAuthenticatorResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/otp_email/{otp_email_id}"
delete: "/resources/v3alpha/users/{id}/otp_email/{otp_email_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -991,7 +991,7 @@ service ZITADELUsers {
// This will allow the user to authenticate with the provided IDP.
rpc AddIDPAuthenticator (AddIDPAuthenticatorRequest) returns (AddIDPAuthenticatorResponse) {
option (google.api.http) = {
post: "/resources/v3alpha/users/{user_id}/idps"
post: "/resources/v3alpha/users/{id}/idps"
body: "authenticator"
};
@ -1016,7 +1016,7 @@ service ZITADELUsers {
// Remove an existing identity provider (IDP) authenticator from a user, so it cannot be used for authentication anymore.
rpc RemoveIDPAuthenticator (RemoveIDPAuthenticatorRequest) returns (RemoveIDPAuthenticatorResponse) {
option (google.api.http) = {
delete: "/resources/v3alpha/users/{user_id}/idps/{idp_id}"
delete: "/resources/v3alpha/users/{id}/idps/{idp_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
@ -1069,7 +1069,7 @@ message GetUserRequest {
}
];
// unique identifier of the user.
string user_id = 2 [
string id = 2 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1123,7 +1123,7 @@ message PatchUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629012906488334\"";
}
@ -1156,7 +1156,7 @@ message DeactivateUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1172,7 +1172,7 @@ message DeactivateUserResponse {
}
message ReactivateUserRequest {
message ActivateUserRequest {
optional zitadel.object.v3alpha.Instance instance = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
default: "\"domain from HOST or :authority header\""
@ -1181,7 +1181,7 @@ message ReactivateUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1192,7 +1192,7 @@ message ReactivateUserRequest {
];
}
message ReactivateUserResponse {
message ActivateUserResponse {
zitadel.resources.object.v3alpha.Details details = 1;
}
@ -1205,7 +1205,7 @@ message LockUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1229,7 +1229,7 @@ message UnlockUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1253,7 +1253,7 @@ message DeleteUserRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1277,7 +1277,7 @@ message SetContactEmailRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1309,7 +1309,7 @@ message VerifyContactEmailRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1343,7 +1343,7 @@ message ResendContactEmailCodeRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1376,7 +1376,7 @@ message SetContactPhoneRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1408,7 +1408,7 @@ message VerifyContactPhoneRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1442,7 +1442,7 @@ message ResendContactPhoneCodeRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1475,7 +1475,7 @@ message AddUsernameRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1507,7 +1507,7 @@ message RemoveUsernameRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1541,7 +1541,7 @@ message SetPasswordRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1567,7 +1567,7 @@ message RequestPasswordResetRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1606,7 +1606,7 @@ message StartWebAuthNRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1645,7 +1645,7 @@ message VerifyWebAuthNRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1680,7 +1680,7 @@ message CreateWebAuthNRegistrationLinkRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1713,7 +1713,7 @@ message RemoveWebAuthNAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1747,7 +1747,7 @@ message StartTOTPRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1789,7 +1789,7 @@ message VerifyTOTPRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1833,7 +1833,7 @@ message RemoveTOTPAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1867,7 +1867,7 @@ message AddOTPSMSAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1906,7 +1906,7 @@ message VerifyOTPSMSRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1950,7 +1950,7 @@ message RemoveOTPSMSAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -1984,7 +1984,7 @@ message AddOTPEmailAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -2022,7 +2022,7 @@ message VerifyOTPEmailRegistrationRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -2066,7 +2066,7 @@ message RemoveOTPEmailAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -2170,7 +2170,7 @@ message GetIdentityProviderIntentResponse {
// and detailed / profile information.
IDPInformation idp_information = 2;
// If the user was already federated and linked to a ZITADEL user, it's id will be returned.
optional string user_id = 3 [
optional string id = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"163840776835432345\"";
}
@ -2186,7 +2186,7 @@ message AddIDPAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@ -2211,7 +2211,7 @@ message RemoveIDPAuthenticatorRequest {
// Optionally expect the user to be in this organization.
optional zitadel.object.v3alpha.Organization organization = 2;
// unique identifier of the user.
string user_id = 3 [
string id = 3 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {