From 0baaaf8a0528becf8657a5fd76c8c07fb6580fb4 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 3 Jun 2022 14:30:39 +0200 Subject: [PATCH] fix: handle default org id (#3769) --- docs/docs/apis/proto/admin.md | 63 +++++++++++++++++++ docs/docs/apis/proto/management.md | 3 +- internal/api/authz/instance.go | 20 +++--- internal/api/authz/instance_test.go | 4 ++ internal/api/grpc/admin/org.go | 15 +++++ internal/api/grpc/management/iam.go | 3 +- internal/api/grpc/management/org.go | 2 +- .../middleware/instance_interceptor_test.go | 4 ++ .../middleware/instance_interceptor_test.go | 4 ++ internal/api/ui/login/custom_action.go | 9 +-- .../api/ui/login/external_login_handler.go | 50 +++++---------- .../api/ui/login/external_register_handler.go | 23 +++---- internal/api/ui/login/jwt_handler.go | 2 +- internal/api/ui/login/login_handler.go | 7 ++- internal/api/ui/login/register_handler.go | 26 +++----- internal/api/ui/login/renderer.go | 19 ++++-- .../templates/external_not_found_option.html | 2 +- .../templates/external_register_overview.html | 2 +- .../api/ui/login/static/templates/login.html | 4 +- .../ui/login/static/templates/register.html | 2 +- .../eventsourcing/handler/idp_config.go | 4 +- internal/command/instance.go | 45 ++++++++++--- internal/command/instance_model.go | 8 +-- internal/command/main_test.go | 4 ++ internal/command/org.go | 29 +++++++++ internal/iam/model/iam.go | 2 +- internal/query/instance.go | 22 ++++--- internal/query/instance_test.go | 10 +-- internal/query/member_roles.go | 2 +- internal/query/projection/instance.go | 16 ++--- internal/query/projection/instance_test.go | 12 ++-- internal/repository/instance/event_org_set.go | 24 +++---- internal/repository/instance/eventstore.go | 2 +- internal/static/i18n/de.yaml | 2 - internal/static/i18n/en.yaml | 2 - internal/static/i18n/it.yaml | 2 - proto/zitadel/admin.proto | 37 +++++++++++ proto/zitadel/management.proto | 2 + 38 files changed, 331 insertions(+), 158 deletions(-) diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md index a7f99da429..7c0e4bc145 100644 --- a/docs/docs/apis/proto/admin.md +++ b/docs/docs/apis/proto/admin.md @@ -272,6 +272,30 @@ Checks whether an organisation exists by the given parameters GET: /orgs/_is_unique +### SetDefaultOrg + +> **rpc** SetDefaultOrg([SetDefaultOrgRequest](#setdefaultorgrequest)) +[SetDefaultOrgResponse](#setdefaultorgresponse) + +Set the default org + + + + PUT: /orgs/default/{org_id} + + +### GetDefaultOrg + +> **rpc** GetDefaultOrg([GetDefaultOrgRequest](#getdefaultorgrequest)) +[GetDefaultOrgResponse](#getdefaultorgresponse) + +Set the default org + + + + GET: /orgs/default + + ### ListOrgs > **rpc** ListOrgs([ListOrgsRequest](#listorgsrequest)) @@ -1964,6 +1988,23 @@ This is an empty request +### GetDefaultOrgRequest +This is an empty request + + + + +### GetDefaultOrgResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| org | zitadel.org.v1.Org | - | | + + + + ### GetDefaultPasswordResetMessageTextRequest @@ -3256,6 +3297,28 @@ This is an empty request +### SetDefaultOrgRequest + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| org_id | string | - | string.min_len: 1
string.max_len: 200
| + + + + +### SetDefaultOrgResponse + + + +| Field | Type | Description | Validation | +| ----- | ---- | ----------- | ----------- | +| details | zitadel.v1.ObjectDetails | - | | + + + + ### SetDefaultPasswordResetMessageTextRequest diff --git a/docs/docs/apis/proto/management.md b/docs/docs/apis/proto/management.md index 3498aa9aed..44505cc2d7 100644 --- a/docs/docs/apis/proto/management.md +++ b/docs/docs/apis/proto/management.md @@ -4701,8 +4701,9 @@ This is an empty request | Field | Type | Description | Validation | | ----- | ---- | ----------- | ----------- | -| global_org_id | string | - | | +| global_org_id | string | deprecated: use default_org_id instead | | | iam_project_id | string | - | | +| default_org_id | string | - | | diff --git a/internal/api/authz/instance.go b/internal/api/authz/instance.go index dcbd4f090e..7b79030f6b 100644 --- a/internal/api/authz/instance.go +++ b/internal/api/authz/instance.go @@ -18,6 +18,7 @@ type Instance interface { RequestedDomain() string RequestedHost() string DefaultLanguage() language.Tag + DefaultOrganisationID() string } type InstanceVerifier interface { @@ -25,15 +26,16 @@ type InstanceVerifier interface { } type instance struct { - ID string - Domain string + id string + domain string projectID string appID string clientID string + orgID string } func (i *instance) InstanceID() string { - return i.ID + return i.id } func (i *instance) ProjectID() string { @@ -49,17 +51,21 @@ func (i *instance) ConsoleApplicationID() string { } func (i *instance) RequestedDomain() string { - return i.Domain + return i.domain } func (i *instance) RequestedHost() string { - return i.Domain + return i.domain } func (i *instance) DefaultLanguage() language.Tag { return language.Und } +func (i *instance) DefaultOrganisationID() string { + return i.orgID +} + func GetInstance(ctx context.Context) Instance { instance, ok := ctx.Value(instanceKey).(Instance) if !ok { @@ -73,7 +79,7 @@ func WithInstance(ctx context.Context, instance Instance) context.Context { } func WithInstanceID(ctx context.Context, id string) context.Context { - return context.WithValue(ctx, instanceKey, &instance{ID: id}) + return context.WithValue(ctx, instanceKey, &instance{id: id}) } func WithRequestedDomain(ctx context.Context, domain string) context.Context { @@ -82,7 +88,7 @@ func WithRequestedDomain(ctx context.Context, domain string) context.Context { i = new(instance) } - i.Domain = domain + i.domain = domain return context.WithValue(ctx, instanceKey, i) } diff --git a/internal/api/authz/instance_test.go b/internal/api/authz/instance_test.go index 3cbc24e52f..ea06a9464a 100644 --- a/internal/api/authz/instance_test.go +++ b/internal/api/authz/instance_test.go @@ -88,6 +88,10 @@ func (m *mockInstance) DefaultLanguage() language.Tag { return language.English } +func (m *mockInstance) DefaultOrganisationID() string { + return "orgID" +} + func (m *mockInstance) RequestedDomain() string { return "zitadel.cloud" } diff --git a/internal/api/grpc/admin/org.go b/internal/api/grpc/admin/org.go index f3a1ef7818..370d12a915 100644 --- a/internal/api/grpc/admin/org.go +++ b/internal/api/grpc/admin/org.go @@ -17,6 +17,21 @@ func (s *Server) IsOrgUnique(ctx context.Context, req *admin_pb.IsOrgUniqueReque return &admin_pb.IsOrgUniqueResponse{IsUnique: isUnique}, err } +func (s *Server) SetDefaultOrg(ctx context.Context, req *admin_pb.SetDefaultOrgRequest) (*admin_pb.SetDefaultOrgResponse, error) { + details, err := s.command.SetDefaultOrg(ctx, req.OrgId) + if err != nil { + return nil, err + } + return &admin_pb.SetDefaultOrgResponse{ + Details: object.DomainToChangeDetailsPb(details), + }, nil +} + +func (s *Server) GetDefaultOrg(ctx context.Context, _ *admin_pb.GetDefaultOrgRequest) (*admin_pb.GetDefaultOrgResponse, error) { + org, err := s.query.OrgByID(ctx, authz.GetInstance(ctx).DefaultOrganisationID()) + return &admin_pb.GetDefaultOrgResponse{Org: org_grpc.OrgToPb(org)}, err +} + func (s *Server) GetOrgByID(ctx context.Context, req *admin_pb.GetOrgByIDRequest) (*admin_pb.GetOrgByIDResponse, error) { org, err := s.query.OrgByID(ctx, req.Id) if err != nil { diff --git a/internal/api/grpc/management/iam.go b/internal/api/grpc/management/iam.go index 4ca2d518bc..a912d20943 100644 --- a/internal/api/grpc/management/iam.go +++ b/internal/api/grpc/management/iam.go @@ -12,7 +12,8 @@ func (s *Server) GetIAM(ctx context.Context, _ *mgmt_pb.GetIAMRequest) (*mgmt_pb return nil, err } return &mgmt_pb.GetIAMResponse{ - GlobalOrgId: iam.GlobalOrgID, + GlobalOrgId: iam.DefaultOrgID, + DefaultOrgId: iam.DefaultOrgID, IamProjectId: iam.IAMProjectID, }, nil } diff --git a/internal/api/grpc/management/org.go b/internal/api/grpc/management/org.go index 8e045a0049..a8cf3fe272 100644 --- a/internal/api/grpc/management/org.go +++ b/internal/api/grpc/management/org.go @@ -213,7 +213,7 @@ func (s *Server) ListOrgMemberRoles(ctx context.Context, _ *mgmt_pb.ListOrgMembe if err != nil { return nil, err } - roles := s.query.GetOrgMemberRoles(authz.GetCtxData(ctx).OrgID == iam.GlobalOrgID) + roles := s.query.GetOrgMemberRoles(authz.GetCtxData(ctx).OrgID == iam.DefaultOrgID) return &mgmt_pb.ListOrgMemberRolesResponse{ Result: roles, }, nil diff --git a/internal/api/grpc/server/middleware/instance_interceptor_test.go b/internal/api/grpc/server/middleware/instance_interceptor_test.go index e4a73808cc..8074f1fca4 100644 --- a/internal/api/grpc/server/middleware/instance_interceptor_test.go +++ b/internal/api/grpc/server/middleware/instance_interceptor_test.go @@ -182,6 +182,10 @@ func (m *mockInstance) DefaultLanguage() language.Tag { return language.English } +func (m *mockInstance) DefaultOrganisationID() string { + return "orgID" +} + func (m *mockInstance) RequestedDomain() string { return "localhost" } diff --git a/internal/api/http/middleware/instance_interceptor_test.go b/internal/api/http/middleware/instance_interceptor_test.go index 039ac0a1c3..bc5b55bba6 100644 --- a/internal/api/http/middleware/instance_interceptor_test.go +++ b/internal/api/http/middleware/instance_interceptor_test.go @@ -266,6 +266,10 @@ func (m *mockInstance) DefaultLanguage() language.Tag { return language.English } +func (m *mockInstance) DefaultOrganisationID() string { + return "orgID" +} + func (m *mockInstance) RequestedDomain() string { return "zitadel.cloud" } diff --git a/internal/api/ui/login/custom_action.go b/internal/api/ui/login/custom_action.go index b9a171361a..e2b7d35f63 100644 --- a/internal/api/ui/login/custom_action.go +++ b/internal/api/ui/login/custom_action.go @@ -16,12 +16,9 @@ func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.Exte if resourceOwner == "" { resourceOwner = config.AggregateID } - if resourceOwner == authz.GetInstance(ctx).InstanceID() { - iam, err := l.query.Instance(ctx) - if err != nil { - return nil, err - } - resourceOwner = iam.GlobalOrgID + instance := authz.GetInstance(ctx) + if resourceOwner == instance.InstanceID() { + resourceOwner = instance.DefaultOrganisationID() } triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication, resourceOwner) if err != nil { diff --git a/internal/api/ui/login/external_login_handler.go b/internal/api/ui/login/external_login_handler.go index 6186ccf47b..88bf6504e8 100644 --- a/internal/api/ui/login/external_login_handler.go +++ b/internal/api/ui/login/external_login_handler.go @@ -12,6 +12,7 @@ import ( "github.com/zitadel/oidc/v2/pkg/oidc" "golang.org/x/oauth2" + "github.com/zitadel/zitadel/internal/api/authz" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" @@ -204,32 +205,26 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R if errors.IsNotFound(err) { err = nil } - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) - return - } + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() - resourceOwner := iam.GlobalOrgID - - if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { resourceOwner = authReq.RequestedOrgID } orgIAMPolicy, err := l.getOrgDomainPolicy(r, resourceOwner) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) return } human, idpLinking, _ := l.mapExternalUserToLoginUser(orgIAMPolicy, externalUser, idpConfig) if !idpConfig.AutoRegister { - l.renderExternalNotFoundOption(w, r, authReq, iam, orgIAMPolicy, human, idpLinking, err) + l.renderExternalNotFoundOption(w, r, authReq, orgIAMPolicy, human, idpLinking, err) return } authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, iam, orgIAMPolicy, human, idpLinking, err) + l.renderExternalNotFoundOption(w, r, authReq, orgIAMPolicy, human, idpLinking, err) return } l.handleAutoRegister(w, r, authReq) @@ -249,20 +244,15 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R l.renderNextStep(w, r, authReq) } -func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, iam *query.Instance, orgIAMPolicy *query.DomainPolicy, human *domain.Human, externalIDP *domain.UserIDPLink, err error) { +func (l *Login) renderExternalNotFoundOption(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgIAMPolicy *query.DomainPolicy, human *domain.Human, externalIDP *domain.UserIDPLink, err error) { var errID, errMessage string if err != nil { errID, errMessage = l.getErrorMessage(r, err) } if orgIAMPolicy == nil { - iam, err = l.query.Instance(r.Context()) - if err != nil { - l.renderError(w, r, authReq, err) - return - } - resourceOwner := iam.GlobalOrgID + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() - if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { resourceOwner = authReq.RequestedOrgID } @@ -317,7 +307,7 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http data := new(externalNotFoundOptionFormData) authReq, err := l.getAuthRequestAndParseData(r, data) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) return } if data.Link { @@ -327,7 +317,7 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) err = l.authRepo.ResetLinkingUsers(r.Context(), authReq.ID, userAgentID) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) } l.handleLogin(w, r) return @@ -336,29 +326,23 @@ func (l *Login) handleExternalNotFoundOptionCheck(w http.ResponseWriter, r *http } func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest) { - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) - return - } - - resourceOwner := iam.GlobalOrgID + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() memberRoles := []string{domain.RoleSelfManagementGlobal} - if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { memberRoles = nil resourceOwner = authReq.RequestedOrgID } orgIamPolicy, err := l.getOrgDomainPolicy(r, resourceOwner) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) return } idpConfig, err := l.authRepo.GetIDPConfigByID(r.Context(), authReq.SelectedIDPConfigID) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, iam, orgIamPolicy, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, orgIamPolicy, nil, nil, err) return } @@ -371,12 +355,12 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR user, externalIDP, metadata := l.mapExternalUserToLoginUser(orgIamPolicy, linkingUser, idpConfig) user, metadata, err = l.customExternalUserToLoginUserMapping(user, nil, authReq, idpConfig, metadata, resourceOwner) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, iam, orgIamPolicy, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, orgIamPolicy, nil, nil, err) return } err = l.authRepo.AutoRegisterExternalUser(setContext(r.Context(), resourceOwner), user, externalIDP, memberRoles, authReq.ID, userAgentID, resourceOwner, metadata, domain.BrowserInfoFromRequest(r)) if err != nil { - l.renderExternalNotFoundOption(w, r, authReq, iam, orgIamPolicy, user, externalIDP, err) + l.renderExternalNotFoundOption(w, r, authReq, orgIamPolicy, user, externalIDP, err) return } authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) diff --git a/internal/api/ui/login/external_register_handler.go b/internal/api/ui/login/external_register_handler.go index 762100400f..c50c6894ab 100644 --- a/internal/api/ui/login/external_register_handler.go +++ b/internal/api/ui/login/external_register_handler.go @@ -8,6 +8,7 @@ import ( "github.com/zitadel/oidc/v2/pkg/oidc" "golang.org/x/text/language" + "github.com/zitadel/zitadel/internal/api/authz" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/domain" iam_model "github.com/zitadel/zitadel/internal/iam/model" @@ -111,12 +112,7 @@ func (l *Login) handleExternalRegisterCallback(w http.ResponseWriter, r *http.Re } func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, idpConfig *iam_model.IDPConfigView, userAgentID string, tokens *oidc.Tokens) { - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderRegisterOption(w, r, authReq, err) - return - } - resourceOwner := iam.GlobalOrgID + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() if authReq.RequestedOrgID != "" { resourceOwner = authReq.RequestedOrgID } @@ -134,11 +130,11 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques l.renderExternalRegisterOverview(w, r, authReq, orgIamPolicy, user, externalIDP, nil) return } - l.registerExternalUser(w, r, authReq, iam, user, externalIDP) + l.registerExternalUser(w, r, authReq, user, externalIDP) } -func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, iam *query.Instance, user *domain.Human, externalIDP *domain.UserIDPLink) { - resourceOwner := iam.GlobalOrgID +func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, user *domain.Human, externalIDP *domain.UserIDPLink) { + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() memberRoles := []string{domain.RoleSelfManagementGlobal} if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { @@ -204,15 +200,10 @@ func (l *Login) handleExternalRegisterCheck(w http.ResponseWriter, r *http.Reque return } - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderRegisterOption(w, r, authReq, err) - return - } - resourceOwner := iam.GlobalOrgID + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() memberRoles := []string{domain.RoleSelfManagementGlobal} - if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != iam.GlobalOrgID { + if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner { memberRoles = nil resourceOwner = authReq.RequestedOrgID } diff --git a/internal/api/ui/login/jwt_handler.go b/internal/api/ui/login/jwt_handler.go index cebebe5c38..8dd56331c0 100644 --- a/internal/api/ui/login/jwt_handler.go +++ b/internal/api/ui/login/jwt_handler.go @@ -112,7 +112,7 @@ func (l *Login) jwtExtractionUserNotFound(w http.ResponseWriter, r *http.Request err = nil } if !idpConfig.AutoRegister { - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) return } authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, authReq.AgentID) diff --git a/internal/api/ui/login/login_handler.go b/internal/api/ui/login/login_handler.go index 26cfc77e48..18cf3cbaf9 100644 --- a/internal/api/ui/login/login_handler.go +++ b/internal/api/ui/login/login_handler.go @@ -98,10 +98,13 @@ func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *dom data := l.getUserData(r, authReq, "Login", errID, errMessage) funcs := map[string]interface{}{ "hasUsernamePasswordLogin": func() bool { - return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowUsernamePassword + return authReq != nil && authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowUsernamePassword }, "hasExternalLogin": func() bool { - return authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0 + return authReq != nil && authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowExternalIDP && authReq.AllowedExternalIDPs != nil && len(authReq.AllowedExternalIDPs) > 0 + }, + "hasRegistration": func() bool { + return authReq != nil && authReq.LoginPolicy != nil && authReq.LoginPolicy.AllowRegister }, } l.renderer.RenderTemplate(w, r, l.getTranslator(r.Context(), authReq), l.renderer.Templates[tmplLogin], data, funcs) diff --git a/internal/api/ui/login/register_handler.go b/internal/api/ui/login/register_handler.go index 38cb8ba4be..e19611556c 100644 --- a/internal/api/ui/login/register_handler.go +++ b/internal/api/ui/login/register_handler.go @@ -5,6 +5,7 @@ import ( "golang.org/x/text/language" + "github.com/zitadel/zitadel/internal/api/authz" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -61,16 +62,11 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { l.renderRegister(w, r, authRequest, data, err) return } - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderRegister(w, r, authRequest, data, err) - return - } - resourceOwner := iam.GlobalOrgID + resourceOwner := authz.GetInstance(r.Context()).DefaultOrganisationID() memberRoles := []string{domain.RoleSelfManagementGlobal} - if authRequest != nil && authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != iam.GlobalOrgID { + if authRequest != nil && authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != resourceOwner { memberRoles = nil resourceOwner = authRequest.RequestedOrgID } @@ -114,10 +110,6 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque if formData.Language == "" { formData.Language = l.renderer.ReqLang(translator, r).String() } - data := registerData{ - baseData: l.getBaseData(r, authRequest, "Register", errID, errMessage), - registerFormData: *formData, - } var resourceOwner string if authRequest != nil { @@ -125,12 +117,12 @@ func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authReque } if resourceOwner == "" { - iam, err := l.query.Instance(r.Context()) - if err != nil { - l.renderRegister(w, r, authRequest, formData, err) - return - } - resourceOwner = iam.GlobalOrgID + resourceOwner = authz.GetInstance(r.Context()).DefaultOrganisationID() + } + + data := registerData{ + baseData: l.getBaseData(r, authRequest, "Register", errID, errMessage), + registerFormData: *formData, } pwPolicy, description, _ := l.getPasswordComplexityPolicy(r, authRequest, resourceOwner) diff --git a/internal/api/ui/login/renderer.go b/internal/api/ui/login/renderer.go index 0d03d06b1e..bf263e5b9d 100644 --- a/internal/api/ui/login/renderer.go +++ b/internal/api/ui/login/renderer.go @@ -211,6 +211,9 @@ func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage "hasExternalLogin": func() bool { return false }, + "hasRegistration": func() bool { + return true + }, "idpProviderClass": func(stylingType domain.IDPConfigStylingType) string { return stylingType.GetCSSClass() }, @@ -299,7 +302,7 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq * case *domain.LinkUsersStep: l.linkUsers(w, r, authReq, err) case *domain.ExternalNotFoundOptionStep: - l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, nil, err) + l.renderExternalNotFoundOption(w, r, authReq, nil, nil, nil, err) case *domain.ExternalLoginStep: l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID) case *domain.GrantRequiredStep: @@ -346,7 +349,7 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title PrivateLabelingOrgID: l.getPrivateLabelingID(r, authReq), OrgID: l.getOrgID(r, authReq), OrgName: l.getOrgName(authReq), - PrimaryDomain: l.getOrgPrimaryDomain(authReq), + PrimaryDomain: l.getOrgPrimaryDomain(r, authReq), DisplayLoginNameSuffix: l.isDisplayLoginNameSuffix(authReq), AuthReqID: getRequestID(authReq, r), CSRF: csrf.TemplateField(r), @@ -490,11 +493,17 @@ func (l *Login) getOrgName(authReq *domain.AuthRequest) string { return authReq.RequestedOrgName } -func (l *Login) getOrgPrimaryDomain(authReq *domain.AuthRequest) string { - if authReq == nil { +func (l *Login) getOrgPrimaryDomain(r *http.Request, authReq *domain.AuthRequest) string { + orgID := authz.GetInstance(r.Context()).DefaultOrganisationID() + if authReq != nil && authReq.RequestedPrimaryDomain != "" { + return authReq.RequestedPrimaryDomain + } + org, err := l.query.OrgByID(r.Context(), orgID) + if err != nil { + logging.New().WithError(err).Error("cannot get default org") return "" } - return authReq.RequestedPrimaryDomain + return org.Domain } func (l *Login) isDisplayLoginNameSuffix(authReq *domain.AuthRequest) bool { diff --git a/internal/api/ui/login/static/templates/external_not_found_option.html b/internal/api/ui/login/static/templates/external_not_found_option.html index 31ff06632a..837d804f22 100644 --- a/internal/api/ui/login/static/templates/external_not_found_option.html +++ b/internal/api/ui/login/static/templates/external_not_found_option.html @@ -39,7 +39,7 @@
- {{if .DisplayLoginNameSuffix}} + {{if .ShowUsername}} @{{.PrimaryDomain}} {{end}}
diff --git a/internal/api/ui/login/static/templates/external_register_overview.html b/internal/api/ui/login/static/templates/external_register_overview.html index c246654868..c572cd7e94 100644 --- a/internal/api/ui/login/static/templates/external_register_overview.html +++ b/internal/api/ui/login/static/templates/external_register_overview.html @@ -39,7 +39,7 @@
- {{if .DisplayLoginNameSuffix}} + {{if .ShowUsername}} @{{.PrimaryDomain}} {{end}}
diff --git a/internal/api/ui/login/static/templates/login.html b/internal/api/ui/login/static/templates/login.html index d3a2b5e7ca..e6e371d09f 100644 --- a/internal/api/ui/login/static/templates/login.html +++ b/internal/api/ui/login/static/templates/login.html @@ -35,7 +35,7 @@
- {{if .LoginPolicy.AllowRegister}} + {{if hasRegistration}} {{end}}
@@ -60,4 +60,4 @@ -{{template "main-bottom" .}} \ No newline at end of file +{{template "main-bottom" .}} diff --git a/internal/api/ui/login/static/templates/register.html b/internal/api/ui/login/static/templates/register.html index a95ad23a51..e905f68eeb 100644 --- a/internal/api/ui/login/static/templates/register.html +++ b/internal/api/ui/login/static/templates/register.html @@ -42,7 +42,7 @@
- {{if .DisplayLoginNameSuffix}} + {{if .ShowUsername}} @{{.PrimaryDomain}} {{end}}
diff --git a/internal/auth/repository/eventsourcing/handler/idp_config.go b/internal/auth/repository/eventsourcing/handler/idp_config.go index 387daeab2b..b849d91aac 100644 --- a/internal/auth/repository/eventsourcing/handler/idp_config.go +++ b/internal/auth/repository/eventsourcing/handler/idp_config.go @@ -114,7 +114,7 @@ func (i *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - idp, err = i.view.IDPConfigByID(idp.IDPConfigID, idp.InstanceID) + idp, err = i.view.IDPConfigByID(idp.IDPConfigID, event.InstanceID) if err != nil { return err } @@ -125,7 +125,7 @@ func (i *IDPConfig) processIdpConfig(providerType iam_model.IDPProviderType, eve if err != nil { return err } - idp, err = i.view.IDPConfigByID(idp.IDPConfigID, idp.InstanceID) + idp, err = i.view.IDPConfigByID(idp.IDPConfigID, event.InstanceID) if err != nil { return err } diff --git a/internal/command/instance.go b/internal/command/instance.go index f0f95bd2af..4db7158d6f 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -272,6 +272,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str validations = append(validations, AddOrgCommand(ctx, orgAgg, setup.Org.Name), + c.prepareSetDefaultOrg(instanceAgg, orgAgg.ID), AddHumanCommand(userAgg, &setup.Org.Human, c.userPasswordAlg, c.userEncryption), c.AddOrgMemberCommand(orgAgg, userID, domain.RoleOrgOwner), c.AddInstanceMemberCommand(instanceAgg, userID, domain.RoleIAMOwner), @@ -379,6 +380,24 @@ func (c *Commands) SetDefaultLanguage(ctx context.Context, defaultLanguage langu }, nil } +func (c *Commands) SetDefaultOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + validation := c.prepareSetDefaultOrg(instanceAgg, orgID) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation) + if err != nil { + return nil, err + } + events, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err + } + return &domain.ObjectDetails{ + Sequence: events[len(events)-1].Sequence(), + EventDate: events[len(events)-1].CreationDate(), + ResourceOwner: events[len(events)-1].Aggregate().InstanceID, + }, nil +} + func prepareAddInstance(a *instance.Aggregate, instanceName string, defaultLanguage language.Tag) preparation.Validation { return func() (preparation.CreateCommands, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { @@ -412,15 +431,25 @@ func SetIAMConsoleID(a *instance.Aggregate, clientID, appID *string) preparation } } -func (c *Commands) setGlobalOrg(ctx context.Context, iamAgg *eventstore.Aggregate, iamWriteModel *InstanceWriteModel, orgID string) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, iamWriteModel) - if err != nil { - return nil, err +func (c *Commands) prepareSetDefaultOrg(a *instance.Aggregate, orgID string) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if orgID == "" { + return nil, errors.ThrowInvalidArgument(nil, "INST-SWffe", "Errors.Invalid.Argument") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel, err := getInstanceWriteModel(ctx, filter) + if err != nil { + return nil, err + } + if writeModel.DefaultOrgID == orgID { + return nil, errors.ThrowPreconditionFailed(nil, "INST-SDfw2", "Errors.Instance.NotChanged") + } + if exists, err := ExistsOrg(ctx, filter, orgID); err != nil || !exists { + return nil, errors.ThrowPreconditionFailed(err, "INSTA-Wfe21", "Errors.Org.NotFound") + } + return []eventstore.Command{instance.NewDefaultOrgSetEventEvent(ctx, &a.Aggregate, orgID)}, nil + }, nil } - if iamWriteModel.GlobalOrgID != "" { - return nil, errors.ThrowPreconditionFailed(nil, "IAM-HGG24", "Errors.IAM.GlobalOrgAlreadySet") - } - return instance.NewGlobalOrgSetEventEvent(ctx, iamAgg, orgID), nil } func (c *Commands) setIAMProject(ctx context.Context, iamAgg *eventstore.Aggregate, iamWriteModel *InstanceWriteModel, projectID string) (eventstore.Command, error) { diff --git a/internal/command/instance_model.go b/internal/command/instance_model.go index 77e764f5cf..1098a3b092 100644 --- a/internal/command/instance_model.go +++ b/internal/command/instance_model.go @@ -15,7 +15,7 @@ type InstanceWriteModel struct { State domain.InstanceState GeneratedDomain string - GlobalOrgID string + DefaultOrgID string ProjectID string DefaultLanguage language.Tag } @@ -46,8 +46,8 @@ func (wm *InstanceWriteModel) Reduce() error { wm.GeneratedDomain = e.Domain case *instance.ProjectSetEvent: wm.ProjectID = e.ProjectID - case *instance.GlobalOrgSetEvent: - wm.GlobalOrgID = e.OrgID + case *instance.DefaultOrgSetEvent: + wm.DefaultOrgID = e.OrgID case *instance.DefaultLanguageSetEvent: wm.DefaultLanguage = e.Language } @@ -68,7 +68,7 @@ func (wm *InstanceWriteModel) Query() *eventstore.SearchQueryBuilder { instance.InstanceDomainAddedEventType, instance.InstanceDomainRemovedEventType, instance.ProjectSetEventType, - instance.GlobalOrgSetEventType, + instance.DefaultOrgSetEventType, instance.DefaultLanguageSetEventType). Builder() } diff --git a/internal/command/main_test.go b/internal/command/main_test.go index 06391db013..6974878473 100644 --- a/internal/command/main_test.go +++ b/internal/command/main_test.go @@ -233,6 +233,10 @@ func (m *mockInstance) DefaultLanguage() language.Tag { return language.English } +func (m *mockInstance) DefaultOrganisationID() string { + return "orgID" +} + func (m *mockInstance) RequestedDomain() string { return "zitadel.cloud" } diff --git a/internal/command/org.go b/internal/command/org.go index 4248b84cb5..bf7f7a18f6 100644 --- a/internal/command/org.go +++ b/internal/command/org.go @@ -211,6 +211,35 @@ func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.Obj return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil } +func ExistsOrg(ctx context.Context, filter preparation.FilterToQueryReducer, id string) (exists bool, err error) { + events, err := filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(id). + OrderAsc(). + AddQuery(). + AggregateTypes(org.AggregateType). + AggregateIDs(id). + EventTypes( + org.OrgAddedEventType, + org.OrgDeactivatedEventType, + org.OrgReactivatedEventType, + org.OrgRemovedEventType, + ).Builder()) + if err != nil { + return false, err + } + + for _, event := range events { + switch event.(type) { + case *org.OrgAddedEvent, *org.OrgReactivatedEvent: + exists = true + case *org.OrgDeactivatedEvent, *org.OrgRemovedEvent: + exists = false + } + } + + return exists, nil +} + func (c *Commands) setUpOrg( ctx context.Context, organisation *domain.Org, diff --git a/internal/iam/model/iam.go b/internal/iam/model/iam.go index 124f619752..a87276eede 100644 --- a/internal/iam/model/iam.go +++ b/internal/iam/model/iam.go @@ -24,7 +24,7 @@ const ( type IAM struct { es_models.ObjectRoot - GlobalOrgID string + DefaultOrgID string IAMProjectID string SetUpDone domain.Step SetUpStarted domain.Step diff --git a/internal/query/instance.go b/internal/query/instance.go index 3953457225..5b5c05b74a 100644 --- a/internal/query/instance.go +++ b/internal/query/instance.go @@ -39,8 +39,8 @@ var ( name: projection.InstanceColumnSequence, table: instanceTable, } - InstanceColumnGlobalOrgID = Column{ - name: projection.InstanceColumnGlobalOrgID, + InstanceColumnDefaultOrgID = Column{ + name: projection.InstanceColumnDefaultOrgID, table: instanceTable, } InstanceColumnProjectID = Column{ @@ -68,7 +68,7 @@ type Instance struct { Sequence uint64 Name string - GlobalOrgID string + DefaultOrgID string IAMProjectID string ConsoleID string ConsoleAppID string @@ -110,6 +110,10 @@ func (i *Instance) DefaultLanguage() language.Tag { return i.DefaultLang } +func (i *Instance) DefaultOrganisationID() string { + return i.DefaultOrgID +} + type InstanceSearchQueries struct { SearchRequest Queries []SearchQuery @@ -196,7 +200,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta InstanceColumnCreationDate.identifier(), InstanceColumnChangeDate.identifier(), InstanceColumnSequence.identifier(), - InstanceColumnGlobalOrgID.identifier(), + InstanceColumnDefaultOrgID.identifier(), InstanceColumnProjectID.identifier(), InstanceColumnConsoleID.identifier(), InstanceColumnConsoleAppID.identifier(), @@ -211,7 +215,7 @@ func prepareInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Row) (*Insta &instance.CreationDate, &instance.ChangeDate, &instance.Sequence, - &instance.GlobalOrgID, + &instance.DefaultOrgID, &instance.IAMProjectID, &instance.ConsoleID, &instance.ConsoleAppID, @@ -235,7 +239,7 @@ func prepareInstancesQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instances, err InstanceColumnChangeDate.identifier(), InstanceColumnSequence.identifier(), InstanceColumnName.identifier(), - InstanceColumnGlobalOrgID.identifier(), + InstanceColumnDefaultOrgID.identifier(), InstanceColumnProjectID.identifier(), InstanceColumnConsoleID.identifier(), InstanceColumnConsoleAppID.identifier(), @@ -254,7 +258,7 @@ func prepareInstancesQuery() (sq.SelectBuilder, func(*sql.Rows) (*Instances, err &instance.ChangeDate, &instance.Sequence, &instance.Name, - &instance.GlobalOrgID, + &instance.DefaultOrgID, &instance.IAMProjectID, &instance.ConsoleID, &instance.ConsoleAppID, @@ -288,7 +292,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Rows) InstanceColumnChangeDate.identifier(), InstanceColumnSequence.identifier(), InstanceColumnName.identifier(), - InstanceColumnGlobalOrgID.identifier(), + InstanceColumnDefaultOrgID.identifier(), InstanceColumnProjectID.identifier(), InstanceColumnConsoleID.identifier(), InstanceColumnConsoleAppID.identifier(), @@ -324,7 +328,7 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Rows) &instance.ChangeDate, &instance.Sequence, &instance.Name, - &instance.GlobalOrgID, + &instance.DefaultOrgID, &instance.IAMProjectID, &instance.ConsoleID, &instance.ConsoleAppID, diff --git a/internal/query/instance_test.go b/internal/query/instance_test.go index 2f8cfce9b5..9fc79e2a49 100644 --- a/internal/query/instance_test.go +++ b/internal/query/instance_test.go @@ -36,7 +36,7 @@ func Test_InstancePrepares(t *testing.T) { ` projections.instances.creation_date,`+ ` projections.instances.change_date,`+ ` projections.instances.sequence,`+ - ` projections.instances.global_org_id,`+ + ` projections.instances.default_org_id,`+ ` projections.instances.iam_project_id,`+ ` projections.instances.console_client_id,`+ ` projections.instances.console_app_id,`+ @@ -65,7 +65,7 @@ func Test_InstancePrepares(t *testing.T) { ` projections.instances.creation_date,`+ ` projections.instances.change_date,`+ ` projections.instances.sequence,`+ - ` projections.instances.global_org_id,`+ + ` projections.instances.default_org_id,`+ ` projections.instances.iam_project_id,`+ ` projections.instances.console_client_id,`+ ` projections.instances.console_app_id,`+ @@ -76,7 +76,7 @@ func Test_InstancePrepares(t *testing.T) { "creation_date", "change_date", "sequence", - "global_org_id", + "default_org_id", "iam_project_id", "console_client_id", "console_app_id", @@ -100,7 +100,7 @@ func Test_InstancePrepares(t *testing.T) { CreationDate: testNow, ChangeDate: testNow, Sequence: 20211108, - GlobalOrgID: "global-org-id", + DefaultOrgID: "global-org-id", IAMProjectID: "project-id", ConsoleID: "client-id", ConsoleAppID: "app-id", @@ -118,7 +118,7 @@ func Test_InstancePrepares(t *testing.T) { ` projections.instances.creation_date,`+ ` projections.instances.change_date,`+ ` projections.instances.sequence,`+ - ` projections.instances.global_org_id,`+ + ` projections.instances.default_org_id,`+ ` projections.instances.iam_project_id,`+ ` projections.instances.console_client_id,`+ ` projections.instances.console_app_id,`+ diff --git a/internal/query/member_roles.go b/internal/query/member_roles.go index e68a0240c6..27f6dbfbde 100644 --- a/internal/query/member_roles.go +++ b/internal/query/member_roles.go @@ -37,7 +37,7 @@ func (q *Queries) GetProjectMemberRoles(ctx context.Context) ([]string, error) { return nil, err } roles := make([]string, 0) - global := authz.GetCtxData(ctx).OrgID == iam.GlobalOrgID + global := authz.GetCtxData(ctx).OrgID == iam.DefaultOrgID for _, roleMap := range q.zitadelRoles { if strings.HasPrefix(roleMap.Role, "PROJECT") && !strings.HasPrefix(roleMap.Role, "PROJECT_GRANT") { if global && !strings.HasSuffix(roleMap.Role, "GLOBAL") { diff --git a/internal/query/projection/instance.go b/internal/query/projection/instance.go index ff5c972a89..20f471c87f 100644 --- a/internal/query/projection/instance.go +++ b/internal/query/projection/instance.go @@ -17,7 +17,7 @@ const ( InstanceColumnName = "name" InstanceColumnChangeDate = "change_date" InstanceColumnCreationDate = "creation_date" - InstanceColumnGlobalOrgID = "global_org_id" + InstanceColumnDefaultOrgID = "default_org_id" InstanceColumnProjectID = "iam_project_id" InstanceColumnConsoleID = "console_client_id" InstanceColumnConsoleAppID = "console_app_id" @@ -39,7 +39,7 @@ func NewInstanceProjection(ctx context.Context, config crdb.StatementHandlerConf crdb.NewColumn(InstanceColumnName, crdb.ColumnTypeText, crdb.Default("")), crdb.NewColumn(InstanceColumnChangeDate, crdb.ColumnTypeTimestamp), crdb.NewColumn(InstanceColumnCreationDate, crdb.ColumnTypeTimestamp), - crdb.NewColumn(InstanceColumnGlobalOrgID, crdb.ColumnTypeText, crdb.Default("")), + crdb.NewColumn(InstanceColumnDefaultOrgID, crdb.ColumnTypeText, crdb.Default("")), crdb.NewColumn(InstanceColumnProjectID, crdb.ColumnTypeText, crdb.Default("")), crdb.NewColumn(InstanceColumnConsoleID, crdb.ColumnTypeText, crdb.Default("")), crdb.NewColumn(InstanceColumnConsoleAppID, crdb.ColumnTypeText, crdb.Default("")), @@ -63,8 +63,8 @@ func (p *InstanceProjection) reducers() []handler.AggregateReducer { Reduce: p.reduceInstanceAdded, }, { - Event: instance.GlobalOrgSetEventType, - Reduce: p.reduceGlobalOrgSet, + Event: instance.DefaultOrgSetEventType, + Reduce: p.reduceDefaultOrgSet, }, { Event: instance.ProjectSetEventType, @@ -100,17 +100,17 @@ func (p *InstanceProjection) reduceInstanceAdded(event eventstore.Event) (*handl ), nil } -func (p *InstanceProjection) reduceGlobalOrgSet(event eventstore.Event) (*handler.Statement, error) { - e, ok := event.(*instance.GlobalOrgSetEvent) +func (p *InstanceProjection) reduceDefaultOrgSet(event eventstore.Event) (*handler.Statement, error) { + e, ok := event.(*instance.DefaultOrgSetEvent) if !ok { - return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2n9f2", "reduce.wrong.event.type %s", instance.GlobalOrgSetEventType) + return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-2n9f2", "reduce.wrong.event.type %s", instance.DefaultOrgSetEventType) } return crdb.NewUpdateStatement( e, []handler.Column{ handler.NewCol(InstanceColumnChangeDate, e.CreationDate()), handler.NewCol(InstanceColumnSequence, e.Sequence()), - handler.NewCol(InstanceColumnGlobalOrgID, e.OrgID), + handler.NewCol(InstanceColumnDefaultOrgID, e.OrgID), }, []handler.Condition{ handler.NewCond(InstanceColumnID, e.Aggregate().InstanceID), diff --git a/internal/query/projection/instance_test.go b/internal/query/projection/instance_test.go index d0c3e73c6c..72d67c586c 100644 --- a/internal/query/projection/instance_test.go +++ b/internal/query/projection/instance_test.go @@ -51,15 +51,15 @@ func TestInstanceProjection_reduces(t *testing.T) { }, }, { - name: "reduceGlobalOrgSet", + name: "reduceDefaultOrgSet", args: args{ event: getEvent(testEvent( - repository.EventType(instance.GlobalOrgSetEventType), + repository.EventType(instance.DefaultOrgSetEventType), instance.AggregateType, - []byte(`{"globalOrgId": "orgid"}`), - ), instance.GlobalOrgSetMapper), + []byte(`{"orgId": "orgid"}`), + ), instance.DefaultOrgSetMapper), }, - reduce: (&InstanceProjection{}).reduceGlobalOrgSet, + reduce: (&InstanceProjection{}).reduceDefaultOrgSet, want: wantReduce{ projection: InstanceProjectionTable, aggregateType: eventstore.AggregateType("instance"), @@ -68,7 +68,7 @@ func TestInstanceProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.instances SET (change_date, sequence, global_org_id) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.instances SET (change_date, sequence, default_org_id) = ($1, $2, $3) WHERE (id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), diff --git a/internal/repository/instance/event_org_set.go b/internal/repository/instance/event_org_set.go index 505e56f945..f960952c31 100644 --- a/internal/repository/instance/event_org_set.go +++ b/internal/repository/instance/event_org_set.go @@ -11,45 +11,45 @@ import ( ) const ( - GlobalOrgSetEventType eventstore.EventType = "iam.global.org.set" + DefaultOrgSetEventType eventstore.EventType = "instance.default.org.set" ) -type GlobalOrgSetEvent struct { +type DefaultOrgSetEvent struct { eventstore.BaseEvent `json:"-"` - OrgID string `json:"globalOrgId"` + OrgID string `json:"orgId"` } -func (e *GlobalOrgSetEvent) Data() interface{} { +func (e *DefaultOrgSetEvent) Data() interface{} { return e } -func (e *GlobalOrgSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { +func (e *DefaultOrgSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint { return nil } -func NewGlobalOrgSetEventEvent( +func NewDefaultOrgSetEventEvent( ctx context.Context, aggregate *eventstore.Aggregate, orgID string, -) *GlobalOrgSetEvent { - return &GlobalOrgSetEvent{ +) *DefaultOrgSetEvent { + return &DefaultOrgSetEvent{ BaseEvent: *eventstore.NewBaseEventForPush( ctx, aggregate, - GlobalOrgSetEventType, + DefaultOrgSetEventType, ), OrgID: orgID, } } -func GlobalOrgSetMapper(event *repository.Event) (eventstore.Event, error) { - e := &GlobalOrgSetEvent{ +func DefaultOrgSetMapper(event *repository.Event) (eventstore.Event, error) { + e := &DefaultOrgSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), } err := json.Unmarshal(event.Data, e) if err != nil { - return nil, errors.ThrowInternal(err, "IAM-cdFZH", "unable to unmarshal global org set") + return nil, errors.ThrowInternal(err, "IAM-cdFZH", "unable to unmarshal default org set") } return e, nil diff --git a/internal/repository/instance/eventstore.go b/internal/repository/instance/eventstore.go index 8b318964d8..6778e0c503 100644 --- a/internal/repository/instance/eventstore.go +++ b/internal/repository/instance/eventstore.go @@ -5,7 +5,7 @@ import ( ) func RegisterEventMappers(es *eventstore.Eventstore) { - es.RegisterFilterEventMapper(GlobalOrgSetEventType, GlobalOrgSetMapper). + es.RegisterFilterEventMapper(DefaultOrgSetEventType, DefaultOrgSetMapper). RegisterFilterEventMapper(ProjectSetEventType, ProjectSetMapper). RegisterFilterEventMapper(ConsoleSetEventType, ConsoleSetMapper). RegisterFilterEventMapper(DefaultLanguageSetEventType, DefaultLanguageSetMapper). diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml index ee45c5aab0..c4db789bec 100644 --- a/internal/static/i18n/de.yaml +++ b/internal/static/i18n/de.yaml @@ -271,8 +271,6 @@ Errors: MemberAlreadyExisting: Member existiert bereits MemberNotExisting: Member existiert nicht IDMissing: ID fehlt - GlobalOrgMissing: Globale Organisation fehlt - GlobalOrgAlreadySet: Globale Organisation wurde bereits gesetzt IAMProjectIDMissing: IAM Project ID fehlt IamProjectAlreadySet: IAM Project ID wurde bereits gesetzt IdpInvalid: IDP Konfiguration ist ungültig diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index b358cb4233..4e8eba4a6f 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -271,8 +271,6 @@ Errors: MemberAlreadyExisting: Member already exists MemberNotExisting: Member does not exist IDMissing: Id missing - GlobalOrgMissing: Global organization missing - GlobalOrgAlreadySet: Global organization has already been set IAMProjectIDMissing: IAM project id missing IamProjectAlreadySet: IAM project id has already been set IdpInvalid: IDP configuration is invalid diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml index ce3ef52e93..7fbb46c998 100644 --- a/internal/static/i18n/it.yaml +++ b/internal/static/i18n/it.yaml @@ -269,8 +269,6 @@ Errors: MemberAlreadyExisting: Il membro già esistente MemberNotExisting: Il membro non esistente IDMissing: ID mancante - GlobalOrgMissing: Manca un'organizzazione globale - GlobalOrgAlreadySet: L'organizzazione globale è già stata impostata IAMProjectIDMissing: Manca l'ID del progetto IAM IamProjectAlreadySet: L'ID del progetto IAM è già stato impostato IdpInvalid: La configurazione dell'IDP non è valida diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 9bce368512..4254784041 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -423,6 +423,28 @@ service AdminService { }; } + // Set the default org + rpc SetDefaultOrg(SetDefaultOrgRequest) returns (SetDefaultOrgResponse) { + option (google.api.http) = { + put: "/orgs/default/{org_id}"; + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.write"; + }; + } + + // Set the default org + rpc GetDefaultOrg(GetDefaultOrgRequest) returns (GetDefaultOrgResponse) { + option (google.api.http) = { + get: "/orgs/default"; + }; + + option (zitadel.v1.auth_option) = { + permission: "iam.read"; + }; + } + //Returns all organisations matching the request // all queries need to match (AND) rpc ListOrgs(ListOrgsRequest) returns (ListOrgsResponse) { @@ -2569,6 +2591,21 @@ message GetDefaultLanguageResponse { string language = 1; } +message SetDefaultOrgRequest { + string org_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}]; +} + +message SetDefaultOrgResponse { + zitadel.v1.ObjectDetails details = 1; +} + +//This is an empty request +message GetDefaultOrgRequest {} + +message GetDefaultOrgResponse { + zitadel.org.v1.Org org = 1; +} + message ListInstanceDomainsRequest { zitadel.v1.ListQuery query = 1; // the field the result is sorted diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 647bf1ade1..ce0ff6b528 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -2877,8 +2877,10 @@ message GetOIDCInformationResponse { message GetIAMRequest {} message GetIAMResponse { + //deprecated: use default_org_id instead string global_org_id = 1; string iam_project_id = 2; + string default_org_id = 3; } //This is an empty request