chore: forward port organization v2 API changes (#11207)

# Which Problems Are Solved

Recent changes to the v2beta and v2 API of the organization service were
directly merged to the `next` branch and release on v4.x, since the
ongoing move to the relation table, blocked it from being merged into
main.

# How the Problems Are Solved

This PR ports the following changes into main:
- fix(api): correct permission check in organization v2beta service:
https://github.com/zitadel/zitadel/commit/8dcfff97ed52a8b9fc77ecb1f972744f42cff3ed
- fix(actions v1): return org metadata again
(https://github.com/zitadel/zitadel/pull/11040)
- feat(api): move organization api
(https://github.com/zitadel/zitadel/pull/11045)

# Additional Changes

None

# Additional Context

relates to #10772

---------

Co-authored-by: Gayathri Vijayan <[email protected]>
This commit is contained in:
Livio Spring
2026-01-05 14:14:01 +00:00
committed by GitHub
co-authored by Gayathri Vijayan
parent 4c8a668585
commit 5f34d1af8f
36 changed files with 4617 additions and 1117 deletions
+1 -1
View File
@@ -529,7 +529,7 @@ func startAPIs(
if err := apis.RegisterService(ctx, settings_v2.CreateServer(config.SystemDefaults, commands, queries, permissionCheck)); err != nil {
return nil, err
}
if err := apis.RegisterService(ctx, org_v2.CreateServer(commands, queries, permissionCheck)); err != nil {
if err := apis.RegisterService(ctx, org_v2.CreateServer(config.SystemDefaults, commands, queries, permissionCheck)); err != nil {
return nil, err
}
if err := apis.RegisterService(ctx, feature_v2.CreateServer(commands, queries)); err != nil {
+1 -10
View File
@@ -366,7 +366,7 @@ module.exports = {
},
org_v2: {
specPath:
".artifacts/openapi/zitadel/org/v2/org_service.swagger.json",
".artifacts/openapi3/zitadel/org/v2/org_service.openapi.yaml",
outputDir: "docs/apis/resources/org_service_v2",
sidebarOptions: {
groupPathsBy: "tag",
@@ -382,15 +382,6 @@ module.exports = {
categoryLinkSource: "auto",
},
},
org_v2beta: {
specPath:
".artifacts/openapi3/zitadel/org/v2beta/org_service.openapi.yaml",
outputDir: "docs/apis/resources/org_service_v2beta",
sidebarOptions: {
groupPathsBy: "tag",
categoryLinkSource: "auto",
},
},
project_v2: {
specPath:
".artifacts/openapi3/zitadel/project/v2/project_service.openapi.yaml",
-13
View File
@@ -11,7 +11,6 @@ const sidebar_api_saml_service_v2 = require("./docs/apis/resources/saml_service_
const sidebar_api_settings_service_v2 = require("./docs/apis/resources/settings_service_v2/sidebar.ts").default
const sidebar_api_feature_service_v2 = require("./docs/apis/resources/feature_service_v2/sidebar.ts").default
const sidebar_api_org_service_v2 = require("./docs/apis/resources/org_service_v2/sidebar.ts").default
const sidebar_api_org_service_v2beta = require("./docs/apis/resources/org_service_v2beta/sidebar.ts").default
const sidebar_api_idp_service_v2 = require("./docs/apis/resources/idp_service_v2/sidebar.ts").default
const sidebar_api_actions_v2 = require("./docs/apis/resources/action_service_v2/sidebar.ts").default
const sidebar_api_project_service_v2 = require("./docs/apis/resources/project_service_v2/sidebar.ts").default
@@ -784,18 +783,6 @@ module.exports = {
},
items: sidebar_api_org_service_v2,
},
{
type: "category",
label: "Organization (Beta)",
link: {
type: "generated-index",
title: "Organization Service Beta API",
slug: "/apis/resources/org_service/v2beta",
description:
"This beta API is intended to manage organizations for ZITADEL. Expect breaking changes to occur. Please use the v2 version for a stable API. \n",
},
items: sidebar_api_org_service_v2beta,
},
{
type: "category",
label: "Identity Provider",
+1
View File
@@ -85,6 +85,7 @@ func GetOrganizationMetadata(ctx context.Context, queries *query.Queries, c *act
organizationID,
&query.OrgMetadataSearchQueries{},
false,
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
+1 -1
View File
@@ -290,7 +290,7 @@ func (s *Server) getDomains(ctx context.Context, orgID string) (_ []*org_pb.Doma
if err != nil {
return nil, err
}
orgDomainsQuery, err := s.query.SearchOrgDomains(ctx, &query.OrgDomainSearchQueries{Queries: []query.SearchQuery{orgDomainOrgIDQuery}}, false)
orgDomainsQuery, err := s.query.SearchOrgDomains(ctx, &query.OrgDomainSearchQueries{Queries: []query.SearchQuery{orgDomainOrgIDQuery}}, false, false)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -885,7 +885,7 @@ func importOrgDomains(ctx context.Context, s *Server, errors *[]*admin_pb.Import
Verified: domainR.IsVerified,
Primary: domainR.IsPrimary,
}
_, err := s.command.AddOrgDomain(ctx, org.GetOrgId(), domainR.DomainName, []string{})
_, err := s.command.AddOrgDomain(ctx, org.GetOrgId(), domainR.DomainName, []string{}, nil)
if err != nil {
*errors = append(*errors, &admin_pb.ImportDataError{Type: "domain", Id: org.GetOrgId() + "_" + domainR.DomainName, Message: errorToImportError(err)})
if isCtxTimeout(ctx) {
+2 -2
View File
@@ -28,7 +28,7 @@ func (s *Server) SetDefaultOrg(ctx context.Context, req *admin_pb.SetDefaultOrgR
}
func (s *Server) RemoveOrg(ctx context.Context, req *admin_pb.RemoveOrgRequest) (*admin_pb.RemoveOrgResponse, error) {
details, err := s.command.RemoveOrg(ctx, req.OrgId)
details, err := s.command.RemoveOrg(ctx, req.OrgId, nil, true)
if err != nil {
return nil, err
}
@@ -87,7 +87,7 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*
Roles: req.Roles,
},
},
}, true, userIDs...)
}, true, nil, userIDs...)
if err != nil {
return nil, err
}
+12 -12
View File
@@ -98,7 +98,7 @@ func (s *Server) AddOrg(ctx context.Context, req *mgmt_pb.AddOrgRequest) (*mgmt_
func (s *Server) UpdateOrg(ctx context.Context, req *mgmt_pb.UpdateOrgRequest) (*mgmt_pb.UpdateOrgResponse, error) {
ctxData := authz.GetCtxData(ctx)
org, err := s.command.ChangeOrg(ctx, ctxData.OrgID, req.Name)
org, err := s.command.ChangeOrg(ctx, ctxData.OrgID, req.Name, nil)
if err != nil {
return nil, err
}
@@ -112,7 +112,7 @@ func (s *Server) UpdateOrg(ctx context.Context, req *mgmt_pb.UpdateOrgRequest) (
}
func (s *Server) DeactivateOrg(ctx context.Context, req *mgmt_pb.DeactivateOrgRequest) (*mgmt_pb.DeactivateOrgResponse, error) {
objectDetails, err := s.command.DeactivateOrg(ctx, authz.GetCtxData(ctx).OrgID)
objectDetails, err := s.command.DeactivateOrg(ctx, authz.GetCtxData(ctx).OrgID, nil)
if err != nil {
return nil, err
}
@@ -122,7 +122,7 @@ func (s *Server) DeactivateOrg(ctx context.Context, req *mgmt_pb.DeactivateOrgRe
}
func (s *Server) ReactivateOrg(ctx context.Context, req *mgmt_pb.ReactivateOrgRequest) (*mgmt_pb.ReactivateOrgResponse, error) {
objectDetails, err := s.command.ReactivateOrg(ctx, authz.GetCtxData(ctx).OrgID)
objectDetails, err := s.command.ReactivateOrg(ctx, authz.GetCtxData(ctx).OrgID, nil)
if err != nil {
return nil, err
}
@@ -132,7 +132,7 @@ func (s *Server) ReactivateOrg(ctx context.Context, req *mgmt_pb.ReactivateOrgRe
}
func (s *Server) RemoveOrg(ctx context.Context, req *mgmt_pb.RemoveOrgRequest) (*mgmt_pb.RemoveOrgResponse, error) {
details, err := s.command.RemoveOrg(ctx, authz.GetCtxData(ctx).OrgID)
details, err := s.command.RemoveOrg(ctx, authz.GetCtxData(ctx).OrgID, nil, true)
if err != nil {
return nil, err
}
@@ -171,7 +171,7 @@ func (s *Server) ListOrgDomains(ctx context.Context, req *mgmt_pb.ListOrgDomains
}
queries.Queries = append(queries.Queries, orgIDQuery)
domains, err := s.query.SearchOrgDomains(ctx, queries, false)
domains, err := s.query.SearchOrgDomains(ctx, queries, false, false)
if err != nil {
return nil, err
}
@@ -187,7 +187,7 @@ func (s *Server) AddOrgDomain(ctx context.Context, req *mgmt_pb.AddOrgDomainRequ
if err != nil {
return nil, err
}
details, err := s.command.AddOrgDomain(ctx, orgID, req.Domain, userIDs)
details, err := s.command.AddOrgDomain(ctx, orgID, req.Domain, userIDs, nil)
if err != nil {
return nil, err
}
@@ -197,7 +197,7 @@ func (s *Server) AddOrgDomain(ctx context.Context, req *mgmt_pb.AddOrgDomainRequ
}
func (s *Server) RemoveOrgDomain(ctx context.Context, req *mgmt_pb.RemoveOrgDomainRequest) (*mgmt_pb.RemoveOrgDomainResponse, error) {
details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req))
details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req), nil)
if err != nil {
return nil, err
}
@@ -207,7 +207,7 @@ func (s *Server) RemoveOrgDomain(ctx context.Context, req *mgmt_pb.RemoveOrgDoma
}
func (s *Server) GenerateOrgDomainValidation(ctx context.Context, req *mgmt_pb.GenerateOrgDomainValidationRequest) (*mgmt_pb.GenerateOrgDomainValidationResponse, error) {
token, url, err := s.command.GenerateOrgDomainValidation(ctx, GenerateOrgDomainValidationRequestToDomain(ctx, req))
token, url, err := s.command.GenerateOrgDomainValidation(ctx, GenerateOrgDomainValidationRequestToDomain(ctx, req), nil)
if err != nil {
return nil, err
}
@@ -232,7 +232,7 @@ func (s *Server) ValidateOrgDomain(ctx context.Context, req *mgmt_pb.ValidateOrg
if err != nil {
return nil, err
}
details, err := s.command.ValidateOrgDomain(ctx, ValidateOrgDomainRequestToDomain(ctx, req), userIDs)
details, err := s.command.ValidateOrgDomain(ctx, ValidateOrgDomainRequestToDomain(ctx, req), userIDs, nil)
if err != nil {
return nil, err
}
@@ -324,7 +324,7 @@ func (s *Server) ListOrgMetadata(ctx context.Context, req *mgmt_pb.ListOrgMetada
if err != nil {
return nil, err
}
res, err := s.query.SearchOrgMetadata(ctx, true, authz.GetCtxData(ctx).OrgID, metadataQueries, false)
res, err := s.query.SearchOrgMetadata(ctx, true, authz.GetCtxData(ctx).OrgID, metadataQueries, false, false)
if err != nil {
return nil, err
}
@@ -359,7 +359,7 @@ func (s *Server) SetOrgMetadata(ctx context.Context, req *mgmt_pb.SetOrgMetadata
}
func (s *Server) BulkSetOrgMetadata(ctx context.Context, req *mgmt_pb.BulkSetOrgMetadataRequest) (*mgmt_pb.BulkSetOrgMetadataResponse, error) {
result, err := s.command.BulkSetOrgMetadata(ctx, authz.GetCtxData(ctx).OrgID, BulkSetOrgMetadataToDomain(req)...)
result, err := s.command.BulkSetOrgMetadata(ctx, authz.GetCtxData(ctx).OrgID, nil, BulkSetOrgMetadataToDomain(req)...)
if err != nil {
return nil, err
}
@@ -379,7 +379,7 @@ func (s *Server) RemoveOrgMetadata(ctx context.Context, req *mgmt_pb.RemoveOrgMe
}
func (s *Server) BulkRemoveOrgMetadata(ctx context.Context, req *mgmt_pb.BulkRemoveOrgMetadataRequest) (*mgmt_pb.BulkRemoveOrgMetadataResponse, error) {
result, err := s.command.BulkRemoveOrgMetadata(ctx, authz.GetCtxData(ctx).OrgID, req.Keys...)
result, err := s.command.BulkRemoveOrgMetadata(ctx, authz.GetCtxData(ctx).OrgID, nil, req.Keys...)
if err != nil {
return nil, err
}
+37
View File
@@ -45,3 +45,40 @@ func UserMetadataFilterToQuery(filter *meta_pb.MetadataSearchFilter) (query.Sear
return nil, zerrors.ThrowInvalidArgument(nil, "METAD-fdg23", "List.Query.Invalid")
}
}
func OrgMetadataListToPb(dataList []*query.OrgMetadata) []*meta_pb.Metadata {
mds := make([]*meta_pb.Metadata, len(dataList))
for i, data := range dataList {
mds[i] = OrgMetadataToPb(data)
}
return mds
}
func OrgMetadataToPb(data *query.OrgMetadata) *meta_pb.Metadata {
return &meta_pb.Metadata{
Key: data.Key,
Value: data.Value,
CreationDate: timestamppb.New(data.CreationDate),
ChangeDate: timestamppb.New(data.ChangeDate),
}
}
func OrgMetadataQueriesToQuery(queries []*meta_pb.MetadataSearchFilter) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i], err = OrgMetadataFilterToQuery(query)
if err != nil {
return nil, err
}
}
return q, nil
}
func OrgMetadataFilterToQuery(filter *meta_pb.MetadataSearchFilter) (query.SearchQuery, error) {
switch q := filter.Filter.(type) {
case *meta_pb.MetadataSearchFilter_KeyFilter:
return query.NewOrgMetadataKeySearchQuery(q.KeyFilter.Key, filter_v2.TextMethodPbToQuery(q.KeyFilter.Method))
default:
return nil, zerrors.ThrowInvalidArgument(nil, "METAD-fdg23", "List.Query.Invalid")
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+170 -1
View File
@@ -4,10 +4,13 @@ import (
"context"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
"github.com/zitadel/zitadel/internal/api/grpc/user/v2"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/zerrors"
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
)
@@ -17,13 +20,123 @@ func (s *Server) AddOrganization(ctx context.Context, request *connect.Request[o
if err != nil {
return nil, err
}
createdOrg, err := s.command.SetUpOrg(ctx, orgSetup, false)
createdOrg, err := s.command.SetUpOrg(ctx, orgSetup, false, s.command.CheckPermissionOrganizationCreate)
if err != nil {
return nil, err
}
return createdOrganizationToPb(createdOrg)
}
func (s *Server) UpdateOrganization(ctx context.Context, request *connect.Request[org.UpdateOrganizationRequest]) (*connect.Response[org.UpdateOrganizationResponse], error) {
organization, err := s.command.ChangeOrg(ctx, request.Msg.GetOrganizationId(), request.Msg.GetName(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.UpdateOrganizationResponse{
ChangeDate: timestamppb.New(organization.EventDate),
}), nil
}
func (s *Server) DeleteOrganization(ctx context.Context, request *connect.Request[org.DeleteOrganizationRequest]) (*connect.Response[org.DeleteOrganizationResponse], error) {
details, err := s.command.RemoveOrg(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationDelete, false)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.DeleteOrganizationResponse{
DeletionDate: timestamppb.New(details.EventDate),
}), nil
}
func (s *Server) SetOrganizationMetadata(ctx context.Context, request *connect.Request[org.SetOrganizationMetadataRequest]) (*connect.Response[org.SetOrganizationMetadataResponse], error) {
result, err := s.command.BulkSetOrgMetadata(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite, bulkSetOrgMetadataToDomain(request.Msg)...)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.SetOrganizationMetadataResponse{
SetDate: timestamppb.New(result.EventDate),
}), nil
}
func (s *Server) DeleteOrganizationMetadata(ctx context.Context, request *connect.Request[org.DeleteOrganizationMetadataRequest]) (*connect.Response[org.DeleteOrganizationMetadataResponse], error) {
result, err := s.command.BulkRemoveOrgMetadata(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite, request.Msg.Keys...)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.DeleteOrganizationMetadataResponse{
DeletionDate: timestamppb.New(result.EventDate),
}), nil
}
func (s *Server) DeactivateOrganization(ctx context.Context, request *connect.Request[org.DeactivateOrganizationRequest]) (*connect.Response[org.DeactivateOrganizationResponse], error) {
objectDetails, err := s.command.DeactivateOrg(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.DeactivateOrganizationResponse{
ChangeDate: timestamppb.New(objectDetails.EventDate),
}), nil
}
func (s *Server) ActivateOrganization(ctx context.Context, request *connect.Request[org.ActivateOrganizationRequest]) (*connect.Response[org.ActivateOrganizationResponse], error) {
objectDetails, err := s.command.ReactivateOrg(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.ActivateOrganizationResponse{
ChangeDate: timestamppb.New(objectDetails.EventDate),
}), err
}
func (s *Server) AddOrganizationDomain(ctx context.Context, request *connect.Request[org.AddOrganizationDomainRequest]) (*connect.Response[org.AddOrganizationDomainResponse], error) {
userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, request.Msg.GetDomain(), request.Msg.GetOrganizationId())
if err != nil {
return nil, err
}
details, err := s.command.AddOrgDomain(ctx, request.Msg.GetOrganizationId(), request.Msg.GetDomain(), userIDs, s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.AddOrganizationDomainResponse{
CreationDate: timestamppb.New(details.EventDate),
}), nil
}
func (s *Server) DeleteOrganizationDomain(ctx context.Context, req *connect.Request[org.DeleteOrganizationDomainRequest]) (*connect.Response[org.DeleteOrganizationDomainResponse], error) {
details, err := s.command.RemoveOrgDomain(ctx, removeOrgDomainRequestToDomain(req.Msg), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.DeleteOrganizationDomainResponse{
DeletionDate: timestamppb.New(details.EventDate),
}), err
}
func (s *Server) GenerateOrganizationDomainValidation(ctx context.Context, req *connect.Request[org.GenerateOrganizationDomainValidationRequest]) (*connect.Response[org.GenerateOrganizationDomainValidationResponse], error) {
token, url, err := s.command.GenerateOrgDomainValidation(ctx, generateOrgDomainValidationRequestToDomain(req.Msg), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.GenerateOrganizationDomainValidationResponse{
Token: token,
Url: url,
}), nil
}
func (s *Server) VerifyOrganizationDomain(ctx context.Context, request *connect.Request[org.VerifyOrganizationDomainRequest]) (*connect.Response[org.VerifyOrganizationDomainResponse], error) {
userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, request.Msg.GetDomain(), request.Msg.GetOrganizationId())
if err != nil {
return nil, err
}
details, err := s.command.ValidateOrgDomain(ctx, validateOrgDomainRequestToDomain(request.Msg), userIDs, s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.VerifyOrganizationDomainResponse{
ChangeDate: timestamppb.New(details.EventDate),
}), nil
}
func addOrganizationRequestToCommand(request *org.AddOrganizationRequest) (*command.OrgSetup, error) {
admins, err := addOrganizationRequestAdminsToCommand(request.GetAdmins())
if err != nil {
@@ -88,3 +201,59 @@ func createdOrganizationToPb(createdOrg *command.CreatedOrg) (_ *connect.Respons
CreatedAdmins: admins,
}), nil
}
func (s *Server) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgDomain, orgID string) ([]string, error) {
return s.query.SearchClaimedUserIDsOfOrgDomain(ctx, orgDomain, orgID)
}
func bulkSetOrgMetadataToDomain(req *org.SetOrganizationMetadataRequest) []*domain.Metadata {
metadata := make([]*domain.Metadata, len(req.Metadata))
for i, data := range req.Metadata {
metadata[i] = &domain.Metadata{
Key: data.Key,
Value: data.Value,
}
}
return metadata
}
func removeOrgDomainRequestToDomain(req *org.DeleteOrganizationDomainRequest) *domain.OrgDomain {
return &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: req.OrganizationId,
},
Domain: req.Domain,
}
}
func generateOrgDomainValidationRequestToDomain(req *org.GenerateOrganizationDomainValidationRequest) *domain.OrgDomain {
return &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: req.OrganizationId,
},
Domain: req.Domain,
ValidationType: domainValidationTypeToDomain(req.Type),
}
}
func domainValidationTypeToDomain(validationType org.DomainValidationType) domain.OrgDomainValidationType {
switch validationType {
case org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP:
return domain.OrgDomainValidationTypeHTTP
case org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS:
return domain.OrgDomainValidationTypeDNS
case org.DomainValidationType_DOMAIN_VALIDATION_TYPE_UNSPECIFIED:
return domain.OrgDomainValidationTypeUnspecified
default:
return domain.OrgDomainValidationTypeUnspecified
}
}
func validateOrgDomainRequestToDomain(req *org.VerifyOrganizationDomainRequest) *domain.OrgDomain {
return &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: req.OrganizationId,
},
Domain: req.Domain,
}
}
+151
View File
@@ -7,10 +7,14 @@ import (
orgv2 "github.com/zitadel/zitadel/backend/v3/api/org/v2"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
"github.com/zitadel/zitadel/internal/api/grpc/metadata/v2"
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
filter_pb "github.com/zitadel/zitadel/pkg/grpc/filter/v2"
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
)
@@ -33,6 +37,81 @@ func (s *Server) ListOrganizations(ctx context.Context, req *connect.Request[org
}), nil
}
func (s *Server) ListOrganizationMetadata(ctx context.Context, request *connect.Request[org.ListOrganizationMetadataRequest]) (*connect.Response[org.ListOrganizationMetadataResponse], error) {
metadataQueries, err := listOrgMetadataToDomain(s.systemDefaults, request.Msg)
if err != nil {
return nil, err
}
res, err := s.query.SearchOrgMetadata(ctx, true, request.Msg.GetOrganizationId(), metadataQueries, false, true)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.ListOrganizationMetadataResponse{
Metadata: metadata.OrgMetadataListToPb(res.Metadata),
Pagination: &filter_pb.PaginationResponse{
TotalResult: res.Count,
AppliedLimit: uint64(request.Msg.GetPagination().GetLimit()),
},
}), nil
}
func (s *Server) ListOrganizationDomains(ctx context.Context, req *connect.Request[org.ListOrganizationDomainsRequest]) (*connect.Response[org.ListOrganizationDomainsResponse], error) {
queries, err := listOrgDomainsRequestToDomain(s.systemDefaults, req.Msg)
if err != nil {
return nil, err
}
orgIDQuery, err := query.NewOrgDomainOrgIDSearchQuery(req.Msg.GetOrganizationId())
if err != nil {
return nil, err
}
queries.Queries = append(queries.Queries, orgIDQuery)
domains, err := s.query.SearchOrgDomains(ctx, queries, false, true)
if err != nil {
return nil, err
}
return connect.NewResponse(&org.ListOrganizationDomainsResponse{
Domains: domainsToPb(domains.Domains),
Pagination: &filter_pb.PaginationResponse{
TotalResult: domains.Count,
AppliedLimit: uint64(req.Msg.GetPagination().GetLimit()),
},
}), nil
}
func listOrgDomainsRequestToDomain(systemDefaults systemdefaults.SystemDefaults, request *org.ListOrganizationDomainsRequest) (*query.OrgDomainSearchQueries, error) {
offset, limit, asc, err := filter.PaginationPbToQuery(systemDefaults, request.Pagination)
if err != nil {
return nil, err
}
queries, err := DomainQueriesToModel(request.Filters)
if err != nil {
return nil, err
}
return &query.OrgDomainSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: fieldNameToOrganizationDomainColumn(request.GetSortingColumn()),
},
Queries: queries,
}, nil
}
func fieldNameToOrganizationDomainColumn(column org.DomainFieldName) query.Column {
switch column {
case org.DomainFieldName_DOMAIN_FIELD_NAME_NAME:
return query.OrgDomainDomainCol
case org.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE:
return query.OrgDomainCreationDateCol
case org.DomainFieldName_DOMAIN_FIELD_NAME_UNSPECIFIED:
return query.Column{}
default:
return query.Column{}
}
}
func listOrgRequestToModel(ctx context.Context, req *connect.Request[org.ListOrganizationsRequest]) (*connect.Response[query.OrgSearchQueries], error) {
offset, limit, asc := object.ListQueryToQuery(req.Msg.Query)
queries, err := orgQueriesToQuery(ctx, req.Msg.Queries)
@@ -112,6 +191,8 @@ func fieldNameToOrganizationColumn(fieldName org.OrganizationFieldName) query.Co
switch fieldName {
case org.OrganizationFieldName_ORGANIZATION_FIELD_NAME_NAME:
return query.OrgColumnName
case org.OrganizationFieldName_ORGANIZATION_FIELD_NAME_CREATION_DATE:
return query.OrgColumnCreationDate
case org.OrganizationFieldName_ORGANIZATION_FIELD_NAME_UNSPECIFIED:
return query.Column{}
default:
@@ -141,3 +222,73 @@ func organizationToPb(organization *query.Org) *org.Organization {
State: orgStateToPb(organization.State),
}
}
func listOrgMetadataToDomain(systemDefaults systemdefaults.SystemDefaults, request *org.ListOrganizationMetadataRequest) (*query.OrgMetadataSearchQueries, error) {
offset, limit, asc, err := filter.PaginationPbToQuery(systemDefaults, request.Pagination)
if err != nil {
return nil, err
}
queries, err := metadata.OrgMetadataQueriesToQuery(request.GetFilters())
if err != nil {
return nil, err
}
return &query.OrgMetadataSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
},
Queries: queries,
}, nil
}
func DomainQueriesToModel(queries []*org.DomainSearchFilter) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i], err = DomainQueryToModel(query)
if err != nil {
return nil, err
}
}
return q, nil
}
func DomainQueryToModel(searchQuery *org.DomainSearchFilter) (query.SearchQuery, error) {
switch q := searchQuery.Filter.(type) {
case *org.DomainSearchFilter_DomainFilter:
return query.NewOrgDomainDomainSearchQuery(object.TextMethodToQuery(q.DomainFilter.Method), q.DomainFilter.GetDomain())
default:
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-Ags89", "List.Query.Invalid")
}
}
func domainsToPb(domains []*query.Domain) []*org.Domain {
d := make([]*org.Domain, len(domains))
for i, domain := range domains {
d[i] = domainToPb(domain)
}
return d
}
func domainToPb(d *query.Domain) *org.Domain {
return &org.Domain{
OrganizationId: d.OrgID,
Domain: d.Domain,
IsVerified: d.IsVerified,
IsPrimary: d.IsPrimary,
ValidationType: domainValidationTypeToPb(d.ValidationType),
}
}
func domainValidationTypeToPb(validationType domain.OrgDomainValidationType) org.DomainValidationType {
switch validationType {
case domain.OrgDomainValidationTypeDNS:
return org.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS
case domain.OrgDomainValidationTypeHTTP:
return org.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP
case domain.OrgDomainValidationTypeUnspecified:
return org.DomainValidationType_DOMAIN_VALIDATION_TYPE_UNSPECIFIED
default:
return org.DomainValidationType_DOMAIN_VALIDATION_TYPE_UNSPECIFIED
}
}
+4 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/server"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/pkg/grpc/org/v2"
@@ -18,19 +19,20 @@ import (
var _ orgconnect.OrganizationServiceHandler = (*Server)(nil)
type Server struct {
systemDefaults systemdefaults.SystemDefaults
command *command.Commands
query *query.Queries
checkPermission domain.PermissionCheck
}
type Config struct{}
func CreateServer(
systemDefaults systemdefaults.SystemDefaults,
command *command.Commands,
query *query.Queries,
checkPermission domain.PermissionCheck,
) *Server {
return &Server{
systemDefaults: systemDefaults,
command: command,
query: query,
checkPermission: checkPermission,
File diff suppressed because it is too large Load Diff
+13 -18
View File
@@ -2,7 +2,6 @@ package org
import (
"context"
"errors"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/timestamppb"
@@ -25,7 +24,7 @@ func (s *Server) CreateOrganization(ctx context.Context, request *connect.Reques
if err != nil {
return nil, err
}
createdOrg, err := s.command.SetUpOrg(ctx, orgSetup, false)
createdOrg, err := s.command.SetUpOrg(ctx, orgSetup, false, s.command.CheckPermissionOrganizationCreate)
if err != nil {
return nil, err
}
@@ -37,7 +36,7 @@ func (s *Server) UpdateOrganization(ctx context.Context, request *connect.Reques
return orgv2.UpdateOrganizationBeta(ctx, request)
}
org, err := s.command.ChangeOrg(ctx, request.Msg.GetId(), request.Msg.GetName())
org, err := s.command.ChangeOrg(ctx, request.Msg.GetId(), request.Msg.GetName(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -74,12 +73,8 @@ func (s *Server) DeleteOrganization(ctx context.Context, request *connect.Reques
return orgv2.DeleteOrganizationBeta(ctx, request)
}
details, err := s.command.RemoveOrg(ctx, request.Msg.GetId())
details, err := s.command.RemoveOrg(ctx, request.Msg.GetId(), s.command.CheckPermissionOrganizationDelete, false)
if err != nil {
var notFoundError *zerrors.NotFoundError
if errors.As(err, &notFoundError) {
return connect.NewResponse(&v2beta_org.DeleteOrganizationResponse{}), nil
}
return nil, err
}
return connect.NewResponse(&v2beta_org.DeleteOrganizationResponse{
@@ -88,7 +83,7 @@ func (s *Server) DeleteOrganization(ctx context.Context, request *connect.Reques
}
func (s *Server) SetOrganizationMetadata(ctx context.Context, request *connect.Request[v2beta_org.SetOrganizationMetadataRequest]) (*connect.Response[v2beta_org.SetOrganizationMetadataResponse], error) {
result, err := s.command.BulkSetOrgMetadata(ctx, request.Msg.GetOrganizationId(), BulkSetOrgMetadataToDomain(request.Msg)...)
result, err := s.command.BulkSetOrgMetadata(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite, BulkSetOrgMetadataToDomain(request.Msg)...)
if err != nil {
return nil, err
}
@@ -102,7 +97,7 @@ func (s *Server) ListOrganizationMetadata(ctx context.Context, request *connect.
if err != nil {
return nil, err
}
res, err := s.query.SearchOrgMetadata(ctx, true, request.Msg.GetOrganizationId(), metadataQueries, false)
res, err := s.query.SearchOrgMetadata(ctx, true, request.Msg.GetOrganizationId(), metadataQueries, false, true)
if err != nil {
return nil, err
}
@@ -116,7 +111,7 @@ func (s *Server) ListOrganizationMetadata(ctx context.Context, request *connect.
}
func (s *Server) DeleteOrganizationMetadata(ctx context.Context, request *connect.Request[v2beta_org.DeleteOrganizationMetadataRequest]) (*connect.Response[v2beta_org.DeleteOrganizationMetadataResponse], error) {
result, err := s.command.BulkRemoveOrgMetadata(ctx, request.Msg.GetOrganizationId(), request.Msg.Keys...)
result, err := s.command.BulkRemoveOrgMetadata(ctx, request.Msg.GetOrganizationId(), s.command.CheckPermissionOrganizationWrite, request.Msg.Keys...)
if err != nil {
return nil, err
}
@@ -130,7 +125,7 @@ func (s *Server) DeactivateOrganization(ctx context.Context, request *connect.Re
return orgv2.DeactivateOrganizationBeta(ctx, request)
}
objectDetails, err := s.command.DeactivateOrg(ctx, request.Msg.GetId())
objectDetails, err := s.command.DeactivateOrg(ctx, request.Msg.GetId(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -144,7 +139,7 @@ func (s *Server) ActivateOrganization(ctx context.Context, request *connect.Requ
return orgv2.ActivateOrganizationBeta(ctx, request)
}
objectDetails, err := s.command.ReactivateOrg(ctx, request.Msg.GetId())
objectDetails, err := s.command.ReactivateOrg(ctx, request.Msg.GetId(), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -158,7 +153,7 @@ func (s *Server) AddOrganizationDomain(ctx context.Context, request *connect.Req
if err != nil {
return nil, err
}
details, err := s.command.AddOrgDomain(ctx, request.Msg.GetOrganizationId(), request.Msg.GetDomain(), userIDs)
details, err := s.command.AddOrgDomain(ctx, request.Msg.GetOrganizationId(), request.Msg.GetDomain(), userIDs, s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -178,7 +173,7 @@ func (s *Server) ListOrganizationDomains(ctx context.Context, req *connect.Reque
}
queries.Queries = append(queries.Queries, orgIDQuery)
domains, err := s.query.SearchOrgDomains(ctx, queries, false)
domains, err := s.query.SearchOrgDomains(ctx, queries, false, true)
if err != nil {
return nil, err
}
@@ -192,7 +187,7 @@ func (s *Server) ListOrganizationDomains(ctx context.Context, req *connect.Reque
}
func (s *Server) DeleteOrganizationDomain(ctx context.Context, req *connect.Request[org.DeleteOrganizationDomainRequest]) (*connect.Response[org.DeleteOrganizationDomainResponse], error) {
details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req.Msg))
details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req.Msg), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -202,7 +197,7 @@ func (s *Server) DeleteOrganizationDomain(ctx context.Context, req *connect.Requ
}
func (s *Server) GenerateOrganizationDomainValidation(ctx context.Context, req *connect.Request[org.GenerateOrganizationDomainValidationRequest]) (*connect.Response[org.GenerateOrganizationDomainValidationResponse], error) {
token, url, err := s.command.GenerateOrgDomainValidation(ctx, GenerateOrgDomainValidationRequestToDomain(ctx, req.Msg))
token, url, err := s.command.GenerateOrgDomainValidation(ctx, GenerateOrgDomainValidationRequestToDomain(ctx, req.Msg), s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
@@ -217,7 +212,7 @@ func (s *Server) VerifyOrganizationDomain(ctx context.Context, request *connect.
if err != nil {
return nil, err
}
details, err := s.command.ValidateOrgDomain(ctx, ValidateOrgDomainRequestToDomain(ctx, request.Msg), userIDs)
details, err := s.command.ValidateOrgDomain(ctx, ValidateOrgDomainRequestToDomain(ctx, request.Msg), userIDs, s.command.CheckPermissionOrganizationWrite)
if err != nil {
return nil, err
}
+3
View File
@@ -131,6 +131,7 @@ func (l *Login) runPostExternalAuthenticationActions(
resourceOwner,
&query.OrgMetadataSearchQueries{},
false,
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
@@ -326,6 +327,7 @@ func (l *Login) runPreCreationActions(
resourceOwner,
&query.OrgMetadataSearchQueries{},
false,
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
@@ -402,6 +404,7 @@ func (l *Login) runPostCreationActions(
resourceOwner,
&query.OrgMetadataSearchQueries{},
false,
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
@@ -105,7 +105,7 @@ func (l *Login) passLoginHintToRegistration(r *http.Request, authReq *domain.Aut
logging.WithFields("authRequest", authReq.ID, "org", authReq.RequestedOrgID).Error("unable to search query for registration loginHint")
return data
}
domains, err := l.query.SearchOrgDomains(r.Context(), &query.OrgDomainSearchQueries{Queries: []query.SearchQuery{searchQuery}}, false)
domains, err := l.query.SearchOrgDomains(r.Context(), &query.OrgDomainSearchQueries{Queries: []query.SearchQuery{searchQuery}}, false, false)
if err != nil {
logging.WithFields("authRequest", authReq.ID, "org", authReq.RequestedOrgID).Error("unable to load domains for registration loginHint")
return data
@@ -84,7 +84,7 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
l.renderRegisterOrg(w, r, authRequest, data, err)
return
}
_, err = l.command.SetUpOrg(ctx, data.toCommandOrg(), true, userIDs...)
_, err = l.command.SetUpOrg(ctx, data.toCommandOrg(), true, nil, userIDs...)
if err != nil {
l.renderRegisterOrg(w, r, authRequest, data, err)
return
+11
View File
@@ -255,6 +255,17 @@ func newMockProjectPermissionCheckSAMLNotAllowed() domain.ProjectPermissionCheck
}
}
func newMockOrganizationPermissionCheckNotAllowed() OrganizationPermissionCheck {
return func(ctx context.Context, organizationID string) (err error) {
return zerrors.ThrowPermissionDenied(nil, "", "Errors.PermissionDenied")
}
}
func newMockOrganizationPermissionCheckAllowed() OrganizationPermissionCheck {
return func(ctx context.Context, organizationID string) (err error) {
return nil
}
}
func newMockTokenVerifierValid() func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return func(ctx context.Context, sessionToken, sessionID, tokenID string) (err error) {
return nil
+51 -16
View File
@@ -181,7 +181,7 @@ func (c *orgSetupCommands) setupOrgAdminMachine(orgAgg *org.Aggregate, machine *
func (c *orgSetupCommands) addCustomDomain(domain string, userIDs []string) error {
if domain != "" {
c.validations = append(c.validations, c.commands.prepareAddOrgDomain(c.aggregate, domain, userIDs))
c.validations = append(c.validations, c.commands.prepareAddOrgDomain(c.aggregate, domain, userIDs, nil))
}
return nil
}
@@ -263,7 +263,7 @@ func (c *orgSetupCommands) createdMachineAdmin(admin *OrgSetupAdmin) *CreatedOrg
return createdAdmin
}
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, allowInitialMail bool, userIDs ...string) (*CreatedOrg, error) {
func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, allowInitialMail bool, permissionCheck OrganizationPermissionCheck, userIDs ...string) (*CreatedOrg, error) {
if err := o.Validate(); err != nil {
return nil, err
}
@@ -276,6 +276,12 @@ func (c *Commands) SetUpOrg(ctx context.Context, o *OrgSetup, allowInitialMail b
}
}
if permissionCheck != nil {
if err := permissionCheck(ctx, o.OrgID); err != nil {
return nil, err
}
}
// because users can choose their own ID, we must check that an org with the same ID does not already exist
existingOrg, err := c.getOrgWriteModelByID(ctx, o.OrgID)
if err != nil {
@@ -394,14 +400,18 @@ func (c *Commands) addOrgWithIDAndMember(ctx context.Context, name, userID, reso
return orgWriteModelToOrg(addedOrg), nil
}
func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.ObjectDetails, error) {
func (c *Commands) ChangeOrg(ctx context.Context, organizationID, name string, permissionCheck OrganizationPermissionCheck) (*domain.ObjectDetails, error) {
name = strings.TrimSpace(name)
orgID = strings.TrimSpace(orgID)
if orgID == "" || name == "" {
organizationID = strings.TrimSpace(organizationID)
if organizationID == "" || name == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
if permissionCheck != nil {
if err := permissionCheck(ctx, organizationID); err != nil {
return nil, err
}
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, organizationID)
if err != nil {
return nil, err
}
@@ -414,7 +424,7 @@ func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.O
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
events := make([]eventstore.Command, 0)
events = append(events, org.NewOrgChangedEvent(ctx, orgAgg, orgWriteModel.Name, name))
changeDomainEvents, err := c.changeDefaultDomain(ctx, orgID, name)
changeDomainEvents, err := c.changeDefaultDomain(ctx, organizationID, name)
if err != nil {
return nil, err
}
@@ -432,8 +442,15 @@ func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.O
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) DeactivateOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
type OrganizationPermissionCheck func(ctx context.Context, organizationID string) error
func (c *Commands) DeactivateOrg(ctx context.Context, organizationID string, permissionCheck OrganizationPermissionCheck) (*domain.ObjectDetails, error) {
if permissionCheck != nil {
if err := permissionCheck(ctx, organizationID); err != nil {
return nil, err
}
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, organizationID)
if err != nil {
return nil, err
}
@@ -455,8 +472,13 @@ func (c *Commands) DeactivateOrg(ctx context.Context, orgID string) (*domain.Obj
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
func (c *Commands) ReactivateOrg(ctx context.Context, organizationID string, permissionCheck OrganizationPermissionCheck) (*domain.ObjectDetails, error) {
if permissionCheck != nil {
if err := permissionCheck(ctx, organizationID); err != nil {
return nil, err
}
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, organizationID)
if err != nil {
return nil, err
}
@@ -478,13 +500,17 @@ func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.Obj
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) RemoveOrg(ctx context.Context, id string) (*domain.ObjectDetails, error) {
func (c *Commands) RemoveOrg(ctx context.Context, id string, permissionCheck OrganizationPermissionCheck, mustExist bool) (*domain.ObjectDetails, error) {
orgAgg := org.NewAggregate(id)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareRemoveOrg(orgAgg))
//nolint:staticcheck
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareRemoveOrg(orgAgg, permissionCheck, mustExist))
if err != nil {
return nil, err
}
if len(cmds) == 0 {
return &domain.ObjectDetails{}, nil
}
events, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
@@ -498,9 +524,15 @@ func (c *Commands) RemoveOrg(ctx context.Context, id string) (*domain.ObjectDeta
}, nil
}
func (c *Commands) prepareRemoveOrg(a *org.Aggregate) preparation.Validation {
//nolint:gocognit
func (c *Commands) prepareRemoveOrg(a *org.Aggregate, permissionCheck OrganizationPermissionCheck, mustExist bool) preparation.Validation {
return func() (preparation.CreateCommands, error) {
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
if permissionCheck != nil {
if err := permissionCheck(ctx, a.ID); err != nil {
return nil, err
}
}
instance := authz.GetInstance(ctx)
if a.ID == instance.DefaultOrganisationID() {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMA-wG9p1", "Errors.Org.DefaultOrgNotDeletable")
@@ -520,7 +552,10 @@ func (c *Commands) prepareRemoveOrg(a *org.Aggregate) preparation.Validation {
return nil, zerrors.ThrowPreconditionFailed(err, "COMMA-wG9p1", "Errors.Org.NotFound")
}
if !isOrgStateExists(writeModel.State) {
return nil, zerrors.ThrowNotFound(nil, "COMMA-aps2n", "Errors.Org.NotFound")
if mustExist {
return nil, zerrors.ThrowNotFound(nil, "COMMA-aps2n", "Errors.Org.NotFound")
}
return nil, nil
}
domainPolicy, err := domainPolicyWriteModel(ctx, filter, a.ID)
+32 -6
View File
@@ -20,7 +20,8 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userIDs []string) preparation.Validation {
//nolint:gocognit
func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userIDs []string, permissionCheck OrganizationPermissionCheck) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if addDomain = strings.TrimSpace(addDomain); addDomain == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-r3h4J", "Errors.Invalid.Argument")
@@ -29,6 +30,15 @@ func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userI
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if permissionCheck != nil {
if err := permissionCheck(ctx, a.ID); err != nil {
return nil, err
}
}
if err := c.checkOrgExists(ctx, a.ID); err != nil {
return nil, err
}
existing, err := orgDomain(ctx, filter, a.ID, addDomain)
if err != nil && !errors.Is(err, zerrors.ThrowNotFound(nil, "", "")) {
return nil, err
@@ -123,12 +133,13 @@ func (c *Commands) VerifyOrgDomain(ctx context.Context, orgID, domain string) (_
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) AddOrgDomain(ctx context.Context, orgID, domain string, claimedUserIDs []string) (_ *domain.ObjectDetails, err error) {
func (c *Commands) AddOrgDomain(ctx context.Context, orgID, domain string, claimedUserIDs []string, permissionCheck OrganizationPermissionCheck) (_ *domain.ObjectDetails, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
orgAgg := org.NewAggregate(orgID)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgDomain(orgAgg, domain, claimedUserIDs))
//nolint:staticcheck
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgDomain(orgAgg, domain, claimedUserIDs, permissionCheck))
if err != nil {
return nil, err
}
@@ -139,7 +150,7 @@ func (c *Commands) AddOrgDomain(ctx context.Context, orgID, domain string, claim
return pushedEventsToObjectDetails(pushedEvents), nil
}
func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *domain.OrgDomain) (token, url string, err error) {
func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *domain.OrgDomain, permissionCheck OrganizationPermissionCheck) (token, url string, err error) {
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
return "", "", zerrors.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
}
@@ -147,6 +158,11 @@ func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *d
if !ok {
return "", "", zerrors.ThrowInvalidArgument(nil, "ORG-Gsw31", "Errors.Org.DomainVerificationTypeInvalid")
}
if permissionCheck != nil {
if err := permissionCheck(ctx, orgDomain.AggregateID); err != nil {
return "", "", err
}
}
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
if err != nil {
return "", "", err
@@ -177,10 +193,15 @@ func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *d
return token, url, nil
}
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs []string) (*domain.ObjectDetails, error) {
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs []string, permissionCheck OrganizationPermissionCheck) (*domain.ObjectDetails, error) {
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
}
if permissionCheck != nil {
if err := permissionCheck(ctx, orgDomain.AggregateID); err != nil {
return nil, err
}
}
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
if err != nil {
return nil, err
@@ -261,10 +282,15 @@ func (c *Commands) SetPrimaryOrgDomain(ctx context.Context, orgDomain *domain.Or
return writeModelToObjectDetails(&domainWriteModel.WriteModel), nil
}
func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.ObjectDetails, error) {
func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, permissionCheck OrganizationPermissionCheck) (*domain.ObjectDetails, error) {
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-SJsK3", "Errors.Org.InvalidDomain")
}
if permissionCheck != nil {
if err := permissionCheck(ctx, orgDomain.AggregateID); err != nil {
return nil, err
}
}
domainWriteModel, err := c.getOrgDomainWriteModel(ctx, orgDomain.AggregateID, orgDomain.Domain)
if err != nil {
return nil, err
+410 -67
View File
@@ -23,11 +23,13 @@ import (
func TestAddDomain(t *testing.T) {
type args struct {
a *org.Aggregate
domain string
claimedUserIDs []string
idGenerator id.Generator
filter preparation.FilterToQueryReducer
a *org.Aggregate
domain string
claimedUserIDs []string
idGenerator id.Generator
filter preparation.FilterToQueryReducer
eventstore func(*testing.T) *eventstore.Eventstore
permissionCheck OrganizationPermissionCheck
}
agg := org.NewAggregate("test")
@@ -40,8 +42,9 @@ func TestAddDomain(t *testing.T) {
{
name: "invalid domain",
args: args{
a: agg,
domain: "",
a: agg,
domain: "",
eventstore: expectEventstore(),
},
want: Want{
ValidationErr: zerrors.ThrowInvalidArgument(nil, "ORG-r3h4J", "Errors.Invalid.Argument"),
@@ -58,6 +61,14 @@ func TestAddDomain(t *testing.T) {
org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true),
}, nil
},
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
),
},
want: Want{
Commands: []eventstore.Command{
@@ -99,6 +110,14 @@ func TestAddDomain(t *testing.T) {
return []eventstore.Event{org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, false, false)}, nil
}
}(),
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
),
},
want: Want{
Commands: []eventstore.Command{
@@ -121,18 +140,67 @@ func TestAddDomain(t *testing.T) {
org.NewDomainVerifiedEvent(ctx, &agg.Aggregate, "domain"),
}, nil
},
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
),
},
want: Want{
CreateErr: zerrors.ThrowAlreadyExists(nil, "", ""),
},
},
{
name: "no permission",
args: args{
a: agg,
domain: "domain",
claimedUserIDs: []string{"userID1"},
filter: nil,
eventstore: expectEventstore(),
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
want: Want{
CreateErr: zerrors.ThrowPermissionDenied(nil, "", "Errors.PermissionDenied"),
},
},
{
name: "correct with permission check",
args: args{
a: agg,
domain: "domain",
claimedUserIDs: []string{"userID1"},
filter: func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{
org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true),
}, nil
},
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
),
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
want: Want{
Commands: []eventstore.Command{
org.NewDomainAddedEvent(context.Background(), &agg.Aggregate, "domain"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
AssertValidation(
t,
http.WithRequestedHost(context.Background(), "domain"),
(&Commands{idGenerator: tt.args.idGenerator}).prepareAddOrgDomain(tt.args.a, tt.args.domain, tt.args.claimedUserIDs),
(&Commands{idGenerator: tt.args.idGenerator, eventstore: tt.args.eventstore(t)}).prepareAddOrgDomain(tt.args.a, tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck),
tt.args.filter,
tt.want,
)
@@ -278,13 +346,14 @@ func TestSetDomainPrimary(t *testing.T) {
func TestCommandSide_AddOrgDomain(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
orgID string
domain string
claimedUserIDs []string
ctx context.Context
orgID string
domain string
claimedUserIDs []string
permissionCheck OrganizationPermissionCheck
}
type res struct {
want *domain.ObjectDetails
@@ -299,9 +368,7 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
{
name: "invalid domain, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -313,8 +380,13 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
{
name: "domain already exists, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -343,8 +415,13 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
{
name: "domain add, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -382,13 +459,76 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
},
},
},
{
name: "domain add (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"name",
),
),
),
expectFilter(
eventFromEventPusher(
org.NewDomainPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
domain: "domain.ch",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "no permission, error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
domain: "domain.ch",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.AddOrgDomain(tt.args.ctx, tt.args.orgID, tt.args.domain, tt.args.claimedUserIDs)
got, err := r.AddOrgDomain(tt.args.ctx, tt.args.orgID, tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -404,12 +544,13 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
ctx context.Context
domain *domain.OrgDomain
ctx context.Context
domain *domain.OrgDomain
permissionCheck OrganizationPermissionCheck
}
type res struct {
wantToken string
@@ -425,9 +566,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "invalid domain, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -444,9 +583,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "missing aggregateid, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -461,9 +598,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "invalid validation type, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -481,8 +616,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "domain not exists, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -510,8 +644,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "domain already verified, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -551,8 +684,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "add dns validation, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -601,8 +733,7 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
{
name: "add http validation, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -648,14 +779,85 @@ func TestCommandSide_GenerateOrgDomainValidation(t *testing.T) {
wantURL: "https://domain.ch/.well-known/zitadel-challenge/a.txt",
},
},
{
name: "add validation (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"name",
),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
),
expectPush(
org.NewDomainVerificationAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
domain.OrgDomainValidationTypeHTTP,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("a"),
},
),
),
),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
ValidationType: domain.OrgDomainValidationTypeHTTP,
},
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{
wantToken: "a",
wantURL: "https://domain.ch/.well-known/zitadel-challenge/a.txt",
},
},
{
name: "add validation (no permission), error",
fields: fields{
eventstore: expectEventstore(),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
ValidationType: domain.OrgDomainValidationTypeHTTP,
},
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
domainVerificationGenerator: tt.fields.secretGenerator,
}
token, url, err := r.GenerateOrgDomainValidation(tt.args.ctx, tt.args.domain)
token, url, err := r.GenerateOrgDomainValidation(tt.args.ctx, tt.args.domain, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -679,9 +881,10 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
domainValidationFunc func(domain, token, verifier string, checkType http.CheckType) error
}
type args struct {
ctx context.Context
domain *domain.OrgDomain
claimedUserIDs []string
ctx context.Context
domain *domain.OrgDomain
claimedUserIDs []string
permissionCheck OrganizationPermissionCheck
}
type res struct {
want *domain.ObjectDetails
@@ -1172,6 +1375,86 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
},
},
},
{
name: "domain verification (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"name",
),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
eventFromEventPusher(
org.NewDomainVerificationAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
domain.OrgDomainValidationTypeDNS,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("a"),
},
),
),
),
expectPush(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
domainValidationFunc: validDomainVerification,
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
ValidationType: domain.OrgDomainValidationTypeDNS,
},
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "domain verification (no permission), error",
fields: fields{
eventstore: expectEventstore(),
alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
domainValidationFunc: validDomainVerification,
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
ValidationType: domain.OrgDomainValidationTypeDNS,
},
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -1182,7 +1465,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
domainVerificationValidator: tt.fields.domainValidationFunc,
idGenerator: tt.fields.idGenerator,
}
got, err := r.ValidateOrgDomain(http.WithRequestedHost(tt.args.ctx, "zitadel.ch"), tt.args.domain, tt.args.claimedUserIDs)
got, err := r.ValidateOrgDomain(http.WithRequestedHost(tt.args.ctx, "zitadel.ch"), tt.args.domain, tt.args.claimedUserIDs, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -1383,11 +1666,12 @@ func TestCommandSide_SetPrimaryDomain(t *testing.T) {
func TestCommandSide_RemoveOrgDomain(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
domain *domain.OrgDomain
ctx context.Context
domain *domain.OrgDomain
permissionCheck OrganizationPermissionCheck
}
type res struct {
want *domain.ObjectDetails
@@ -1402,9 +1686,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "invalid domain, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -1421,9 +1703,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "missing aggregateid, error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -1438,8 +1718,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "domain not exists, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -1467,8 +1746,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "remove verified domain, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -1513,8 +1791,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "remove domain, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -1555,8 +1832,7 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
{
name: "remove verified domain, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -1600,13 +1876,80 @@ func TestCommandSide_RemoveOrgDomain(t *testing.T) {
},
},
},
{
name: "remove verified domain (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"name",
),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch",
),
),
),
expectPush(
org.NewDomainRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"domain.ch", true,
),
),
),
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
},
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "remove verified domain (no permission), error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
domain: &domain.OrgDomain{
ObjectRoot: models.ObjectRoot{
AggregateID: "org1",
},
Domain: "domain.ch",
},
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.RemoveOrgDomain(tt.args.ctx, tt.args.domain)
got, err := r.RemoveOrgDomain(tt.args.ctx, tt.args.domain, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
+12 -2
View File
@@ -32,10 +32,15 @@ func (c *Commands) SetOrgMetadata(ctx context.Context, orgID string, metadata *d
return writeModelToOrgMetadata(setMetadata), nil
}
func (c *Commands) BulkSetOrgMetadata(ctx context.Context, orgID string, metadatas ...*domain.Metadata) (_ *domain.ObjectDetails, err error) {
func (c *Commands) BulkSetOrgMetadata(ctx context.Context, orgID string, permissionCheck OrganizationPermissionCheck, metadatas ...*domain.Metadata) (_ *domain.ObjectDetails, err error) {
if len(metadatas) == 0 {
return nil, zerrors.ThrowPreconditionFailed(nil, "META-9mm2d", "Errors.Metadata.NoData")
}
if permissionCheck != nil {
if err := permissionCheck(ctx, orgID); err != nil {
return nil, err
}
}
err = c.checkOrgExists(ctx, orgID)
if err != nil {
return nil, err
@@ -108,10 +113,15 @@ func (c *Commands) RemoveOrgMetadata(ctx context.Context, orgID, metadataKey str
return writeModelToObjectDetails(&removeMetadata.WriteModel), nil
}
func (c *Commands) BulkRemoveOrgMetadata(ctx context.Context, orgID string, metadataKeys ...string) (_ *domain.ObjectDetails, err error) {
func (c *Commands) BulkRemoveOrgMetadata(ctx context.Context, orgID string, permissionCheck OrganizationPermissionCheck, metadataKeys ...string) (_ *domain.ObjectDetails, err error) {
if len(metadataKeys) == 0 {
return nil, zerrors.ThrowPreconditionFailed(nil, "META-9mw2d", "Errors.Metadata.NoData")
}
if permissionCheck != nil {
if err := permissionCheck(ctx, orgID); err != nil {
return nil, err
}
}
err = c.checkOrgExists(ctx, orgID)
if err != nil {
return nil, err
+149 -32
View File
@@ -144,13 +144,14 @@ func TestCommandSide_SetOrgMetadata(t *testing.T) {
func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
metadataList []*domain.Metadata
ctx context.Context
orgID string
permissionCheck OrganizationPermissionCheck
metadataList []*domain.Metadata
}
)
type res struct {
@@ -166,9 +167,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
{
name: "empty meta data list, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -181,8 +180,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
{
name: "org not existing, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -201,8 +199,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
{
name: "invalid metadata, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -228,8 +225,7 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
{
name: "add metadata, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -266,13 +262,72 @@ func TestCommandSide_BulkSetOrgMetadata(t *testing.T) {
},
},
},
{
name: "add metadata (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"ZITADEL",
),
),
),
expectPush(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key",
[]byte("value"),
),
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key1",
[]byte("value1"),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
metadataList: []*domain.Metadata{
{Key: "key", Value: []byte("value")},
{Key: "key1", Value: []byte("value1")},
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "add metadata (no permission), error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
metadataList: []*domain.Metadata{
{Key: "key", Value: []byte("value")},
{Key: "key1", Value: []byte("value1")},
},
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.BulkSetOrgMetadata(tt.args.ctx, tt.args.orgID, tt.args.metadataList...)
got, err := r.BulkSetOrgMetadata(tt.args.ctx, tt.args.orgID, tt.args.permissionCheck, tt.args.metadataList...)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -428,13 +483,14 @@ func TestCommandSide_OrgRemoveMetadata(t *testing.T) {
func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
metadataList []string
ctx context.Context
orgID string
permissionCheck OrganizationPermissionCheck
metadataList []string
}
)
type res struct {
@@ -450,9 +506,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
{
name: "empty meta data list, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -465,8 +519,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
{
name: "org not existing, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -482,8 +535,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
{
name: "remove metadata keys not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -515,8 +567,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
{
name: "invalid metadata, pre condition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -555,8 +606,7 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
{
name: "remove metadata, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
@@ -604,13 +654,80 @@ func TestCommandSide_BulkRemoveOrgMetadata(t *testing.T) {
},
},
},
{
name: "remove metadata (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"ZITADEL",
),
),
),
expectFilter(
eventFromEventPusher(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key",
[]byte("value"),
),
),
eventFromEventPusher(
org.NewMetadataSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key1",
[]byte("value1"),
),
),
),
expectPush(
org.NewMetadataRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key",
),
org.NewMetadataRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"key1",
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
metadataList: []string{"key", "key1"},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "remove metadata (no permission), ok",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
metadataList: []string{"key", "key1"},
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.BulkRemoveOrgMetadata(tt.args.ctx, tt.args.orgID, tt.args.metadataList...)
got, err := r.BulkRemoveOrgMetadata(tt.args.ctx, tt.args.orgID, tt.args.permissionCheck, tt.args.metadataList...)
if tt.res.err == nil {
assert.NoError(t, err)
}
+276 -18
View File
@@ -463,9 +463,10 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
eventstore func(t *testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
orgID string
name string
ctx context.Context
orgID string
name string
permissionCheck OrganizationPermissionCheck
}
type res struct {
err func(error) bool
@@ -727,13 +728,76 @@ func TestCommandSide_ChangeOrg(t *testing.T) {
},
res: res{},
},
{
name: "change org name (with permission check), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org"),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org.zitadel.ch"),
),
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org.zitadel.ch"),
),
eventFromEventPusher(
org.NewDomainPrimarySetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org.zitadel.ch"),
),
),
expectPush(
org.NewOrgChangedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, "org", "ORG",
),
),
),
},
args: args{
ctx: http_util.WithRequestedHost(context.Background(), "zitadel.ch"),
orgID: "org1",
name: "ORG",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{},
},
{
name: "change org name (no permission), error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: http_util.WithRequestedHost(context.Background(), "zitadel.ch"),
orgID: "org1",
name: "ORG",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore(t),
}
_, err := r.ChangeOrg(tt.args.ctx, tt.args.orgID, tt.args.name)
_, err := r.ChangeOrg(tt.args.ctx, tt.args.orgID, tt.args.name, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -751,8 +815,9 @@ func TestCommandSide_DeactivateOrg(t *testing.T) {
iamDomain string
}
type args struct {
ctx context.Context
orgID string
ctx context.Context
orgID string
permissionCheck OrganizationPermissionCheck
}
type res struct {
want *domain.Org
@@ -855,6 +920,45 @@ func TestCommandSide_DeactivateOrg(t *testing.T) {
},
res: res{},
},
{
name: "deactivate org (with permission check)",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectPush(
org.NewOrgDeactivatedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{},
},
{
name: "deactivate org (no permission)",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -862,7 +966,7 @@ func TestCommandSide_DeactivateOrg(t *testing.T) {
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
}
_, err := r.DeactivateOrg(tt.args.ctx, tt.args.orgID)
_, err := r.DeactivateOrg(tt.args.ctx, tt.args.orgID, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -880,8 +984,9 @@ func TestCommandSide_ReactivateOrg(t *testing.T) {
iamDomain string
}
type args struct {
ctx context.Context
orgID string
ctx context.Context
orgID string
permissionCheck OrganizationPermissionCheck
}
type res struct {
want *domain.Org
@@ -989,6 +1094,49 @@ func TestCommandSide_ReactivateOrg(t *testing.T) {
},
res: res{},
},
{
name: "reactivate org (with permission check)",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org"),
),
eventFromEventPusher(
org.NewOrgDeactivatedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate),
),
),
expectPush(
org.NewOrgReactivatedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{},
},
{
name: "reactivate org (with permission check)",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -996,7 +1144,7 @@ func TestCommandSide_ReactivateOrg(t *testing.T) {
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
}
_, err := r.ReactivateOrg(tt.args.ctx, tt.args.orgID)
_, err := r.ReactivateOrg(tt.args.ctx, tt.args.orgID, tt.args.permissionCheck)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -1013,8 +1161,10 @@ func TestCommandSide_RemoveOrg(t *testing.T) {
idGenerator id.Generator
}
type args struct {
ctx context.Context
orgID string
ctx context.Context
orgID string
permissionCheck OrganizationPermissionCheck
mustExist bool
}
type res struct {
err func(error) bool
@@ -1064,7 +1214,24 @@ func TestCommandSide_RemoveOrg(t *testing.T) {
},
},
{
name: "org not found, error",
name: "org not found, must exist, error",
fields: fields{
eventstore: expectEventstore(
expectFilter(), // zitadel project check
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
mustExist: true,
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "org not found, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(), // zitadel project check
@@ -1075,9 +1242,7 @@ func TestCommandSide_RemoveOrg(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
},
res: res{
err: zerrors.IsNotFound,
},
res: res{},
},
{
name: "push failed, error",
@@ -1252,6 +1417,63 @@ func TestCommandSide_RemoveOrg(t *testing.T) {
},
res: res{},
},
{
name: "remove org (with permission check)",
fields: fields{
eventstore: expectEventstore(
expectFilter(), // zitadel project check
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewDomainPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
true,
true,
true,
),
),
),
expectFilter(),
expectFilterOrganizationSettings("org1", false, false),
expectFilter(),
expectFilter(),
expectFilter(),
expectPush(
org.NewOrgRemovedEvent(
context.Background(), &org.NewAggregate("org1").Aggregate, "org", []string{}, false, []string{}, []*domain.UserIDPLink{}, []string{},
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckAllowed(),
},
res: res{},
},
{
name: "remove org (no permission)",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.IsPermissionDenied,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -1259,7 +1481,7 @@ func TestCommandSide_RemoveOrg(t *testing.T) {
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
}
_, err := r.RemoveOrg(tt.args.ctx, tt.args.orgID)
_, err := r.RemoveOrg(tt.args.ctx, tt.args.orgID, tt.args.permissionCheck, tt.args.mustExist)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -1281,6 +1503,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
ctx context.Context
setupOrg *OrgSetup
allowInitialMail bool
permissionCheck OrganizationPermissionCheck
userIDs []string
}
type res struct {
@@ -1821,6 +2044,41 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
},
},
},
{
name: "no permission, error",
fields: fields{
eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id"),
newCode: mockEncryptedCode("userinit", time.Hour),
keyAlgorithm: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
},
args: args{
ctx: http_util.WithRequestedHost(context.Background(), "iam-domain"),
setupOrg: &OrgSetup{
Name: "Org",
Admins: []*OrgSetupAdmin{
{
Machine: &AddMachine{
Machine: &Machine{
Username: "username",
Name: "name",
Description: "description",
AccessTokenType: domain.OIDCTokenTypeBearer,
},
Pat: &AddPat{
ExpirationDate: testNow.Add(time.Hour),
Scopes: []string{openid.ScopeOpenID},
},
},
},
},
},
permissionCheck: newMockOrganizationPermissionCheckNotAllowed(),
},
res: res{
err: zerrors.ThrowPermissionDenied(nil, "", "Errors.PermissionDenied"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -1835,7 +2093,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) {
},
},
}
got, err := r.SetUpOrg(tt.args.ctx, tt.args.setupOrg, tt.args.allowInitialMail, tt.args.userIDs...)
got, err := r.SetUpOrg(tt.args.ctx, tt.args.setupOrg, tt.args.allowInitialMail, tt.args.permissionCheck, tt.args.userIDs...)
assert.ErrorIs(t, err, tt.res.err)
assert.Equal(t, tt.res.createdOrg, got)
})
+12
View File
@@ -161,6 +161,18 @@ func (c *Commands) NewPermissionCheckUserGrantDelete(ctx context.Context) UserGr
return c.newUserGrantPermissionCheck(ctx, domain.PermissionUserGrantDelete)
}
func (c *Commands) CheckPermissionOrganizationCreate(ctx context.Context, organizationID string) error {
return c.newPermissionCheck(ctx, domain.PermissionOrganizationWrite, org.AggregateType)(organizationID, organizationID)
}
func (c *Commands) CheckPermissionOrganizationWrite(ctx context.Context, organizationID string) error {
return c.newPermissionCheck(ctx, domain.PermissionOrganizationWrite, org.AggregateType)(organizationID, organizationID)
}
func (c *Commands) CheckPermissionOrganizationDelete(ctx context.Context, organizationID string) error {
return c.newPermissionCheck(ctx, domain.PermissionOrganizationDelete, org.AggregateType)(organizationID, organizationID)
}
func (c *Commands) checkPermissionCreateGroup(ctx context.Context, resourceOwner, groupID string) error {
return c.newPermissionCheck(ctx, domain.PermissionGroupCreate, group.AggregateType)(resourceOwner, groupID)
}
+2
View File
@@ -37,6 +37,8 @@ const (
PermissionSessionLink = "session.link"
PermissionSessionDelete = "session.delete"
PermissionOrgRead = "org.read"
PermissionOrganizationWrite = "org.write"
PermissionOrganizationDelete = "org.delete"
PermissionIDPRead = "iam.idp.read"
PermissionOrgIDPRead = "org.idp.read"
PermissionProjectCreate = "project.create"
+16 -1
View File
@@ -55,7 +55,7 @@ func NewOrgDomainVerifiedSearchQuery(verified bool) (SearchQuery, error) {
return NewBoolQuery(OrgDomainIsVerifiedCol, verified)
}
func (q *Queries) SearchOrgDomains(ctx context.Context, queries *OrgDomainSearchQueries, withOwnerRemoved bool) (domains *Domains, err error) {
func (q *Queries) SearchOrgDomains(ctx context.Context, queries *OrgDomainSearchQueries, withOwnerRemoved, withPermissionCheck bool) (domains *Domains, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@@ -64,6 +64,11 @@ func (q *Queries) SearchOrgDomains(ctx context.Context, queries *OrgDomainSearch
if !withOwnerRemoved {
eq[OrgDomainOwnerRemovedCol.identifier()] = false
}
if withPermissionCheck {
// We always use the permission v2 check and don't check the feature flag, since it's stable enough to work
// in this case and using the old checks only adds more latency, but no benefit.
query = orgDomainPermissionCheckV2(ctx, query, queries)
}
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
if err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "QUERY-ZRfj1", "Errors.Query.SQLStatement")
@@ -176,3 +181,13 @@ var (
table: orgDomainsTable,
}
)
func orgDomainPermissionCheckV2(ctx context.Context, query sq.SelectBuilder, queries *OrgDomainSearchQueries) sq.SelectBuilder {
join, args := PermissionClause(
ctx,
OrgDomainOrgIDCol,
domain.PermissionOrgRead,
SingleOrgPermissionOption(queries.Queries),
)
return query.JoinClause(join, args...)
}
+17 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
domain_pkg "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
@@ -113,7 +114,7 @@ func (q *Queries) GetOrgMetadataByKey(ctx context.Context, shouldTriggerBulk boo
return metadata, err
}
func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, orgID string, queries *OrgMetadataSearchQueries, withOwnerRemoved bool) (metadata *OrgMetadataList, err error) {
func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool, orgID string, queries *OrgMetadataSearchQueries, withOwnerRemoved, withPermissionCheck bool) (metadata *OrgMetadataList, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@@ -131,6 +132,11 @@ func (q *Queries) SearchOrgMetadata(ctx context.Context, shouldTriggerBulk bool,
eq[OrgMetadataOwnerRemovedCol.identifier()] = false
}
query, scan := prepareOrgMetadataListQuery()
if withPermissionCheck {
// We always use the permission v2 check and don't check the feature flag, since it's stable enough to work
// in this case and using the old checks only adds more latency, but no benefit.
query = orgMetadataPermissionCheckV2(ctx, query, queries)
}
stmt, args, err := queries.toQuery(query).Where(eq).ToSql()
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-Egbld", "Errors.Query.SQLStatement")
@@ -248,3 +254,13 @@ func prepareOrgMetadataListQuery() (sq.SelectBuilder, func(*sql.Rows) (*OrgMetad
}, nil
}
}
func orgMetadataPermissionCheckV2(ctx context.Context, query sq.SelectBuilder, queries *OrgMetadataSearchQueries) sq.SelectBuilder {
join, args := PermissionClause(
ctx,
OrgMetadataOrgIDCol,
domain_pkg.PermissionOrgRead,
SingleOrgPermissionOption(queries.Queries),
)
return query.JoinClause(join, args...)
}
+6 -6
View File
@@ -1175,7 +1175,7 @@ service AdminService {
// Get Organization By ID
//
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organizations.api.mdx) instead.
//
// Returns an organization by its ID. Make sure the user has the permissions to access the organization.
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
@@ -1201,7 +1201,7 @@ service AdminService {
// Is Organization Unique
//
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organizations.api.mdx) instead.
//
// Checks if an organization with the searched parameters already exists or not.
rpc IsOrgUnique(IsOrgUniqueRequest) returns (IsOrgUniqueResponse) {
@@ -1250,7 +1250,7 @@ service AdminService {
// Get Default Organization
//
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organizations.api.mdx) instead.
//
// Get the default organization of the ZITADEL instance. If no specific organization is given on the register form, a user will be registered to the default organization.
rpc GetDefaultOrg(GetDefaultOrgRequest) returns (GetDefaultOrgResponse) {
@@ -1271,7 +1271,7 @@ service AdminService {
// Search Organizations
//
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizations](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organizations.api.mdx) instead.
//
// Returns a list of organizations that match the requesting filters. All filters are applied with an AND condition.
rpc ListOrgs(ListOrgsRequest) returns (ListOrgsResponse) {
@@ -1309,7 +1309,7 @@ service AdminService {
// Setup Organization
//
// Deprecated: use [organization service v2 CreateOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-create-organization.api.mdx) instead.
// Deprecated: use [organization service v2 AddOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-add-organization.api.mdx) instead.
//
// Create a new organization with an administrative user. If no specific roles are sent for the first user, the user will get the role ORG_OWNER.
rpc SetUpOrg(SetUpOrgRequest) returns (SetUpOrgResponse) {
@@ -1347,7 +1347,7 @@ service AdminService {
// Remove Organization
//
// Deprecated: use [organization service v2 DeleteOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-delete-organization.api.mdx) instead.
// Deprecated: use [organization service v2 DeleteOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-delete-organization.api.mdx) instead.
//
// Deletes the organization and all its resources (Users, Projects, Grants to and from the org). Users of this organization will not be able to log in.
rpc RemoveOrg(RemoveOrgRequest) returns (RemoveOrgResponse) {
+17 -17
View File
@@ -2238,7 +2238,7 @@ service ManagementService {
// Get Organization By Domain
//
// Deprecated: use [organization v2 service ListOrganizations](apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead.
// Deprecated: use [organization v2 service ListOrganizations](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organizations.api.mdx) instead.
//
// Search an organization by the domain, overall organizations. The domain must match exactly.
rpc GetOrgByDomainGlobal(GetOrgByDomainGlobalRequest) returns (GetOrgByDomainGlobalResponse) {
@@ -2283,7 +2283,7 @@ service ManagementService {
// Create Organization
//
// Deprecated: use [organization service v2 CreateOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-create-organization.api.mdx) instead
// Deprecated: use [organization service v2 CreateOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-add-organization.api.mdx) instead
//
// Create a new organization. Based on the given name a domain will be generated to be able to identify users within an organization.
rpc AddOrg(AddOrgRequest) returns (AddOrgResponse) {
@@ -2312,7 +2312,7 @@ service ManagementService {
// Update Organization
//
// Deprecated: use [organization service v2 UpdateOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-update-organization.api.mdx) instead.
// Deprecated: use [organization service v2 UpdateOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-update-organization.api.mdx) instead.
//
// Change the name of the organization.
rpc UpdateOrg(UpdateOrgRequest) returns (UpdateOrgResponse) {
@@ -2341,7 +2341,7 @@ service ManagementService {
// Deactivate Organization
//
// Deprecated: use [organization service v2 DeactivateOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-deactivate-organization.api.mdx) instead.
// Deprecated: use [organization service v2 DeactivateOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-deactivate-organization.api.mdx) instead.
//
// Sets the state of my organization to deactivated. Users of this organization will not be able to log in.
rpc DeactivateOrg(DeactivateOrgRequest) returns (DeactivateOrgResponse) {
@@ -2370,7 +2370,7 @@ service ManagementService {
// Reactivate Organization
//
// Deprecated: use [organization service v2 ActivateOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-activate-organization.api.mdx) instead.
// Deprecated: use [organization service v2 ActivateOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-activate-organization.api.mdx) instead.
//
// Set the state of my organization to active. The state of the organization has to be deactivated to perform the request. Users of this organization will be able to log in again.
rpc ReactivateOrg(ReactivateOrgRequest) returns (ReactivateOrgResponse) {
@@ -2399,7 +2399,7 @@ service ManagementService {
// Delete Organization
//
// Deprecated: use [organization service v2 DeleteOrganization](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-delete-organization.api.mdx) instead.
// Deprecated: use [organization service v2 DeleteOrganization](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-delete-organization.api.mdx) instead.
//
// Deletes my organization and all its resources (Users, Projects, Grants to and from the org). Users of this organization will not be able to log in.
rpc RemoveOrg(RemoveOrgRequest) returns (RemoveOrgResponse) {
@@ -2427,7 +2427,7 @@ service ManagementService {
// Set Organization Metadata
//
// Deprecated: use [organization service v2 SetOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-set-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 SetOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-set-organization-metadata.api.mdx) instead.
//
// This endpoint either adds or updates a metadata value for the requested key. Make sure the value is base64 encoded.
rpc SetOrgMetadata(SetOrgMetadataRequest) returns (SetOrgMetadataResponse) {
@@ -2457,7 +2457,7 @@ service ManagementService {
// Bulk Set Organization Metadata
//
// Deprecated: use [organization service v2 SetOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-set-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 SetOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-set-organization-metadata.api.mdx) instead.
//
// This endpoint sets a list of metadata to the organization. Make sure the values are base64 encoded.
rpc BulkSetOrgMetadata(BulkSetOrgMetadataRequest) returns (BulkSetOrgMetadataResponse) {
@@ -2487,7 +2487,7 @@ service ManagementService {
// Search Organization Metadata
//
// Deprecated: use [organization service v2 ListOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-list-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organization-metadata.api.mdx) instead.
//
// Get the metadata of an organization filtered by your query.
rpc ListOrgMetadata(ListOrgMetadataRequest) returns (ListOrgMetadataResponse) {
@@ -2517,7 +2517,7 @@ service ManagementService {
// Get Organization Metadata By Key
//
// Deprecated: use [organization service v2 ListOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-list-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organization-metadata.api.mdx) instead.
//
// Get a metadata object from an organization by a specific key.
rpc GetOrgMetadata(GetOrgMetadataRequest) returns (GetOrgMetadataResponse) {
@@ -2546,7 +2546,7 @@ service ManagementService {
// Delete Organization Metadata By Key
//
// Deprecated: use [organization service v2 DeleteOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-delete-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 DeleteOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-delete-organization-metadata.api.mdx) instead.
//
// Remove a metadata object from an organization with a specific key.
rpc RemoveOrgMetadata(RemoveOrgMetadataRequest) returns (RemoveOrgMetadataResponse) {
@@ -2575,7 +2575,7 @@ service ManagementService {
// Bulk Delete Metadata
//
// Deprecated: use [organization service v2 DeleteOrganizationMetadata](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-delete-organization-metadata.api.mdx) instead.
// Deprecated: use [organization service v2 DeleteOrganizationMetadata](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-delete-organization-metadata.api.mdx) instead.
//
// Remove a list of metadata objects from an organization with a list of keys.
rpc BulkRemoveOrgMetadata(BulkRemoveOrgMetadataRequest) returns (BulkRemoveOrgMetadataResponse) {
@@ -2605,7 +2605,7 @@ service ManagementService {
// Add Domain
//
// Deprecated: use [organization service v2 AddOrganizationDomain](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-add-organization-domain.api.mdx) instead.
// Deprecated: use [organization service v2 AddOrganizationDomain](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-add-organization-domain.api.mdx) instead.
//
// Add a new domain to an organization. The domains are used to identify to which organization a user belongs.
rpc AddOrgDomain(AddOrgDomainRequest) returns (AddOrgDomainResponse) {
@@ -2634,7 +2634,7 @@ service ManagementService {
// Search Domains
//
// Deprecated: use [organization service v2 ListOrganizationDomains](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-list-organization-domains.api.mdx) instead.
// Deprecated: use [organization service v2 ListOrganizationDomains](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-list-organization-domains.api.mdx) instead.
//
// Returns the list of registered domains of an organization. The domains are used to identify to which organization a user belongs.
rpc ListOrgDomains(ListOrgDomainsRequest) returns (ListOrgDomainsResponse) {
@@ -2663,7 +2663,7 @@ service ManagementService {
// Remove Domain
//
// Deprecated: use [organization service v2 DeleteOrganizationDomain](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-delete-organization-domain.api.mdx) instead.
// Deprecated: use [organization service v2 DeleteOrganizationDomain](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-delete-organization-domain.api.mdx) instead.
//
// Delete a new domain from an organization. The domains are used to identify to which organization a user belongs. If the uses use the domain for login, this will not be possible afterwards. They have to use another domain instead.
rpc RemoveOrgDomain(RemoveOrgDomainRequest) returns (RemoveOrgDomainResponse) {
@@ -2691,7 +2691,7 @@ service ManagementService {
// Generate Domain Verification
//
// Deprecated: use [organization service v2 GenerateOrganizationDomainValidation](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-generate-organization-domain-validation.api.mdx) instead.
// Deprecated: use [organization service v2 GenerateOrganizationDomainValidation](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-generate-organization-domain-validation.api.mdx) instead.
//
// Generate a new file to be able to verify your domain with DNS or HTTP challenge.
rpc GenerateOrgDomainValidation(GenerateOrgDomainValidationRequest) returns (GenerateOrgDomainValidationResponse) {
@@ -2720,7 +2720,7 @@ service ManagementService {
// Verify Domain
//
// Deprecated: use [organization service v2 VerifyOrganizationDomain](apis/resources/org_service_v2beta/zitadel-org-v-2-beta-organization-service-verify-organization-domain.api.mdx) instead.
// Deprecated: use [organization service v2 VerifyOrganizationDomain](apis/resources/org_service_v2/zitadel-org-v-2-organization-service-verify-organization-domain.api.mdx) instead.
//
// Make sure you have added the required verification to your domain, depending on the method you have chosen (HTTP or DNS challenge). ZITADEL will check it and set the domain as verified if it was successful. A verify domain has to be unique.
rpc ValidateOrgDomain(ValidateOrgDomainRequest) returns (ValidateOrgDomainResponse) {
+43 -3
View File
@@ -12,21 +12,26 @@ import "zitadel/object/v2/object.proto";
message Organization {
// Unique identifier of the organization.
// ID is the unique identifier of the organization.
string id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\""
}
];
// Details about the organizations' creation and change date.
zitadel.object.v2.Details details = 2;
// Current state of the organization, for example active, inactive and deleted.
// Current state of the organization, for example active, inactive or deleted.
OrganizationState state = 3;
// Name of the organization.
string name = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ZITADEL\"";
}
];
// Primary domain used in the organization.
string primary_domain = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -40,4 +45,39 @@ enum OrganizationState {
ORGANIZATION_STATE_ACTIVE = 1;
ORGANIZATION_STATE_INACTIVE = 2;
ORGANIZATION_STATE_REMOVED = 3;
}
}
enum DomainValidationType {
DOMAIN_VALIDATION_TYPE_UNSPECIFIED = 0;
// HTTP validation requires you to host a specific file on your domain.
// This file is checked by us to verify that you own the domain.
DOMAIN_VALIDATION_TYPE_HTTP = 1;
// DNS validation requires you to create a specific TXT record in your domain's DNS settings.
// This record is checked by us to verify that you own the domain.
DOMAIN_VALIDATION_TYPE_DNS = 2;
}
message Domain {
// OrganizationID is the unique identifier of the organization the domain belongs to.
string organization_id = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\""
}
];
// Domain is the fully qualified domain name.
string domain = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"zitadel.com\"";
}
];
// IsVerified is a boolean flag indicating if the domain has been verified.
bool is_verified = 3;
// IsPrimary is a boolean flag indicating if the domain is the primary domain of the organization.
bool is_primary = 4;
// ValidationType indicates the protocol used to validate the domain ownership.
DomainValidationType validation_type = 5;
}
+772 -6
View File
@@ -14,11 +14,14 @@ import "zitadel/user/v2/user_service.proto";
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/struct.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
import "zitadel/org/v2/org.proto";
import "zitadel/org/v2/query.proto";
import "zitadel/filter/v2/filter.proto";
import "zitadel/metadata/v2/metadata.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/org/v2;org";
@@ -110,11 +113,15 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
}
};
// This service is intended to manage organizations in a ZITADEL instance.
service OrganizationService {
// Create an Organization
// Add Organization
//
// Create a new organization with an administrative user. If no specific roles are sent for the users, they will be granted the role ORG_OWNER.
//
// Required permission:
// - `org.create`
rpc AddOrganization(AddOrganizationRequest) returns (AddOrganizationResponse) {
option (google.api.http) = {
post: "/v2/organizations"
@@ -123,7 +130,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.create"
permission: "authenticated"
}
http_response: {
success_code: 201
@@ -140,9 +147,54 @@ service OrganizationService {
};
}
// Search Organizations
// Update Organization
//
// Search for Organizations. By default, we will return all organization of the instance. Make sure to include a limit and sorting for pagination..
// Change the name of the organization.
//
// Required permission:
// - `org.write`
rpc UpdateOrganization(UpdateOrganizationRequest) returns (UpdateOrganizationResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Organization updated successfully";
};
};
responses: {
key: "404"
value: {
description: "Organisation's not found";
}
};
responses: {
key: "409"
value: {
description: "Organisation's name already taken";
}
};
};
}
// List Organizations
//
// Search for Organizations. By default, we will return all organization of the instance that you have permission to read.
// Make sure to include a limit and sorting for pagination.
//
// Required permission:
// - `org.read`
rpc ListOrganizations(ListOrganizationsRequest) returns (ListOrganizationsResponse) {
option (google.api.http) = {
post: "/v2/organizations/_search";
@@ -175,6 +227,311 @@ service OrganizationService {
};
};
}
// Delete Organization
//
// Deletes the organization and all its resources (Users, Projects, Grants to and from the org). Users of this organization will not be able to log in.
//
// Required permission:
// - `org.delete`
rpc DeleteOrganization(DeleteOrganizationRequest) returns (DeleteOrganizationResponse) {
option (google.api.http) = {
delete: "/v2/organizations/{organization_id}"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated";
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
value: {
description: "Organization created successfully";
};
};
responses: {
key: "404"
value: {
description: "Organisation's not found";
}
};
};
}
// Set Organization Metadata
//
// Adds or updates a metadata value for the requested key. Make sure the value is base64 encoded.
//
// Required permission:
// - `org.write`
rpc SetOrganizationMetadata(SetOrganizationMetadataRequest) returns (SetOrganizationMetadataResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/metadata"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
responses: {
key: "400"
value: {
description: "Organisation's not found";
}
};
};
}
// List Organization Metadata
//
// List metadata of an organization filtered by query.
//
// Required permission:
// - `org.read`
rpc ListOrganizationMetadata(ListOrganizationMetadataRequest) returns (ListOrganizationMetadataResponse ) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/metadata/search"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = { auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Delete Organization Metadata
//
// Delete metadata objects from an organization with a specific key.
//
// Required permission:
// - `org.write`
rpc DeleteOrganizationMetadata(DeleteOrganizationMetadataRequest) returns (DeleteOrganizationMetadataResponse) {
option (google.api.http) = {
delete: "/v2/organizations/{organization_id}/metadata"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Add Organization Domain
//
// Add a new domain to an organization. The domains are used to identify to which organization a user belongs.
//
// Required permission:
// - `org.write`
rpc AddOrganizationDomain(AddOrganizationDomainRequest) returns (AddOrganizationDomainResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/domains"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
responses: {
key: "409"
value: {
description: "Domain already exists";
}
};
};
}
// List Organization Domains
//
// Returns the list of registered domains of an organization. The domains are used to identify to which organization a user belongs.
//
// Required permission:
// - `org.read`
rpc ListOrganizationDomains(ListOrganizationDomainsRequest) returns (ListOrganizationDomainsResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/domains/search"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Delete Organization Domain
//
// Delete a new domain from an organization. The domains are used to identify to which organization a user belongs. If the uses use the domain for login, this will not be possible afterwards. They have to use another domain instead.
//
// Required permission:
// - `org.write`
rpc DeleteOrganizationDomain(DeleteOrganizationDomainRequest) returns (DeleteOrganizationDomainResponse) {
option (google.api.http) = {
delete: "/v2/organizations/{organization_id}/domains"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Generate Organization Domain Validation
//
// Generate a new file to be able to verify your domain with DNS or HTTP challenge.
//
// Required permission:
// - `org.write`
rpc GenerateOrganizationDomainValidation(GenerateOrganizationDomainValidationRequest) returns (GenerateOrganizationDomainValidationResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/domains/validation/generate"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
responses: {
key: "404"
value: {
description: "Domain doesn't exist on organization";
}
};
};
}
// Verify Organization Domain
//
// Make sure you have added the required verification to your domain, depending on the method you have chosen (HTTP or DNS challenge). ZITADEL will check it and set the domain as verified if it was successful. A verify domain has to be unique.
//
// Required permission:
// - `org.write`
rpc VerifyOrganizationDomain(VerifyOrganizationDomainRequest) returns (VerifyOrganizationDomainResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/domains/validation/verify"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Deactivate Organization
//
// Sets the state of my organization to deactivated. Users of this organization will not be able to log in.
//
// Required permission:
// - `org.write`
rpc DeactivateOrganization(DeactivateOrganizationRequest) returns (DeactivateOrganizationResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/deactivate"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
// Activate Organization
//
// Set the state of my organization to active. The state of the organization has to be deactivated to perform the request. Users of this organization will be able to log in again.
//
// Required permission:
// - `org.write`
rpc ActivateOrganization(ActivateOrganizationRequest) returns (ActivateOrganizationResponse) {
option (google.api.http) = {
post: "/v2/organizations/{organization_id}/activate"
body: "*"
};
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "authenticated"
}
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
responses: {
key: "200";
};
};
}
}
message AddOrganizationRequest{
@@ -187,6 +544,8 @@ message AddOrganizationRequest{
repeated string roles = 3;
}
// Name is the unique name of the organization to be created.
// This must be unique across the instance.
string name = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
@@ -196,9 +555,30 @@ message AddOrganizationRequest{
example: "\"ZITADEL\"";
}
];
// Specify users to be assigned as organization admins.
// If no users are specified here, the organization will be created without any admin users.
// The organization can still be managed by any instance administrator.
// If no roles are specified for a user, they will be assigned the role ORG_OWNER.
repeated Admin admins = 2;
// optionally set your own id unique for the organization.
// OrganizationID is the unique identifier of the organization. This field is optional.
// If omitted, the system will generate one,
// which is the recommended way. The generated ID will be returned in the response.
optional string organization_id = 4 [
(validate.rules).string = {min_len: 1, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
max_length: 200;
example: "\"d654e6ba-70a3-48ef-a95d-37c8d8a7901a\"";
}
];
// Optionally, set a unique id for the organization. If omitted, the system will generate one,
// which is the recommended way. The generated ID will be returned in the response.
//
// Deprecated: use 'organization_id' field instead.
optional string org_id = 3 [
deprecated = true,
(validate.rules).string = {min_len: 1, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
max_length: 200;
@@ -218,17 +598,403 @@ message AddOrganizationResponse{
repeated CreatedAdmin created_admins = 3;
}
message UpdateOrganizationRequest {
// OrganizationID is the unique identifier of the organization to be updated.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Name is the new name for the organization to be set.
// Note that since the name is used to generate the organization's default domain,
// changing the name will also change the domain.
// Additionally, if the domain is used as suffix for user logins,
// their login names will also change accordingly.
// It will not affect any custom domains added to the organization.
string name = 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: "\"Customer 1\"";
}
];
}
message UpdateOrganizationResponse {
// ChangeDate is the timestamp of the update to the organization.
google.protobuf.Timestamp change_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message ListOrganizationsRequest {
//list limitations and ordering
zitadel.object.v2.ListQuery query = 1;
// the field the result is sorted
zitadel.org.v2.OrganizationFieldName sorting_column = 2;
//criteria the client is looking for
// criteria the client is looking for
repeated zitadel.org.v2.SearchQuery queries = 3;
}
message ListOrganizationsResponse {
// Details of organizations results.
zitadel.object.v2.ListDetails details = 1;
// The sorting columns the result is sorted.
zitadel.org.v2.OrganizationFieldName sorting_column = 2;
// The Result is a list of organizations matching the query.
repeated zitadel.org.v2.Organization result = 3;
}
message DeleteOrganizationRequest {
// OrganizationID is the unique identifier of the organization to be deleted.
string organization_id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
min_length: 1;
max_length: 200;
}
];
}
message DeleteOrganizationResponse {
// DeletionDate is the timestamp of the deletion of the organization.
google.protobuf.Timestamp deletion_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message DeactivateOrganizationRequest {
// OrganizationID is the unique identifier of the organization to be deactivated.
string organization_id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
min_length: 1;
max_length: 200;
},
(google.api.field_behavior) = REQUIRED
];
}
message DeactivateOrganizationResponse {
// ChangeDate is the timestamp of the deactivation of the organization.
google.protobuf.Timestamp change_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message ActivateOrganizationRequest {
// OrganizationID is the unique identifier of the organization to be activated.
string organization_id = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"69629023906488334\"";
min_length: 1;
max_length: 200;
},
(google.api.field_behavior) = REQUIRED
];
}
message ActivateOrganizationResponse {
// ChangeDate is the timestamp of the activation of the organization.
google.protobuf.Timestamp change_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message AddOrganizationDomainRequest {
// OrganizationID is the unique identifier of the organization to which the domain is to be added.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Domain is the full qualified domain name to be added to the organization.
// Note that the domain has to be unique across the instance.
// Depending on the settings, you might have to verify the domain before it can be used.
string domain = 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: "\"testdomain.com\"";
}
];
}
message AddOrganizationDomainResponse {
// CreationDate is the timestamp when the organization domain was created.
google.protobuf.Timestamp creation_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2024-12-18T07:50:47.492Z\"";
}
];
}
message ListOrganizationDomainsRequest {
// OrganizationID is the unique identifier of the organization from which the domains are to be listed.
string organization_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;
example: "\"69629012906488334\"";
}
];
// List limitations and ordering.
optional zitadel.filter.v2.PaginationRequest pagination = 2;
// Filters define the criteria to query for.
repeated DomainSearchFilter filters = 3;
// SortingColumn is the field the result is sorted by.
// Beware that if you change this, your result pagination might be inconsistent.
DomainFieldName sorting_column = 4;
}
message ListOrganizationDomainsResponse {
// Pagination of the organizations domain results.
zitadel.filter.v2.PaginationResponse pagination = 1;
// Domains is a list of fully qualified domain names registered to the organization matching the query.
repeated Domain domains = 2;
}
message DeleteOrganizationDomainRequest {
// OrganizationID is the unique identifier of the organization from which the domain is to be deleted.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Domain is the full qualified domain name to be deleted from the organization.
// Note that if the domain is used as suffix for user logins,
// those users will not be able to log in anymore. They have to use another domain instead.
// Also if the domain was used for domain discovery,
// users will not be able to find the organization by the domain anymore.
string domain = 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: "\"testdomain.com\"";
}
];
}
message DeleteOrganizationDomainResponse {
// DeletionDate is the timestamp of the deletion of the organization domain.
google.protobuf.Timestamp deletion_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message GenerateOrganizationDomainValidationRequest {
// OrganizationID is the unique identifier of the organization for which the domain validation is to be generated.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Domain is the full qualified domain name for which the validation is to be generated.
string domain = 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: "\"testdomain.com\"";
}
];
// Type is the domain validation type to be generated.
// Depending on the type, you will have to add a DNS record or a file to your webserver.
// Make sure that the challenge is reachable via the chosen method.
// The validation has to be done within one hour, otherwise the token will expire and you will have to generate a new one.
// After you have added the record or the file, you can verify the domain via the VerifyOrganizationDomain endpoint.
// You can check the status of the domain via the ListOrganizationDomains endpoint.
// The domain will be marked as verified after a successful verification.
DomainValidationType type = 3 [(validate.rules).enum = {defined_only: true, not_in: [0]}];
}
message GenerateOrganizationDomainValidationResponse {
// Token is a verification token that needs to be added to the DNS records or as a file to the webserver.
// Zitadel will check for this token to verify the domain.
string token = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ofSBHsSAVHAoTIE4Iv2gwhaYhTjcY5QX\"";
}
];
// URL is the location where the token needs to be placed for HTTP challenge.
string url = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://testdomain.com/.well-known/zitadel-challenge/ofSBHsSAVHAoTIE4Iv2gwhaYhTjcY5QX\"";
}
];
}
message VerifyOrganizationDomainRequest {
// Organization ID is the unique identifier of the organization whose domain is to be verified.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Domain is the full qualified domain name to be verified.
string domain = 2 [
(validate.rules).string = {min_len: 1, max_len: 253},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
min_length: 1;
max_length: 253;
example: "\"testdomain.com\"";
}
];
}
message VerifyOrganizationDomainResponse {
// ChangeDate is the timestamp of the verification of the organization domain.
google.protobuf.Timestamp change_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message Metadata {
// Key is identifier of the metadata entry.
string key = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
// Value is the values of the metadata entry.
bytes value = 2 [(validate.rules).bytes = {min_len: 1, max_len: 500000}];
}
message SetOrganizationMetadataRequest{
// Organization ID is the unique identifier of the organization whose metadata is to be set.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Metadata is a list of metadata entries to set.
repeated Metadata metadata = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "[{\"key\": \"test1\", \"value\": \"VGhpcyBpcyBteSBmaXJzdCB2YWx1ZQ==\"}, {\"key\": \"test2\", \"value\": \"VGhpcyBpcyBteSBzZWNvbmQgdmFsdWU=\"}]"
}
];
}
message SetOrganizationMetadataResponse{
// The timestamp of the update of the organization metadata.
google.protobuf.Timestamp set_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
message ListOrganizationMetadataRequest {
// Organization ID is the unique identifier of the organization whose metadata is to be listed.
string organization_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;
example: "\"69629012906488334\"";
}
];
// List limitations and ordering.
optional zitadel.filter.v2.PaginationRequest pagination = 2;
// Filters define the criteria to query the metadata for.
repeated zitadel.metadata.v2.MetadataSearchFilter filters = 3;
}
message ListOrganizationMetadataResponse {
// Pagination of the Organizations metadata results.
zitadel.filter.v2.PaginationResponse pagination = 1;
// Metadata is a list of organization metadata that matched the query.
repeated zitadel.metadata.v2.Metadata metadata = 2;
}
message DeleteOrganizationMetadataRequest {
// Organization ID is the unique identifier of the organization whose metadata is to be deleted.
string organization_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;
example: "\"69629012906488334\"";
}
];
// Keys are the organization metadata entries to be deleted by their key.
repeated string keys = 2 [(validate.rules).repeated.items.string = {min_len: 1, max_len: 200}];
}
message DeleteOrganizationMetadataResponse{
// DeletionDate is the timestamp of the deletion of the organization metadata.
google.protobuf.Timestamp deletion_date = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"2025-01-23T10:34:18.051Z\"";
}
];
}
+28 -1
View File
@@ -16,10 +16,19 @@ message SearchQuery {
oneof query {
option (validate.required) = true;
// Filter organizations by their name.
OrganizationNameQuery name_query = 1;
// Filter organizations by their domain.
OrganizationDomainQuery domain_query = 2;
// Filter organizations by their state.
OrganizationStateQuery state_query = 3;
// Filter organizations by their id.
OrganizationIDQuery id_query = 4;
// Filter for the default organization.
DefaultOrganizationQuery default_query = 5;
}
}
@@ -35,6 +44,7 @@ message OrganizationNameQuery {
example: "\"gigi-giraffe\"";
}
];
// Defines which text equality method is used.
zitadel.object.v2.TextQueryMethod method = 2 [
(validate.rules).enum.defined_only = true
@@ -52,6 +62,7 @@ message OrganizationDomainQuery {
example: "\"citadel.cloud\"";
}
];
// Defines which text equality method is used.
zitadel.object.v2.TextQueryMethod method = 2 [
(validate.rules).enum.defined_only = true
@@ -81,6 +92,22 @@ message OrganizationIDQuery {
enum OrganizationFieldName {
ORGANIZATION_FIELD_NAME_UNSPECIFIED = 0;
ORGANIZATION_FIELD_NAME_NAME = 1;
ORGANIZATION_FIELD_NAME_CREATION_DATE = 2;
}
message DefaultOrganizationQuery {}
message DefaultOrganizationQuery {}
message DomainSearchFilter {
oneof filter {
option (validate.required) = true;
// Filter organization domains by their domain name.
OrganizationDomainQuery domain_filter = 1;
}
}
enum DomainFieldName {
DOMAIN_FIELD_NAME_UNSPECIFIED = 0;
DOMAIN_FIELD_NAME_NAME = 1;
DOMAIN_FIELD_NAME_CREATION_DATE = 2;
}
+44 -15
View File
@@ -107,6 +107,9 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
}
};
// This service is intended to manage organizations in a ZITADEL instance.
//
// Deprecated: use organization service v2 instead. This service will be removed in the next major version of ZITADEL.
service OrganizationService {
// Create Organization
@@ -125,7 +128,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.create"
permission: "authenticated"
}
};
@@ -148,6 +151,8 @@ service OrganizationService {
// Update Organization
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Change the name of the organization.
//
// Required permission:
@@ -160,7 +165,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -189,10 +194,12 @@ service OrganizationService {
// List Organizations
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Returns a list of organizations that match the requesting filters. All filters are applied with an AND condition.
//
// Required permission:
// - `iam.read`
// - `org.read`
//
// Deprecated: Use [ListOrganizations](/apis/resources/org_service_v2/organization-service-list-organizations.api.mdx) instead to list organizations.
rpc ListOrganizations(ListOrganizationsRequest) returns (ListOrganizationsResponse) {
@@ -203,7 +210,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "iam.read";
permission: "authenticated";
}
};
@@ -217,6 +224,8 @@ service OrganizationService {
// Delete Organization
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Deletes the organization and all its resources (Users, Projects, Grants to and from the org). Users of this organization will not be able to log in.
//
// Required permission:
@@ -228,7 +237,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.delete";
permission: "authenticated";
}
};
@@ -250,6 +259,8 @@ service OrganizationService {
// Set Organization Metadata
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Adds or updates a metadata value for the requested key. Make sure the value is base64 encoded.
//
// Required permission:
@@ -262,7 +273,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -282,6 +293,8 @@ service OrganizationService {
// List Organization Metadata
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// List metadata of an organization filtered by query.
//
// Required permission:
@@ -293,7 +306,7 @@ service OrganizationService {
};
option (zitadel.protoc_gen_zitadel.v2.options) = { auth_option: {
permission: "org.read"
permission: "authenticated"
}
};
@@ -307,6 +320,8 @@ service OrganizationService {
// Delete Organization Metadata
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Delete metadata objects from an organization with a specific key.
//
// Required permission:
@@ -318,7 +333,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -332,6 +347,8 @@ service OrganizationService {
// Add Organization Domain
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Add a new domain to an organization. The domains are used to identify to which organization a user belongs.
//
// Required permission:
@@ -344,7 +361,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -364,6 +381,8 @@ service OrganizationService {
// List Organization Domains
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Returns the list of registered domains of an organization. The domains are used to identify to which organization a user belongs.
//
// Required permission:
@@ -376,7 +395,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.read"
permission: "authenticated"
}
};
@@ -390,6 +409,8 @@ service OrganizationService {
// Delete Organization Domain
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Delete a new domain from an organization. The domains are used to identify to which organization a user belongs. If the uses use the domain for login, this will not be possible afterwards. They have to use another domain instead.
//
// Required permission:
@@ -401,7 +422,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -415,6 +436,8 @@ service OrganizationService {
// Generate Organization Domain Validation
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Generate a new file to be able to verify your domain with DNS or HTTP challenge.
//
// Required permission:
@@ -427,7 +450,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -446,6 +469,8 @@ service OrganizationService {
// Verify Organization Domain
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Make sure you have added the required verification to your domain, depending on the method you have chosen (HTTP or DNS challenge). ZITADEL will check it and set the domain as verified if it was successful. A verify domain has to be unique.
//
// Required permission:
@@ -458,7 +483,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -471,6 +496,8 @@ service OrganizationService {
// Deactivate Organization
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Sets the state of my organization to deactivated. Users of this organization will not be able to log in.
//
// Required permission:
@@ -483,7 +510,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};
@@ -497,6 +524,8 @@ service OrganizationService {
// Activate Organization
//
// Deprecated: please move to the corresponding endpoint under organization service v2. This endpoint will be removed with the next major version of ZITADEL.
//
// Set the state of my organization to active. The state of the organization has to be deactivated to perform the request. Users of this organization will be able to log in again.
//
// Required permission:
@@ -509,7 +538,7 @@ service OrganizationService {
option (zitadel.protoc_gen_zitadel.v2.options) = {
auth_option: {
permission: "org.write"
permission: "authenticated"
}
};