diff --git a/internal/api/grpc/admin/org.go b/internal/api/grpc/admin/org.go index 293e7c74d7..ef97e47bb0 100644 --- a/internal/api/grpc/admin/org.go +++ b/internal/api/grpc/admin/org.go @@ -78,7 +78,7 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (* if err != nil { return nil, err } - human := setUpOrgHumanToCommand(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) //TODO: handle machine + human := setUpOrgHumanToCommand(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) // TODO: handle machine createdOrg, err := s.command.SetUpOrg(ctx, &command.OrgSetup{ Name: req.Org.Name, CustomDomain: req.Org.Domain, @@ -93,8 +93,8 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (* return nil, err } var userID string - if len(createdOrg.CreatedAdmins) == 1 { - userID = createdOrg.CreatedAdmins[0].ID + if len(createdOrg.OrgAdmins) == 1 { + userID = createdOrg.OrgAdmins[0].GetID() } return &admin_pb.SetUpOrgResponse{ Details: object.DomainToAddDetailsPb(createdOrg.ObjectDetails), diff --git a/internal/api/grpc/object/v2beta/converter.go b/internal/api/grpc/object/v2beta/converter.go index 73d5f18843..818e376564 100644 --- a/internal/api/grpc/object/v2beta/converter.go +++ b/internal/api/grpc/object/v2beta/converter.go @@ -2,6 +2,7 @@ package object import ( "context" + "time" "google.golang.org/protobuf/types/known/timestamppb" @@ -115,6 +116,36 @@ func DomainValidationTypeFromModel(validationType domain.OrgDomainValidationType } } +func ToViewDetailsPb( + sequence uint64, + creationDate, + changeDate time.Time, + resourceOwner string, +) *object.Details { + details := &object.Details{ + Sequence: sequence, + ResourceOwner: resourceOwner, + } + if !creationDate.IsZero() { + details.CreationDate = timestamppb.New(creationDate) + } + if !changeDate.IsZero() { + details.ChangeDate = timestamppb.New(changeDate) + } + return details +} + +func DomainToChangeDetailsPb(objectDetail *domain.ObjectDetails) *object.Details { + details := &object.Details{ + Sequence: objectDetail.Sequence, + ResourceOwner: objectDetail.ResourceOwner, + } + if !objectDetail.EventDate.IsZero() { + details.ChangeDate = timestamppb.New(objectDetail.EventDate) + } + return details +} + func DomainValidationTypeToDomain(validationType org_pb.DomainValidationType) domain.OrgDomainValidationType { switch validationType { case org_pb.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP: diff --git a/internal/api/grpc/org/v2/org.go b/internal/api/grpc/org/v2/org.go index bbc3caca85..b876826365 100644 --- a/internal/api/grpc/org/v2/org.go +++ b/internal/api/grpc/org/v2/org.go @@ -69,12 +69,15 @@ func addOrganizationRequestAdminToCommand(admin *org.AddOrganizationRequest_Admi } func createdOrganizationToPb(createdOrg *command.CreatedOrg) (_ *org.AddOrganizationResponse, err error) { - admins := make([]*org.AddOrganizationResponse_CreatedAdmin, len(createdOrg.CreatedAdmins)) - for i, admin := range createdOrg.CreatedAdmins { - admins[i] = &org.AddOrganizationResponse_CreatedAdmin{ - UserId: admin.ID, - EmailCode: admin.EmailCode, - PhoneCode: admin.PhoneCode, + admins := make([]*org.AddOrganizationResponse_CreatedAdmin, 0, len(createdOrg.OrgAdmins)) + for _, admin := range createdOrg.OrgAdmins { + admin, ok := admin.(*command.CreatedOrgAdmin) + if ok { + admins = append(admins, &org.AddOrganizationResponse_CreatedAdmin{ + UserId: admin.GetID(), + EmailCode: admin.EmailCode, + PhoneCode: admin.PhoneCode, + }) } } return &org.AddOrganizationResponse{ diff --git a/internal/api/grpc/org/v2beta/helper.go b/internal/api/grpc/org/v2beta/helper.go index 39bad0dae2..603a559723 100644 --- a/internal/api/grpc/org/v2beta/helper.go +++ b/internal/api/grpc/org/v2beta/helper.go @@ -72,18 +72,33 @@ func OrgStateToPb(state domain.OrgState) v2beta_org.OrgState { } func createdOrganizationToPb(createdOrg *command.CreatedOrg) (_ *org.CreateOrganizationResponse, err error) { - admins := make([]*org.CreatedAdmin, len(createdOrg.CreatedAdmins)) - for i, admin := range createdOrg.CreatedAdmins { - admins[i] = &org.CreatedAdmin{ - UserId: admin.ID, - EmailCode: admin.EmailCode, - PhoneCode: admin.PhoneCode, + admins := make([]*org.OrganizationAdmin, len(createdOrg.OrgAdmins)) + for i, admin := range createdOrg.OrgAdmins { + switch admin := admin.(type) { + case *command.CreatedOrgAdmin: + admins[i] = &org.OrganizationAdmin{ + OrganizationAdmin: &org.OrganizationAdmin_CreatedAdmin{ + CreatedAdmin: &org.CreatedAdmin{ + UserId: admin.ID, + EmailCode: admin.EmailCode, + PhoneCode: admin.PhoneCode, + }, + }, + } + case *command.AssignedOrgAdmin: + admins[i] = &org.OrganizationAdmin{ + OrganizationAdmin: &org.OrganizationAdmin_AssignedAdmin{ + AssignedAdmin: &org.AssignedAdmin{ + UserId: admin.ID, + }, + }, + } } } return &org.CreateOrganizationResponse{ - CreationDate: timestamppb.New(createdOrg.ObjectDetails.EventDate), - Id: createdOrg.ObjectDetails.ResourceOwner, - CreatedAdmins: admins, + CreationDate: timestamppb.New(createdOrg.ObjectDetails.EventDate), + Id: createdOrg.ObjectDetails.ResourceOwner, + OrganizationAdmins: admins, }, nil } @@ -150,6 +165,21 @@ func FieldNameToOrgColumn(fieldName v2beta_org.OrgFieldName) query.Column { } } +// func OrgViewToPb(org *query.Org) *v2beta_org.Organization { +// return &v2beta_org.Organization{ +// Id: org.ID, +// State: OrgStateToPb(org.State), +// Name: org.Name, +// PrimaryDomain: org.Domain, +// Details: v2beta_object.ToViewDetailsPb( +// org.Sequence, +// org.CreationDate, +// org.ChangeDate, +// org.ResourceOwner, +// ), +// } +// } + func ListOrgDomainsRequestToModel(systemDefaults systemdefaults.SystemDefaults, request *org.ListOrganizationDomainsRequest) (*query.OrgDomainSearchQueries, error) { offset, limit, asc, err := filter.PaginationPbToQuery(systemDefaults, request.Pagination) if err != nil { diff --git a/internal/api/grpc/org/v2beta/org.go b/internal/api/grpc/org/v2beta/org.go index 4d19f370f8..d7d2d8bd37 100644 --- a/internal/api/grpc/org/v2beta/org.go +++ b/internal/api/grpc/org/v2beta/org.go @@ -29,6 +29,18 @@ func (s *Server) CreateOrganization(ctx context.Context, request *v2beta_org.Cre return createdOrganizationToPb(createdOrg) } +func addOrganizationRequestToCommand(request *v2beta_org.CreateOrganizationRequest) (*command.OrgSetup, error) { + admins, err := createOrganizationRequestAdminsToCommand(request.GetAdmins()) + if err != nil { + return nil, err + } + return &command.OrgSetup{ + Name: request.GetName(), + CustomDomain: "", + Admins: admins, + }, nil +} + func (s *Server) UpdateOrganization(ctx context.Context, request *v2beta_org.UpdateOrganizationRequest) (*v2beta_org.UpdateOrganizationResponse, error) { org, err := s.command.ChangeOrg(ctx, request.Id, request.Name) if err != nil { diff --git a/internal/command/org.go b/internal/command/org.go index b6650ef7f2..c1136d9ddc 100644 --- a/internal/command/org.go +++ b/internal/command/org.go @@ -54,7 +54,11 @@ type orgSetupCommands struct { type CreatedOrg struct { ObjectDetails *domain.ObjectDetails - CreatedAdmins []*CreatedOrgAdmin + OrgAdmins []OrgAdmin +} + +type OrgAdmin interface { + GetID() string } type CreatedOrgAdmin struct { @@ -65,6 +69,18 @@ type CreatedOrgAdmin struct { MachineKey *MachineKey } +func (a *CreatedOrgAdmin) GetID() string { + return a.ID +} + +type AssignedOrgAdmin struct { + ID string +} + +func (a *AssignedOrgAdmin) GetID() string { + return a.ID +} + func (o *OrgSetup) Validate() (err error) { if o.OrgID != "" && strings.TrimSpace(o.OrgID) == "" { return zerrors.ThrowInvalidArgument(nil, "ORG-4ABd3", "Errors.Invalid.Argument") @@ -188,12 +204,12 @@ func (c *orgSetupCommands) push(ctx context.Context) (_ *CreatedOrg, err error) EventDate: events[len(events)-1].CreatedAt(), ResourceOwner: c.aggregate.ID, }, - CreatedAdmins: c.createdAdmins(), + OrgAdmins: c.createdAdmins(), }, nil } -func (c *orgSetupCommands) createdAdmins() []*CreatedOrgAdmin { - users := make([]*CreatedOrgAdmin, 0, len(c.admins)) +func (c *orgSetupCommands) createdAdmins() []OrgAdmin { + users := make([]OrgAdmin, 0, len(c.admins)) for _, admin := range c.admins { if admin.ID != "" && admin.Human == nil { continue diff --git a/proto/zitadel/org/v2beta/org_service.proto b/proto/zitadel/org/v2beta/org_service.proto index 2c6c8f62eb..bdf296863d 100644 --- a/proto/zitadel/org/v2beta/org_service.proto +++ b/proto/zitadel/org/v2beta/org_service.proto @@ -548,6 +548,35 @@ message CreatedAdmin { optional string email_code = 2; optional string phone_code = 3; } +message AssignedAdmin { string user_id = 1; } + +message OrganizationAdmin { + oneof OrganizationAdmin { + option (validate.required) = true; + + CreatedAdmin created_admin = 3; + AssignedAdmin assigned_admin = 4; + // The timestamp of the organization was created. + google.protobuf.Timestamp created_date = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"2024-12-18T07:50:47.492Z\""; + } + ]; + + // Organization ID of the newly created organization. + 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: "\"69629012906488334\""; + } + ]; + + // The admins created for the Organization + } +} message CreateOrganizationResponse{ // The timestamp of the organization was created. @@ -569,7 +598,7 @@ message CreateOrganizationResponse{ ]; // The admins created for the Organization - repeated CreatedAdmin created_admins = 3; + repeated OrganizationAdmin organization_admins = 4; } message UpdateOrganizationRequest {