From a9eb50321c3fa91fed4126b807010ad52f1fd756 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:24:15 +0200 Subject: [PATCH] fix: update userschema api to v3alpha definition (#8511) # Which Problems Are Solved UserSchema API is currently not completely as defined for v3alpha. # How the Problems Are Solved Update the protos and integration tests. # Additional Changes None # Additional Context None --- .../resources/userschema/v3alpha/query.go | 30 +++-- .../v3alpha/query_integration_test.go | 111 +++++++++-------- .../userschema/v3alpha/userschema.go | 16 ++- .../v3alpha/userschema_integration_test.go | 96 +++++++++------ internal/integration/client.go | 8 +- .../userschema/v3alpha/user_schema.proto | 77 +++++++++--- .../v3alpha/user_schema_service.proto | 116 ++++-------------- 7 files changed, 242 insertions(+), 212 deletions(-) diff --git a/internal/api/grpc/resources/userschema/v3alpha/query.go b/internal/api/grpc/resources/userschema/v3alpha/query.go index aa25136e2..b457c71c9 100644 --- a/internal/api/grpc/resources/userschema/v3alpha/query.go +++ b/internal/api/grpc/resources/userschema/v3alpha/query.go @@ -35,7 +35,7 @@ func (s *Server) SearchUserSchemas(ctx context.Context, req *schema.SearchUserSc }, nil } -func (s *Server) GetUserSchemaByID(ctx context.Context, req *schema.GetUserSchemaByIDRequest) (*schema.GetUserSchemaByIDResponse, error) { +func (s *Server) GetUserSchema(ctx context.Context, req *schema.GetUserSchemaRequest) (*schema.GetUserSchemaResponse, error) { if err := checkUserSchemaEnabled(ctx); err != nil { return nil, err } @@ -47,8 +47,8 @@ func (s *Server) GetUserSchemaByID(ctx context.Context, req *schema.GetUserSchem if err != nil { return nil, err } - return &schema.GetUserSchemaByIDResponse{ - Schema: userSchema, + return &schema.GetUserSchemaResponse{ + UserSchema: userSchema, }, nil } @@ -94,8 +94,8 @@ func userSchemaFieldNameToSortingColumn(field *schema.FieldName) query.Column { } } -func userSchemasToPb(schemas []*query.UserSchema) (_ []*schema.UserSchema, err error) { - userSchemas := make([]*schema.UserSchema, len(schemas)) +func userSchemasToPb(schemas []*query.UserSchema) (_ []*schema.GetUserSchema, err error) { + userSchemas := make([]*schema.GetUserSchema, len(schemas)) for i, userSchema := range schemas { userSchemas[i], err = userSchemaToPb(userSchema) if err != nil { @@ -105,18 +105,22 @@ func userSchemasToPb(schemas []*query.UserSchema) (_ []*schema.UserSchema, err e return userSchemas, nil } -func userSchemaToPb(userSchema *query.UserSchema) (*schema.UserSchema, error) { +func userSchemaToPb(userSchema *query.UserSchema) (*schema.GetUserSchema, error) { s := new(structpb.Struct) if err := s.UnmarshalJSON(userSchema.Schema); err != nil { return nil, err } - return &schema.UserSchema{ - Details: resource_object.DomainToDetailsPb(&userSchema.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, userSchema.ResourceOwner), - Type: userSchema.Type, - State: userSchemaStateToPb(userSchema.State), - Revision: userSchema.Revision, - Schema: s, - PossibleAuthenticators: authenticatorTypesToPb(userSchema.PossibleAuthenticators), + return &schema.GetUserSchema{ + Details: resource_object.DomainToDetailsPb(&userSchema.ObjectDetails, object.OwnerType_OWNER_TYPE_INSTANCE, userSchema.ResourceOwner), + Config: &schema.UserSchema{ + Type: userSchema.Type, + DataType: &schema.UserSchema_Schema{ + Schema: s, + }, + PossibleAuthenticators: authenticatorTypesToPb(userSchema.PossibleAuthenticators), + }, + State: userSchemaStateToPb(userSchema.State), + Revision: userSchema.Revision, }, nil } diff --git a/internal/api/grpc/resources/userschema/v3alpha/query_integration_test.go b/internal/api/grpc/resources/userschema/v3alpha/query_integration_test.go index 18996fb3f..38e931787 100644 --- a/internal/api/grpc/resources/userschema/v3alpha/query_integration_test.go +++ b/internal/api/grpc/resources/userschema/v3alpha/query_integration_test.go @@ -4,10 +4,10 @@ package userschema_test import ( "context" - "fmt" "testing" "time" + "github.com/brianvoe/gofakeit/v6" "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -69,7 +69,7 @@ func TestServer_ListUserSchemas(t *testing.T) { TotalResult: 0, AppliedLimit: 100, }, - Result: []*schema.UserSchema{}, + Result: []*schema.GetUserSchema{}, }, }, { @@ -78,7 +78,7 @@ func TestServer_ListUserSchemas(t *testing.T) { ctx: IAMOwnerCTX, req: &schema.SearchUserSchemasRequest{}, prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error { - schemaType := fmt.Sprint(time.Now().UnixNano() + 1) + schemaType := gofakeit.Name() createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType) request.Filters = []*schema.SearchFilter{ { @@ -90,7 +90,7 @@ func TestServer_ListUserSchemas(t *testing.T) { }, }, } - resp.Result[0].Type = schemaType + resp.Result[0].Config.Type = schemaType resp.Result[0].Details = createResp.GetDetails() // as schema is freshly created, the changed date is the created date resp.Result[0].Details.Created = resp.Result[0].Details.GetChanged() @@ -103,12 +103,17 @@ func TestServer_ListUserSchemas(t *testing.T) { TotalResult: 1, AppliedLimit: 100, }, - Result: []*schema.UserSchema{ + Result: []*schema.GetUserSchema{ { - State: schema.State_STATE_ACTIVE, - Revision: 1, - Schema: userSchema, - PossibleAuthenticators: nil, + State: schema.State_STATE_ACTIVE, + Revision: 1, + Config: &schema.UserSchema{ + Type: "", + DataType: &schema.UserSchema_Schema{ + Schema: userSchema, + }, + PossibleAuthenticators: nil, + }, }, }, }, @@ -119,7 +124,7 @@ func TestServer_ListUserSchemas(t *testing.T) { ctx: IAMOwnerCTX, req: &schema.SearchUserSchemasRequest{}, prepare: func(request *schema.SearchUserSchemasRequest, resp *schema.SearchUserSchemasResponse) error { - schemaType := fmt.Sprint(time.Now().UnixNano()) + schemaType := gofakeit.Name() schemaType1 := schemaType + "_1" schemaType2 := schemaType + "_2" createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType1) @@ -138,9 +143,9 @@ func TestServer_ListUserSchemas(t *testing.T) { }, } - resp.Result[0].Type = schemaType1 + resp.Result[0].Config.Type = schemaType1 resp.Result[0].Details = createResp.GetDetails() - resp.Result[1].Type = schemaType2 + resp.Result[1].Config.Type = schemaType2 resp.Result[1].Details = createResp2.GetDetails() return nil }, @@ -150,18 +155,24 @@ func TestServer_ListUserSchemas(t *testing.T) { TotalResult: 2, AppliedLimit: 100, }, - Result: []*schema.UserSchema{ + Result: []*schema.GetUserSchema{ { - State: schema.State_STATE_ACTIVE, - Revision: 1, - Schema: userSchema, - PossibleAuthenticators: nil, + State: schema.State_STATE_ACTIVE, + Revision: 1, + Config: &schema.UserSchema{ + DataType: &schema.UserSchema_Schema{ + Schema: userSchema, + }, + }, }, { - State: schema.State_STATE_ACTIVE, - Revision: 1, - Schema: userSchema, - PossibleAuthenticators: nil, + State: schema.State_STATE_ACTIVE, + Revision: 1, + Config: &schema.UserSchema{ + DataType: &schema.UserSchema_Schema{ + Schema: userSchema, + }, + }, }, }, }, @@ -203,7 +214,7 @@ func TestServer_ListUserSchemas(t *testing.T) { } } -func TestServer_GetUserSchemaByID(t *testing.T) { +func TestServer_GetUserSchema(t *testing.T) { ensureFeatureEnabled(t, IAMOwnerCTX) userSchema := new(structpb.Struct) @@ -215,22 +226,22 @@ func TestServer_GetUserSchemaByID(t *testing.T) { require.NoError(t, err) type args struct { ctx context.Context - req *schema.GetUserSchemaByIDRequest - prepare func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error + req *schema.GetUserSchemaRequest + prepare func(request *schema.GetUserSchemaRequest, resp *schema.GetUserSchemaResponse) error } tests := []struct { name string args args - want *schema.GetUserSchemaByIDResponse + want *schema.GetUserSchemaResponse wantErr bool }{ { name: "missing permission", args: args{ ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner), - req: &schema.GetUserSchemaByIDRequest{}, - prepare: func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error { - schemaType := fmt.Sprint(time.Now().UnixNano() + 1) + req: &schema.GetUserSchemaRequest{}, + prepare: func(request *schema.GetUserSchemaRequest, resp *schema.GetUserSchemaResponse) error { + schemaType := gofakeit.Name() createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType) request.Id = createResp.GetDetails().GetId() return nil @@ -242,7 +253,7 @@ func TestServer_GetUserSchemaByID(t *testing.T) { name: "not existing, error", args: args{ ctx: IAMOwnerCTX, - req: &schema.GetUserSchemaByIDRequest{ + req: &schema.GetUserSchemaRequest{ Id: "notexisting", }, }, @@ -252,23 +263,26 @@ func TestServer_GetUserSchemaByID(t *testing.T) { name: "get, ok", args: args{ ctx: IAMOwnerCTX, - req: &schema.GetUserSchemaByIDRequest{}, - prepare: func(request *schema.GetUserSchemaByIDRequest, resp *schema.GetUserSchemaByIDResponse) error { - schemaType := fmt.Sprint(time.Now().UnixNano() + 1) + req: &schema.GetUserSchemaRequest{}, + prepare: func(request *schema.GetUserSchemaRequest, resp *schema.GetUserSchemaResponse) error { + schemaType := gofakeit.Name() createResp := Tester.CreateUserSchemaEmptyWithType(IAMOwnerCTX, schemaType) request.Id = createResp.GetDetails().GetId() - resp.Schema.Type = schemaType - resp.Schema.Details = createResp.GetDetails() + resp.UserSchema.Config.Type = schemaType + resp.UserSchema.Details = createResp.GetDetails() return nil }, }, - want: &schema.GetUserSchemaByIDResponse{ - Schema: &schema.UserSchema{ - State: schema.State_STATE_ACTIVE, - Revision: 1, - Schema: userSchema, - PossibleAuthenticators: nil, + want: &schema.GetUserSchemaResponse{ + UserSchema: &schema.GetUserSchema{ + State: schema.State_STATE_ACTIVE, + Revision: 1, + Config: &schema.UserSchema{ + DataType: &schema.UserSchema_Schema{ + Schema: userSchema, + }, + }, }, }, }, @@ -286,16 +300,17 @@ func TestServer_GetUserSchemaByID(t *testing.T) { } require.EventuallyWithT(t, func(ttt *assert.CollectT) { - got, err := Client.GetUserSchemaByID(tt.args.ctx, tt.args.req) + got, err := Client.GetUserSchema(tt.args.ctx, tt.args.req) if tt.wantErr { - require.Error(ttt, err) - return + assert.Error(t, err, "Error: "+err.Error()) + } else { + assert.NoError(t, err) + wantSchema := tt.want.GetUserSchema() + gotSchema := got.GetUserSchema() + integration.AssertResourceDetails(t, wantSchema.GetDetails(), gotSchema.GetDetails()) + tt.want.UserSchema.Details = got.GetUserSchema().GetDetails() + grpc.AllFieldsEqual(t, tt.want.ProtoReflect(), got.ProtoReflect(), grpc.CustomMappers) } - assert.NoError(ttt, err) - - integration.AssertResourceDetails(t, tt.want.GetSchema().GetDetails(), got.GetSchema().GetDetails()) - tt.want.Schema.Details = got.GetSchema().GetDetails() - grpc.AllFieldsEqual(t, tt.want.ProtoReflect(), got.ProtoReflect(), grpc.CustomMappers) }, retryDuration, time.Millisecond*100, "timeout waiting for expected user schema result") }) } diff --git a/internal/api/grpc/resources/userschema/v3alpha/userschema.go b/internal/api/grpc/resources/userschema/v3alpha/userschema.go index 406b358b1..7044c79c5 100644 --- a/internal/api/grpc/resources/userschema/v3alpha/userschema.go +++ b/internal/api/grpc/resources/userschema/v3alpha/userschema.go @@ -3,6 +3,8 @@ package userschema import ( "context" + "github.com/muhlemmer/gu" + "github.com/zitadel/zitadel/internal/api/authz" resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha" "github.com/zitadel/zitadel/internal/command" @@ -102,20 +104,28 @@ func createUserSchemaToCommand(req *schema.CreateUserSchemaRequest, resourceOwne } func patchUserSchemaToCommand(req *schema.PatchUserSchemaRequest, resourceOwner string) (*command.ChangeUserSchema, error) { - schema, err := req.GetSchema().MarshalJSON() + schema, err := req.GetUserSchema().GetSchema().MarshalJSON() if err != nil { return nil, err } + + var ty *string + if req.GetUserSchema() != nil && req.GetUserSchema().GetType() != "" { + ty = gu.Ptr(req.GetUserSchema().GetType()) + } return &command.ChangeUserSchema{ ID: req.GetId(), ResourceOwner: resourceOwner, - Type: req.Type, + Type: ty, Schema: schema, - PossibleAuthenticators: authenticatorsToDomain(req.GetPossibleAuthenticators()), + PossibleAuthenticators: authenticatorsToDomain(req.GetUserSchema().GetPossibleAuthenticators()), }, nil } func authenticatorsToDomain(authenticators []schema.AuthenticatorType) []domain.AuthenticatorType { + if authenticators == nil { + return nil + } types := make([]domain.AuthenticatorType, len(authenticators)) for i, authenticator := range authenticators { types[i] = authenticatorTypeToDomain(authenticator) diff --git a/internal/api/grpc/resources/userschema/v3alpha/userschema_integration_test.go b/internal/api/grpc/resources/userschema/v3alpha/userschema_integration_test.go index 8036c7533..f907d0f56 100644 --- a/internal/api/grpc/resources/userschema/v3alpha/userschema_integration_test.go +++ b/internal/api/grpc/resources/userschema/v3alpha/userschema_integration_test.go @@ -4,9 +4,7 @@ package userschema_test import ( "context" - "fmt" "testing" - "time" "github.com/brianvoe/gofakeit/v6" "github.com/muhlemmer/gu" @@ -34,7 +32,7 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "missing permission, error", ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner), req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), }, }, @@ -44,7 +42,7 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "empty type", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: "", }, }, @@ -54,7 +52,7 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "empty schema, error", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), }, }, @@ -64,9 +62,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "invalid schema, error", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -95,9 +93,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "no authenticators, ok", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -134,9 +132,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "invalid authenticator, error", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -168,9 +166,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "with authenticator, ok", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -210,9 +208,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "with invalid permission, error", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -245,9 +243,9 @@ func TestServer_CreateUserSchema(t *testing.T) { name: "with valid permission, ok", ctx: IAMOwnerCTX, req: &schema.CreateUserSchemaRequest{ - UserSchema: &schema.CreateUserSchema{ + UserSchema: &schema.UserSchema{ Type: gofakeit.Name(), - DataType: &schema.CreateUserSchema_Schema{ + DataType: &schema.UserSchema_Schema{ Schema: func() *structpb.Struct { s := new(structpb.Struct) err := s.UnmarshalJSON([]byte(` @@ -326,7 +324,9 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: Tester.WithAuthorization(context.Background(), integration.OrgOwner), req: &schema.PatchUserSchemaRequest{ - Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)), + UserSchema: &schema.PatchUserSchema{ + Type: gu.Ptr(gofakeit.Name()), + }, }, }, wantErr: true, @@ -364,7 +364,9 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - Type: gu.Ptr(""), + UserSchema: &schema.PatchUserSchema{ + Type: gu.Ptr(""), + }, }, }, wantErr: true, @@ -379,7 +381,9 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)), + UserSchema: &schema.PatchUserSchema{ + Type: gu.Ptr(gofakeit.Name()), + }, }, }, want: &schema.PatchUserSchemaResponse{ @@ -402,7 +406,9 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - DataType: &schema.PatchUserSchemaRequest_Schema{}, + UserSchema: &schema.PatchUserSchema{ + DataType: &schema.PatchUserSchema_Schema{}, + }, }, }, want: &schema.PatchUserSchemaResponse{ @@ -425,10 +431,11 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - DataType: &schema.PatchUserSchemaRequest_Schema{ - Schema: func() *structpb.Struct { - s := new(structpb.Struct) - err := s.UnmarshalJSON([]byte(` + UserSchema: &schema.PatchUserSchema{ + DataType: &schema.PatchUserSchema_Schema{ + Schema: func() *structpb.Struct { + s := new(structpb.Struct) + err := s.UnmarshalJSON([]byte(` { "$schema": "urn:zitadel:schema:v1", "type": "object", @@ -443,9 +450,10 @@ func TestServer_UpdateUserSchema(t *testing.T) { } } `)) - require.NoError(t, err) - return s - }(), + require.NoError(t, err) + return s + }(), + }, }, }, }, @@ -461,10 +469,11 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - DataType: &schema.PatchUserSchemaRequest_Schema{ - Schema: func() *structpb.Struct { - s := new(structpb.Struct) - err := s.UnmarshalJSON([]byte(` + UserSchema: &schema.PatchUserSchema{ + DataType: &schema.PatchUserSchema_Schema{ + Schema: func() *structpb.Struct { + s := new(structpb.Struct) + err := s.UnmarshalJSON([]byte(` { "$schema": "urn:zitadel:schema:v1", "type": "object", @@ -479,9 +488,10 @@ func TestServer_UpdateUserSchema(t *testing.T) { "required": ["name"] } `)) - require.NoError(t, err) - return s - }(), + require.NoError(t, err) + return s + }(), + }, }, }, }, @@ -505,8 +515,10 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - PossibleAuthenticators: []schema.AuthenticatorType{ - schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED, + UserSchema: &schema.PatchUserSchema{ + PossibleAuthenticators: []schema.AuthenticatorType{ + schema.AuthenticatorType_AUTHENTICATOR_TYPE_UNSPECIFIED, + }, }, }, }, @@ -522,8 +534,10 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - PossibleAuthenticators: []schema.AuthenticatorType{ - schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME, + UserSchema: &schema.PatchUserSchema{ + PossibleAuthenticators: []schema.AuthenticatorType{ + schema.AuthenticatorType_AUTHENTICATOR_TYPE_USERNAME, + }, }, }, }, @@ -551,7 +565,9 @@ func TestServer_UpdateUserSchema(t *testing.T) { args: args{ ctx: IAMOwnerCTX, req: &schema.PatchUserSchemaRequest{ - Type: gu.Ptr(fmt.Sprint(time.Now().UnixNano() + 1)), + UserSchema: &schema.PatchUserSchema{ + Type: gu.Ptr(gofakeit.Name()), + }, }, }, wantErr: true, diff --git a/internal/integration/client.go b/internal/integration/client.go index f1fa7ca6d..ed5f0620c 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -770,9 +770,9 @@ func (s *Tester) CreateUserSchema(ctx context.Context, schemaData []byte) *users err := userSchema.UnmarshalJSON(schemaData) logging.OnError(err).Fatal("create userschema unmarshal") schema, err := s.Client.UserSchemaV3.CreateUserSchema(ctx, &userschema_v3alpha.CreateUserSchemaRequest{ - UserSchema: &userschema_v3alpha.CreateUserSchema{ + UserSchema: &userschema_v3alpha.UserSchema{ Type: fmt.Sprint(time.Now().UnixNano() + 1), - DataType: &userschema_v3alpha.CreateUserSchema_Schema{ + DataType: &userschema_v3alpha.UserSchema_Schema{ Schema: userSchema, }, }, @@ -790,9 +790,9 @@ func (s *Tester) CreateUserSchemaEmptyWithType(ctx context.Context, schemaType s }`)) logging.OnError(err).Fatal("create userschema unmarshal") schema, err := s.Client.UserSchemaV3.CreateUserSchema(ctx, &userschema_v3alpha.CreateUserSchemaRequest{ - UserSchema: &userschema_v3alpha.CreateUserSchema{ + UserSchema: &userschema_v3alpha.UserSchema{ Type: schemaType, - DataType: &userschema_v3alpha.CreateUserSchema_Schema{ + DataType: &userschema_v3alpha.UserSchema_Schema{ Schema: userSchema, }, }, diff --git a/proto/zitadel/resources/userschema/v3alpha/user_schema.proto b/proto/zitadel/resources/userschema/v3alpha/user_schema.proto index cb957eadb..d9c2e3091 100644 --- a/proto/zitadel/resources/userschema/v3alpha/user_schema.proto +++ b/proto/zitadel/resources/userschema/v3alpha/user_schema.proto @@ -10,37 +10,84 @@ import "zitadel/resources/object/v3alpha/object.proto"; option go_package = "github.com/zitadel/zitadel/pkg/grpc/resources/userschema/v3alpha;userschema"; -message UserSchema { +message GetUserSchema { // Details provide some base information (such as the last change date) of the schema. - zitadel.resources.object.v3alpha.Details details = 2; - // Type is a human readable text describing the schema. - string type = 3 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "\"employees\"" - } - ]; + zitadel.resources.object.v3alpha.Details details = 1; + UserSchema config = 2; // Current state of the schema. - State state = 4 [ + State state = 3 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"STATE_ACTIVE\"" } ]; // Revision is a read only version of the schema, each update of the `schema`-field increases the revision. - uint32 revision = 5 [ + uint32 revision = 4 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "\"2\"" } ]; - // JSON schema representation defining the user. - google.protobuf.Struct schema = 6 [ +} + +message UserSchema { + // Type is a human readable word describing the schema. + string type = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "{\"$schema\":\"https://example.com/user/employees\",\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"required\":true},\"description\":{\"type\":\"string\"}}}" + min_length: 1; + max_length: 200; + example: "\"employees\""; } ]; + oneof data_type { + option (validate.required) = true; + + // JSON schema representation defining the user. + google.protobuf.Struct schema = 2 [ + (validate.rules).message = {required: true}, + (google.api.field_behavior) = REQUIRED, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "{\"$schema\":\"https://example.com/user/employees\",\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"required\":true},\"description\":{\"type\":\"string\"}}}" + } + ]; + + // (--In the future we will allow to use an external registry.--) + } // Defines the possible types of authenticators. - // This allows creating different user types like human/machine without usage of actions to validate possible authenticators. + repeated AuthenticatorType possible_authenticators = 3 [ + (validate.rules).repeated = {unique: true, items: {enum: {defined_only: true, not_in: [0]}}}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[\"AUTHENTICATOR_TYPE_USERNAME\",\"AUTHENTICATOR_TYPE_PASSWORD\",\"AUTHENTICATOR_TYPE_WEBAUTHN\"]"; + } + ]; +} + +message PatchUserSchema { + // Type is a human readable word describing the schema. + optional string type = 2 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + min_length: 1; + max_length: 200; + example: "\"employees\""; + } + ]; + oneof data_type { + // JSON schema representation defining the user. + google.protobuf.Struct schema = 3 [ + (validate.rules).message = {required: true}, + (google.api.field_behavior) = REQUIRED, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "{\"$schema\":\"https://example.com/user/employees\",\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"required\":true},\"description\":{\"type\":\"string\"}}}" + } + ]; + } + // Defines the possible types of authenticators. + // // Removal of an authenticator does not remove the authenticator on a user. - repeated AuthenticatorType possible_authenticators = 7 [ + repeated AuthenticatorType possible_authenticators = 4 [ + (validate.rules).repeated = {unique: true, items: {enum: {defined_only: true, not_in: [0]}}}, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "[\"AUTHENTICATOR_TYPE_USERNAME\",\"AUTHENTICATOR_TYPE_PASSWORD\",\"AUTHENTICATOR_TYPE_WEBAUTHN\"]"; } diff --git a/proto/zitadel/resources/userschema/v3alpha/user_schema_service.proto b/proto/zitadel/resources/userschema/v3alpha/user_schema_service.proto index 77def934e..b709ac290 100644 --- a/proto/zitadel/resources/userschema/v3alpha/user_schema_service.proto +++ b/proto/zitadel/resources/userschema/v3alpha/user_schema_service.proto @@ -43,7 +43,7 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { produces: "application/grpc-web+proto"; host: "$CUSTOM-DOMAIN"; - base_path: "/"; + base_path: "/resources/v3alpha/user_schemas"; external_docs: { description: "Detailed information about ZITADEL", @@ -110,8 +110,8 @@ service ZITADELUserSchemas { // Search all matching user schemas. By default, we will return all user schema of your instance. Make sure to include a limit and sorting for pagination. rpc SearchUserSchemas (SearchUserSchemasRequest) returns (SearchUserSchemasResponse) { option (google.api.http) = { - post: "/v3alpha/user_schemas/search" - body: "*" + post: "/_search" + body: "filters" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -144,9 +144,9 @@ service ZITADELUserSchemas { // User schema by ID // // Returns the user schema identified by the requested ID. - rpc GetUserSchemaByID (GetUserSchemaByIDRequest) returns (GetUserSchemaByIDResponse) { + rpc GetUserSchema (GetUserSchemaRequest) returns (GetUserSchemaResponse) { option (google.api.http) = { - get: "/v3alpha/user_schemas/{id}" + get: "/{id}" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -170,8 +170,8 @@ service ZITADELUserSchemas { // Create the first revision of a new user schema. The schema can then be used on users to store and validate their data. rpc CreateUserSchema (CreateUserSchemaRequest) returns (CreateUserSchemaResponse) { option (google.api.http) = { - post: "/v3alpha/user_schemas" - body: "*" + post: "/" + body: "user_schema" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -203,8 +203,8 @@ service ZITADELUserSchemas { // Patch an existing user schema to a new revision. Users based on the current revision will not be affected until they are updated. rpc PatchUserSchema (PatchUserSchemaRequest) returns (PatchUserSchemaResponse) { option (google.api.http) = { - patch: "/v3alpha/user_schemas/{id}" - body: "*" + patch: "/{id}" + body: "user_schema" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -228,7 +228,7 @@ service ZITADELUserSchemas { // Deactivate an existing user schema and change it into a read-only state. Users based on this schema cannot be updated anymore, but are still able to authenticate. rpc DeactivateUserSchema (DeactivateUserSchemaRequest) returns (DeactivateUserSchemaResponse) { option (google.api.http) = { - post: "/v3alpha/user_schemas/{id}/deactivate" + post: "/{id}/_deactivate" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -252,7 +252,7 @@ service ZITADELUserSchemas { // Reactivate an previously deactivated user schema and change it into an active state again. rpc ReactivateUserSchema (ReactivateUserSchemaRequest) returns (ReactivateUserSchemaResponse) { option (google.api.http) = { - post: "/v3alpha/user_schemas/{id}/reactivate" + post: "/{id}/_reactivate" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -276,7 +276,7 @@ service ZITADELUserSchemas { // Delete an existing user schema. This operation is only allowed if there are no associated users to it. rpc DeleteUserSchema (DeleteUserSchemaRequest) returns (DeleteUserSchemaResponse) { option (google.api.http) = { - delete: "/v3alpha/user_schemas/{id}" + delete: "/{id}" }; option (zitadel.protoc_gen_zitadel.v2.options) = { @@ -294,7 +294,6 @@ service ZITADELUserSchemas { }; }; } - } message SearchUserSchemasRequest { @@ -321,11 +320,10 @@ message SearchUserSchemasResponse { // States by which field the results are sorted. FieldName sorting_column = 2; // The result contains the user schemas, which matched the queries. - repeated UserSchema result = 3; + repeated GetUserSchema result = 3; } - -message GetUserSchemaByIDRequest { +message GetUserSchemaRequest { // unique identifier of the schema. string id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, @@ -338,8 +336,8 @@ message GetUserSchemaByIDRequest { ]; } -message GetUserSchemaByIDResponse { - zitadel.resources.userschema.v3alpha.UserSchema schema = 1; +message GetUserSchemaResponse { + GetUserSchema user_schema = 2; } message CreateUserSchemaRequest { @@ -348,51 +346,7 @@ message CreateUserSchemaRequest { default: "\"domain from HOST or :authority header\"" } ]; - CreateUserSchema user_schema = 2 [ - (validate.rules).message = { - required: true - } - ]; -} - -message CreateUserSchema{ - optional zitadel.object.v3alpha.Instance instance = 4 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - default: "\"domain from HOST or :authority header\"" - } - ]; - - // Type is a human readable word describing the schema. - string type = 1 [ - (validate.rules).string = {min_len: 1, max_len: 200}, - (google.api.field_behavior) = REQUIRED, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - min_length: 1; - max_length: 200; - example: "\"employees\""; - } - ]; - oneof data_type { - option (validate.required) = true; - - // JSON schema representation defining the user. - google.protobuf.Struct schema = 2 [ - (validate.rules).message = {required: true}, - (google.api.field_behavior) = REQUIRED, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "{\"$schema\":\"https://example.com/user/employees\",\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"required\":true},\"description\":{\"type\":\"string\"}}}" - } - ]; - - // (--In the future we will allow to use an external registry.--) - } - // Defines the possible types of authenticators. - repeated AuthenticatorType possible_authenticators = 3 [ - (validate.rules).repeated = {unique: true, items: {enum: {defined_only: true, not_in: [0]}}}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"AUTHENTICATOR_TYPE_USERNAME\",\"AUTHENTICATOR_TYPE_PASSWORD\",\"AUTHENTICATOR_TYPE_WEBAUTHN\"]"; - } - ]; + UserSchema user_schema = 2; } message CreateUserSchemaResponse { @@ -402,42 +356,23 @@ message CreateUserSchemaResponse { message PatchUserSchemaRequest { - optional zitadel.object.v3alpha.Instance instance = 5 [ + optional zitadel.object.v3alpha.Instance instance = 1 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { default: "\"domain from HOST or :authority header\"" } ]; // unique identifier of the schema. - string id = 1; - // Type is a human readable word describing the schema. - optional string type = 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) = { - min_length: 1; - max_length: 200; - example: "\"employees\""; - } - ]; - oneof data_type { - // JSON schema representation defining the user. - google.protobuf.Struct schema = 3 [ - (validate.rules).message = {required: true}, - (google.api.field_behavior) = REQUIRED, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "{\"$schema\":\"https://example.com/user/employees\",\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\",\"required\":true},\"description\":{\"type\":\"string\"}}}" - } - ]; - } - // Defines the possible types of authenticators. - // - // Removal of an authenticator does not remove the authenticator on a user. - repeated AuthenticatorType possible_authenticators = 4 [ - (validate.rules).repeated = {unique: true, items: {enum: {defined_only: true, not_in: [0]}}}, - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "[\"AUTHENTICATOR_TYPE_USERNAME\",\"AUTHENTICATOR_TYPE_PASSWORD\",\"AUTHENTICATOR_TYPE_WEBAUTHN\"]"; + min_length: 1, + max_length: 200, + example: "\"69629026806489455\""; } ]; + + PatchUserSchema user_schema = 3; } message PatchUserSchemaResponse { @@ -454,6 +389,7 @@ message DeactivateUserSchemaRequest { // unique identifier of the schema. string id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1, max_length: 200, @@ -476,6 +412,7 @@ message ReactivateUserSchemaRequest { // unique identifier of the schema. string id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1, max_length: 200, @@ -498,6 +435,7 @@ message DeleteUserSchemaRequest { // unique identifier of the schema. string id = 1 [ (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { min_length: 1, max_length: 200,