feat(api): reworking AddOrganization() API call to return all admins (#9900)

This commit is contained in:
Iraq
2025-06-05 11:05:35 +02:00
committed by GitHub
parent 85e3b7449c
commit 7df4f76f3c
9 changed files with 160 additions and 63 deletions

View File

@@ -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),

View File

@@ -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{

View File

@@ -150,8 +150,8 @@ func Test_createdOrganizationToPb(t *testing.T) {
EventDate: now,
ResourceOwner: "orgID",
},
CreatedAdmins: []*command.CreatedOrgAdmin{
{
OrgAdmins: []command.OrgAdmin{
&command.CreatedOrgAdmin{
ID: "id",
EmailCode: gu.Ptr("emailCode"),
PhoneCode: gu.Ptr("phoneCode"),

View File

@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"github.com/zitadel/zitadel/internal/integration"
"github.com/zitadel/zitadel/pkg/grpc/admin"
v2beta_object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta"
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
user_v2beta "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
@@ -85,6 +84,29 @@ func TestServer_CreateOrganization(t *testing.T) {
},
wantErr: true,
},
{
name: "existing user as admin",
ctx: CTX,
req: &v2beta_org.CreateOrganizationRequest{
Name: gofakeit.AppName(),
Admins: []*v2beta_org.CreateOrganizationRequest_Admin{
{
UserType: &v2beta_org.CreateOrganizationRequest_Admin_UserId{UserId: User.GetUserId()},
},
},
},
want: &v2beta_org.CreateOrganizationResponse{
OrganizationAdmins: []*v2beta_org.OrganizationAdmin{
{
OrganizationAdmin: &v2beta_org.OrganizationAdmin_AssignedAdmin{
AssignedAdmin: &v2beta_org.AssignedAdmin{
UserId: User.GetUserId(),
},
},
},
},
},
},
{
name: "admin with init",
ctx: CTX,
@@ -111,11 +133,15 @@ func TestServer_CreateOrganization(t *testing.T) {
},
want: &v2beta_org.CreateOrganizationResponse{
Id: integration.NotEmpty,
CreatedAdmins: []*v2beta_org.CreatedAdmin{
OrganizationAdmins: []*v2beta_org.OrganizationAdmin{
{
UserId: integration.NotEmpty,
EmailCode: gu.Ptr(integration.NotEmpty),
PhoneCode: nil,
OrganizationAdmin: &v2beta_org.OrganizationAdmin_CreatedAdmin{
CreatedAdmin: &v2beta_org.CreatedAdmin{
UserId: integration.NotEmpty,
EmailCode: gu.Ptr(integration.NotEmpty),
PhoneCode: nil,
},
},
},
},
},
@@ -155,10 +181,21 @@ func TestServer_CreateOrganization(t *testing.T) {
},
},
want: &v2beta_org.CreateOrganizationResponse{
CreatedAdmins: []*v2beta_org.CreatedAdmin{
// a single admin is expected, because the first provided already exists
// OrganizationId: integration.NotEmpty,
OrganizationAdmins: []*v2beta_org.OrganizationAdmin{
{
UserId: integration.NotEmpty,
OrganizationAdmin: &v2beta_org.OrganizationAdmin_AssignedAdmin{
AssignedAdmin: &v2beta_org.AssignedAdmin{
UserId: User.GetUserId(),
},
},
},
{
OrganizationAdmin: &v2beta_org.OrganizationAdmin_CreatedAdmin{
CreatedAdmin: &v2beta_org.CreatedAdmin{
UserId: integration.NotEmpty,
},
},
},
},
},
@@ -192,13 +229,16 @@ func TestServer_CreateOrganization(t *testing.T) {
now := time.Now()
assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute))
// organization id must be the same as the resourceOwner
// check the admins
require.Len(t, got.GetCreatedAdmins(), len(tt.want.GetCreatedAdmins()))
for i, admin := range tt.want.GetCreatedAdmins() {
gotAdmin := got.GetCreatedAdmins()[i]
assertCreatedAdmin(t, admin, gotAdmin)
require.Equal(t, len(tt.want.GetOrganizationAdmins()), len(got.GetOrganizationAdmins()))
for i, admin := range tt.want.GetOrganizationAdmins() {
gotAdmin := got.GetOrganizationAdmins()[i].OrganizationAdmin
switch admin := admin.OrganizationAdmin.(type) {
case *v2beta_org.OrganizationAdmin_CreatedAdmin:
assertCreatedAdmin(t, admin.CreatedAdmin, gotAdmin.(*v2beta_org.OrganizationAdmin_CreatedAdmin).CreatedAdmin)
case *v2beta_org.OrganizationAdmin_AssignedAdmin:
assert.Equal(t, admin.AssignedAdmin.GetUserId(), gotAdmin.(*v2beta_org.OrganizationAdmin_AssignedAdmin).AssignedAdmin.GetUserId())
}
}
})
}
@@ -472,8 +512,8 @@ func TestServer_ListOrganizations(t *testing.T) {
ctx: listOrgIAmOwnerCtx,
query: []*v2beta_org.OrganizationSearchFilter{
{
Filter: &org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &org.OrgDomainFilter{
Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &v2beta_org.OrgDomainFilter{
Domain: func() string {
listOrgRes, err := listOrgClient.ListOrganizations(listOrgIAmOwnerCtx, &v2beta_org.ListOrganizationsRequest{
Filter: []*v2beta_org.OrganizationSearchFilter{
@@ -507,8 +547,8 @@ func TestServer_ListOrganizations(t *testing.T) {
ctx: listOrgIAmOwnerCtx,
query: []*v2beta_org.OrganizationSearchFilter{
{
Filter: &org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &org.OrgDomainFilter{
Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &v2beta_org.OrgDomainFilter{
Domain: func() string {
domain := strings.ToLower(strings.ReplaceAll(orgsName[1][1:len(orgsName[1])-2], " ", "-"))
return domain
@@ -530,8 +570,8 @@ func TestServer_ListOrganizations(t *testing.T) {
ctx: listOrgIAmOwnerCtx,
query: []*v2beta_org.OrganizationSearchFilter{
{
Filter: &org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &org.OrgDomainFilter{
Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{
DomainFilter: &v2beta_org.OrgDomainFilter{
Domain: func() string {
domain := strings.ToUpper(strings.ReplaceAll(orgsName[1][1:len(orgsName[1])-2], " ", "-"))
return domain
@@ -1374,7 +1414,7 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
req: &v2beta_org.GenerateOrganizationDomainValidationRequest{
OrganizationId: orgId,
Domain: domain,
Type: org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
Type: v2beta_org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
},
},
{
@@ -1383,7 +1423,7 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
req: &v2beta_org.GenerateOrganizationDomainValidationRequest{
OrganizationId: "non existent org id",
Domain: domain,
Type: org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
Type: v2beta_org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
},
// BUG: this should be 'organization does not exist'
err: errors.New("Domain doesn't exist on organization"),
@@ -1394,7 +1434,7 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
req: &v2beta_org.GenerateOrganizationDomainValidationRequest{
OrganizationId: orgId,
Domain: domain,
Type: org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS,
Type: v2beta_org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS,
},
},
{
@@ -1403,7 +1443,7 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
req: &v2beta_org.GenerateOrganizationDomainValidationRequest{
OrganizationId: "non existent org id",
Domain: domain,
Type: org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS,
Type: v2beta_org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS,
},
// BUG: this should be 'organization does not exist'
err: errors.New("Domain doesn't exist on organization"),
@@ -1414,7 +1454,7 @@ func TestServer_ValidateOrganizationDomain(t *testing.T) {
req: &v2beta_org.GenerateOrganizationDomainValidationRequest{
OrganizationId: orgId,
Domain: "non existent domain",
Type: org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
Type: v2beta_org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP,
},
err: errors.New("Domain doesn't exist on organization"),
},

View File

@@ -150,8 +150,8 @@ func Test_createdOrganizationToPb(t *testing.T) {
EventDate: now,
ResourceOwner: "orgID",
},
CreatedAdmins: []*command.CreatedOrgAdmin{
{
OrgAdmins: []command.OrgAdmin{
&command.CreatedOrgAdmin{
ID: "id",
EmailCode: gu.Ptr("emailCode"),
PhoneCode: gu.Ptr("phoneCode"),
@@ -162,11 +162,15 @@ func Test_createdOrganizationToPb(t *testing.T) {
want: &org.CreateOrganizationResponse{
CreationDate: timestamppb.New(now),
Id: "orgID",
CreatedAdmins: []*org.CreatedAdmin{
OrganizationAdmins: []*org.OrganizationAdmin{
{
UserId: "id",
EmailCode: gu.Ptr("emailCode"),
PhoneCode: gu.Ptr("phoneCode"),
OrganizationAdmin: &org.OrganizationAdmin_CreatedAdmin{
CreatedAdmin: &org.CreatedAdmin{
UserId: "id",
EmailCode: gu.Ptr("emailCode"),
PhoneCode: gu.Ptr("phoneCode"),
},
},
},
},
},

View File

@@ -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,14 +204,15 @@ 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 {
users = append(users, &AssignedOrgAdmin{ID: admin.ID})
continue
}
if admin.Human != nil {

View File

@@ -1531,8 +1531,8 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "orgID",
},
CreatedAdmins: []*CreatedOrgAdmin{
{
OrgAdmins: []OrgAdmin{
&CreatedOrgAdmin{
ID: "userID",
},
},
@@ -1574,7 +1574,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "custom-org-ID",
},
CreatedAdmins: []*CreatedOrgAdmin{},
OrgAdmins: []OrgAdmin{},
},
},
},
@@ -1641,7 +1641,11 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "orgID",
},
CreatedAdmins: []*CreatedOrgAdmin{},
OrgAdmins: []OrgAdmin{
&AssignedOrgAdmin{
ID: "userID",
},
},
},
},
},
@@ -1751,8 +1755,8 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
ObjectDetails: &domain.ObjectDetails{
ResourceOwner: "orgID",
},
CreatedAdmins: []*CreatedOrgAdmin{
{
OrgAdmins: []OrgAdmin{
&CreatedOrgAdmin{
ID: "userID",
PAT: &PersonalAccessToken{
ObjectRoot: models.ObjectRoot{

View File

@@ -558,6 +558,18 @@ message CreatedAdmin {
optional string phone_code = 3;
}
message AssignedAdmin {
string user_id = 1;
}
message OrganizationAdmin {
// The admins created/assigned for the Organization.
oneof OrganizationAdmin {
CreatedAdmin created_admin = 1;
AssignedAdmin assigned_admin = 2;
}
}
message CreateOrganizationResponse{
// The timestamp of the organization was created.
google.protobuf.Timestamp creation_date = 1 [
@@ -577,8 +589,8 @@ message CreateOrganizationResponse{
}
];
// The admins created for the Organization
repeated CreatedAdmin created_admins = 3;
// The admins created/assigned for the Organization
repeated OrganizationAdmin organization_admins = 3;
}
message UpdateOrganizationRequest {
@@ -939,3 +951,5 @@ message DeleteOrganizationMetadataResponse{
}
];
}