From 9dbcfc255d61a07d827ffcc23e3dcfe5d7a6243c Mon Sep 17 00:00:00 2001 From: Iraq Jaber Date: Fri, 2 May 2025 18:54:58 +0200 Subject: [PATCH] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! refactor(api): moving organization API resourced based added all Domain related endpoinds, testing not complete --- internal/api/grpc/object/v2beta/converter.go | 22 +++ internal/api/grpc/org/v2beta/helper.go | 87 ++++++---- .../org/v2beta/integration_test/org_test.go | 130 ++++++++++++++- internal/api/grpc/org/v2beta/org.go | 46 ++++-- internal/command/org_domain.go | 3 + proto/zitadel/org/v2beta/org_service.proto | 155 +++++++++++++++++- 6 files changed, 397 insertions(+), 46 deletions(-) diff --git a/internal/api/grpc/object/v2beta/converter.go b/internal/api/grpc/object/v2beta/converter.go index a28f2cf0bb..89a5e8bc7b 100644 --- a/internal/api/grpc/object/v2beta/converter.go +++ b/internal/api/grpc/object/v2beta/converter.go @@ -138,3 +138,25 @@ func ToViewDetailsPb( } return details } + +func DomainToChangeDetailsPb(objectDetail *domain.ObjectDetails) *object.Details { + details := &object.Details{ + Sequence: objectDetail.Sequence, + ResourceOwner: objectDetail.ResourceOwner, + } + if !objectDetail.EventDate.IsZero() { + details.ChangeDate = timestamppb.New(objectDetail.EventDate) + } + return details +} + +func DomainValidationTypeToDomain(validationType org_pb.DomainValidationType) domain.OrgDomainValidationType { + switch validationType { + case org_pb.DomainValidationType_DOMAIN_VALIDATION_TYPE_HTTP: + return domain.OrgDomainValidationTypeHTTP + case org_pb.DomainValidationType_DOMAIN_VALIDATION_TYPE_DNS: + return domain.OrgDomainValidationTypeDNS + default: + return domain.OrgDomainValidationTypeUnspecified + } +} diff --git a/internal/api/grpc/org/v2beta/helper.go b/internal/api/grpc/org/v2beta/helper.go index cfd373518a..c36120a258 100644 --- a/internal/api/grpc/org/v2beta/helper.go +++ b/internal/api/grpc/org/v2beta/helper.go @@ -1,19 +1,23 @@ package org import ( + "context" "time" + "github.com/zitadel/zitadel/internal/api/authz" v2beta_object "github.com/zitadel/zitadel/internal/api/grpc/object/v2beta" "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/query" "github.com/zitadel/zitadel/internal/zerrors" + + // TODO fix below org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta" v2beta_org "github.com/zitadel/zitadel/pkg/grpc/org/v2beta" v2beta "github.com/zitadel/zitadel/pkg/grpc/object/v2beta" - org_pb "github.com/zitadel/zitadel/pkg/grpc/org/v2beta" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -22,7 +26,6 @@ import ( func listOrgRequestToModel(request *v2beta_org.ListOrganizationsRequest) (*query.OrgSearchQueries, error) { offset, limit, asc := v2beta_object.ListQueryToModel(request.Query) - // queries, err := org_pb.OrgQueriesToModel(request.Queries) queries, err := OrgQueriesToModel(request.Queries) if err != nil { return nil, err @@ -38,8 +41,8 @@ func listOrgRequestToModel(request *v2beta_org.ListOrganizationsRequest) (*query }, nil } -func OrganizationViewToPb(org *query.Org) *org_pb.Organization { - return &org_pb.Organization{ +func OrganizationViewToPb(org *query.Org) *v2beta_org.Organization { + return &v2beta_org.Organization{ Id: org.ID, State: OrgStateToPb(org.State), Name: org.Name, @@ -53,14 +56,14 @@ func OrganizationViewToPb(org *query.Org) *org_pb.Organization { } } -func OrgStateToPb(state domain.OrgState) org_pb.OrgState { +func OrgStateToPb(state domain.OrgState) v2beta_org.OrgState { switch state { case domain.OrgStateActive: - return org_pb.OrgState_ORG_STATE_ACTIVE + return v2beta_org.OrgState_ORG_STATE_ACTIVE case domain.OrgStateInactive: - return org_pb.OrgState_ORG_STATE_INACTIVE + return v2beta_org.OrgState_ORG_STATE_INACTIVE default: - return org_pb.OrgState_ORG_STATE_UNSPECIFIED + return v2beta_org.OrgState_ORG_STATE_UNSPECIFIED } } @@ -99,15 +102,15 @@ func createdOrganizationToPb(createdOrg *command.CreatedOrg) (_ *org.CreateOrgan }, nil } -func OrgViewsToPb(orgs []*query.Org) []*org_pb.Organization { - o := make([]*org_pb.Organization, len(orgs)) +func OrgViewsToPb(orgs []*query.Org) []*v2beta_org.Organization { + o := make([]*v2beta_org.Organization, len(orgs)) for i, org := range orgs { o[i] = OrgViewToPb(org) } return o } -func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []query.SearchQuery, err error) { +func OrgQueriesToModel(queries []*v2beta_org.OrgQuery) (_ []query.SearchQuery, err error) { q := make([]query.SearchQuery, len(queries)) for i, query := range queries { q[i], err = OrgQueryToModel(query) @@ -118,47 +121,47 @@ func OrgQueriesToModel(queries []*org_pb.OrgQuery) (_ []query.SearchQuery, err e return q, nil } -func OrgQueryToModel(apiQuery *org_pb.OrgQuery) (query.SearchQuery, error) { +func OrgQueryToModel(apiQuery *v2beta_org.OrgQuery) (query.SearchQuery, error) { switch q := apiQuery.Query.(type) { - case *org_pb.OrgQuery_DomainQuery: + case *v2beta_org.OrgQuery_DomainQuery: return query.NewOrgVerifiedDomainSearchQuery(v2beta_object.TextMethodToQuery(q.DomainQuery.Method), q.DomainQuery.Domain) - case *org_pb.OrgQuery_NameQuery: + case *v2beta_org.OrgQuery_NameQuery: return query.NewOrgNameSearchQuery(v2beta_object.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name) - case *org_pb.OrgQuery_StateQuery: + case *v2beta_org.OrgQuery_StateQuery: return query.NewOrgStateSearchQuery(OrgStateToDomain(q.StateQuery.State)) - case *org_pb.OrgQuery_IdQuery: + case *v2beta_org.OrgQuery_IdQuery: return query.NewOrgIDSearchQuery(q.IdQuery.Id) default: return nil, zerrors.ThrowInvalidArgument(nil, "ORG-vR9nC", "List.Query.Invalid") } } -func OrgStateToDomain(state org_pb.OrgState) domain.OrgState { +func OrgStateToDomain(state v2beta_org.OrgState) domain.OrgState { switch state { - case org_pb.OrgState_ORG_STATE_ACTIVE: + case v2beta_org.OrgState_ORG_STATE_ACTIVE: return domain.OrgStateActive - case org_pb.OrgState_ORG_STATE_INACTIVE: + case v2beta_org.OrgState_ORG_STATE_INACTIVE: return domain.OrgStateInactive - case org_pb.OrgState_ORG_STATE_UNSPECIFIED: + case v2beta_org.OrgState_ORG_STATE_UNSPECIFIED: fallthrough default: return domain.OrgStateUnspecified } } -func FieldNameToOrgColumn(fieldName org_pb.OrgFieldName) query.Column { +func FieldNameToOrgColumn(fieldName v2beta_org.OrgFieldName) query.Column { switch fieldName { - case org_pb.OrgFieldName_ORG_FIELD_NAME_NAME: + case v2beta_org.OrgFieldName_ORG_FIELD_NAME_NAME: return query.OrgColumnName - case org_pb.OrgFieldName_ORG_FIELD_NAME_UNSPECIFIED: + case v2beta_org.OrgFieldName_ORG_FIELD_NAME_UNSPECIFIED: return query.Column{} default: return query.Column{} } } -func OrgViewToPb(org *query.Org) *org_pb.Organization { - return &org_pb.Organization{ +func OrgViewToPb(org *query.Org) *v2beta_org.Organization { + return &v2beta_org.Organization{ Id: org.ID, State: OrgStateToPb(org.State), Name: org.Name, @@ -197,7 +200,7 @@ func ListQueryToModel(query *v2beta.ListQuery) (offset, limit uint64, asc bool) return query.Offset, uint64(query.Limit), query.Asc } -func DomainQueriesToModel(queries []*org_pb.DomainSearchQuery) (_ []query.SearchQuery, err error) { +func DomainQueriesToModel(queries []*v2beta_org.DomainSearchQuery) (_ []query.SearchQuery, err error) { q := make([]query.SearchQuery, len(queries)) for i, query := range queries { q[i], err = DomainQueryToModel(query) @@ -208,12 +211,40 @@ func DomainQueriesToModel(queries []*org_pb.DomainSearchQuery) (_ []query.Search return q, nil } -func DomainQueryToModel(searchQuery *org_pb.DomainSearchQuery) (query.SearchQuery, error) { +func DomainQueryToModel(searchQuery *v2beta_org.DomainSearchQuery) (query.SearchQuery, error) { switch q := searchQuery.Query.(type) { - case *org_pb.DomainSearchQuery_DomainNameQuery: + case *v2beta_org.DomainSearchQuery_DomainNameQuery: // return query.NewOrgDomainDomainSearchQuery(object.TextMethodToQuery(q.DomainNameQuery.Method), q.DomainNameQuery.Name) return query.NewOrgDomainDomainSearchQuery(v2beta_object.TextMethodToQuery(q.DomainNameQuery.Method), q.DomainNameQuery.Name) default: return nil, zerrors.ThrowInvalidArgument(nil, "ORG-Ags89", "List.Query.Invalid") } } + +func RemoveOrgDomainRequestToDomain(ctx context.Context, req *v2beta_org.DeleteOrganizationDomainRequest) *domain.OrgDomain { + return &domain.OrgDomain{ + ObjectRoot: models.ObjectRoot{ + AggregateID: req.OrganizationId, + }, + Domain: req.Domain, + } +} + +func GenerateOrgDomainValidationRequestToDomain(ctx context.Context, req *v2beta_org.GenerateOrganizationDomainValidationRequest) *domain.OrgDomain { + return &domain.OrgDomain{ + ObjectRoot: models.ObjectRoot{ + AggregateID: req.OrganizationId, + }, + Domain: req.Domain, + ValidationType: v2beta_object.DomainValidationTypeToDomain(req.Type), + } +} + +func ValidateOrgDomainRequestToDomain(ctx context.Context, req *v2beta_org.VerifyOrganizationDomainRequest) *domain.OrgDomain { + return &domain.OrgDomain{ + ObjectRoot: models.ObjectRoot{ + AggregateID: authz.GetCtxData(ctx).OrgID, + }, + Domain: req.Domain, + } +} diff --git a/internal/api/grpc/org/v2beta/integration_test/org_test.go b/internal/api/grpc/org/v2beta/integration_test/org_test.go index 2c90462838..7232c4febc 100644 --- a/internal/api/grpc/org/v2beta/integration_test/org_test.go +++ b/internal/api/grpc/org/v2beta/integration_test/org_test.go @@ -455,7 +455,7 @@ func TestServer_DeactivateReactivateOrganization(t *testing.T) { Id: orgId, }) require.NoError(t, err) - require.Equal(t, v2beta_org.OrganizationState_ORGANIZATION_STATE_ACTIVE, res.Organization.State) + require.Equal(t, v2beta_org.OrgState_ORG_STATE_ACTIVE, res.Organization.State) // 3. deactivate organization once deactivate_res, err := Client.DeactivateOrganization(ctx, &v2beta_org.DeactivateOrganizationRequest{ @@ -473,7 +473,7 @@ func TestServer_DeactivateReactivateOrganization(t *testing.T) { Id: orgId, }) require.NoError(t, err) - require.Equal(t, v2beta_org.OrganizationState_ORGANIZATION_STATE_INACTIVE, res.Organization.State) + require.Equal(t, v2beta_org.OrgState_ORG_STATE_INACTIVE, res.Organization.State) // 5. repeat deactivate organization once deactivate_res, err = Client.DeactivateOrganization(ctx, &v2beta_org.DeactivateOrganizationRequest{ @@ -491,7 +491,7 @@ func TestServer_DeactivateReactivateOrganization(t *testing.T) { Id: orgId, }) require.NoError(t, err) - require.Equal(t, v2beta_org.OrganizationState_ORGANIZATION_STATE_INACTIVE, res.Organization.State) + require.Equal(t, v2beta_org.OrgState_ORG_STATE_INACTIVE, res.Organization.State) // 7. reactivate organization reactivate_res, err := Client.ReactivateOrganization(ctx, &v2beta_org.ReactivateOrganizationRequest{ @@ -509,7 +509,7 @@ func TestServer_DeactivateReactivateOrganization(t *testing.T) { Id: orgId, }) require.NoError(t, err) - require.Equal(t, v2beta_org.OrganizationState_ORGANIZATION_STATE_ACTIVE, res.Organization.State) + require.Equal(t, v2beta_org.OrgState_ORG_STATE_ACTIVE, res.Organization.State) // 9. repeat reactivate organization reactivate_res, err = Client.ReactivateOrganization(ctx, &v2beta_org.ReactivateOrganizationRequest{ @@ -527,7 +527,7 @@ func TestServer_DeactivateReactivateOrganization(t *testing.T) { Id: orgId, }) require.NoError(t, err) - require.Equal(t, v2beta_org.OrganizationState_ORGANIZATION_STATE_ACTIVE, res.Organization.State) + require.Equal(t, v2beta_org.OrgState_ORG_STATE_ACTIVE, res.Organization.State) } func createOrgs(noOfOrgs int) ([]*v2beta_org.CreateOrganizationResponse, []string, error) { @@ -551,6 +551,126 @@ func createOrgs(noOfOrgs int) ([]*v2beta_org.CreateOrganizationResponse, []strin return orgs, orgsName, nil } +func TestServer_AddOListDeleterganizationDomain(t *testing.T) { + // 1. create organization + orgs, _, err := createOrgs(1) + if err != nil { + assert.Fail(t, "unable to create org") + } + orgId := orgs[0].OrganizationId + ctx := Instance.WithAuthorization(context.Background(), integration.UserTypeIAMOwner) + + domain := "www.domain.com" + // 2. add domain + addOrgDomainRes, err := Client.AddOrganizationDomain(ctx, &v2beta_org.AddOrganizationDomainRequest{ + OrganizationId: orgId, + Domain: domain, + }) + require.NoError(t, err) + + // check details + assert.NotZero(t, addOrgDomainRes.GetDetails().GetSequence()) + gotCD := addOrgDomainRes.GetDetails().GetChangeDate().AsTime() + now := time.Now() + assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute)) + assert.NotEmpty(t, addOrgDomainRes.GetDetails().GetResourceOwner()) + + // 2. check domain is added + queryRes, err := Client.ListOrganizationDomains(CTX, &v2beta_org.ListOrganizationDomainsRequest{ + OrganizationId: orgId, + }) + require.NoError(t, err) + found := false + for _, res := range queryRes.Result { + if res.DomainName == domain { + found = true + } + } + require.True(t, found, "unable to find added domain") + + // 3. readd domain + _, err = Client.AddOrganizationDomain(ctx, &v2beta_org.AddOrganizationDomainRequest{ + OrganizationId: orgId, + Domain: domain, + }) + // TODO remove error for adding already existing domain + // require.NoError(t, err) + require.Contains(t, err.Error(), "Errors.Already.Exists") + // check details + // assert.NotZero(t, addOrgDomainRes.GetDetails().GetSequence()) + // gotCD = addOrgDomainRes.GetDetails().GetChangeDate().AsTime() + // now = time.Now() + // assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute)) + // assert.NotEmpty(t, addOrgDomainRes.GetDetails().GetResourceOwner()) + + // 4. check domain is added + queryRes, err = Client.ListOrganizationDomains(CTX, &v2beta_org.ListOrganizationDomainsRequest{ + OrganizationId: orgId, + }) + require.NoError(t, err) + found = false + for _, res := range queryRes.Result { + if res.DomainName == domain { + found = true + } + } + require.True(t, found, "unable to find added domain") + + // 5. delete organisation domain + deleteOrgDomainRes, err := Client.DeleteOrganizationDomain(ctx, &v2beta_org.DeleteOrganizationDomainRequest{ + OrganizationId: orgId, + Domain: domain, + }) + require.NoError(t, err) + // check details + assert.NotZero(t, deleteOrgDomainRes.GetDetails().GetSequence()) + gotCD = deleteOrgDomainRes.GetDetails().GetChangeDate().AsTime() + now = time.Now() + assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute)) + assert.NotEmpty(t, deleteOrgDomainRes.GetDetails().GetResourceOwner()) + + // 6. check organization domain deleted + queryRes, err = Client.ListOrganizationDomains(CTX, &v2beta_org.ListOrganizationDomainsRequest{ + OrganizationId: orgId, + }) + require.NoError(t, err) + found = false + for _, res := range queryRes.Result { + if res.DomainName == domain { + found = true + } + } + require.False(t, found, "deleted domain found") + + // 7. redelete organisation domain + _, err = Client.DeleteOrganizationDomain(ctx, &v2beta_org.DeleteOrganizationDomainRequest{ + OrganizationId: orgId, + Domain: domain, + }) + // TODO remove error for deleting org domain already deleted + // require.NoError(t, err) + require.Contains(t, err.Error(), "Domain doesn't exist on organization") + // check details + // assert.NotZero(t, deleteOrgDomainRes.GetDetails().GetSequence()) + // gotCD = deleteOrgDomainRes.GetDetails().GetChangeDate().AsTime() + // now = time.Now() + // assert.WithinRange(t, gotCD, now.Add(-time.Minute), now.Add(time.Minute)) + // assert.NotEmpty(t, deleteOrgDomainRes.GetDetails().GetResourceOwner()) + + // 8. check organization domain deleted + queryRes, err = Client.ListOrganizationDomains(CTX, &v2beta_org.ListOrganizationDomainsRequest{ + OrganizationId: orgId, + }) + require.NoError(t, err) + found = false + for _, res := range queryRes.Result { + if res.DomainName == domain { + found = true + } + } + require.False(t, found, "deleted domain found") +} + func assertCreatedAdmin(t *testing.T, expected, got *v2beta_org.CreateOrganizationResponse_CreatedAdmin) { if expected.GetUserId() != "" { assert.NotEmpty(t, got.GetUserId()) diff --git a/internal/api/grpc/org/v2beta/org.go b/internal/api/grpc/org/v2beta/org.go index b38d2ade5e..42c79949fb 100644 --- a/internal/api/grpc/org/v2beta/org.go +++ b/internal/api/grpc/org/v2beta/org.go @@ -3,7 +3,6 @@ package org import ( "context" - "github.com/zitadel/zitadel/internal/api/authz" object "github.com/zitadel/zitadel/internal/api/grpc/object/v2beta" user "github.com/zitadel/zitadel/internal/api/grpc/user/v2beta" "github.com/zitadel/zitadel/internal/command" @@ -110,7 +109,7 @@ func (s *Server) ListOrganizationDomains(ctx context.Context, req *org.ListOrgan if err != nil { return nil, err } - orgIDQuery, err := query.NewOrgDomainOrgIDSearchQuery(authz.GetCtxData(ctx).OrgID) + orgIDQuery, err := query.NewOrgDomainOrgIDSearchQuery(req.OrganizationId) if err != nil { return nil, err } @@ -126,15 +125,40 @@ func (s *Server) ListOrganizationDomains(ctx context.Context, req *org.ListOrgan }, nil } -// func (s *Server) RemoveOrganizagtionDomain(ctx context.Context, req *mgmt_pb.RemoveOrgDomainRequest) (*mgmt_pb.RemoveOrgDomainResponse, error) { -// details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req)) -// if err != nil { -// return nil, err -// } -// return &mgmt_pb.RemoveOrgDomainResponse{ -// Details: object.DomainToChangeDetailsPb(details), -// }, err -// } +func (s *Server) DeleteOrganizationDomain(ctx context.Context, req *org.DeleteOrganizationDomainRequest) (*org.DeleteOrganizationDomainResponse, error) { + details, err := s.command.RemoveOrgDomain(ctx, RemoveOrgDomainRequestToDomain(ctx, req)) + if err != nil { + return nil, err + } + return &org.DeleteOrganizationDomainResponse{ + Details: object.DomainToDetailsPb(details), + }, err +} + +func (s *Server) GenerateOrganizationDomainValidation(ctx context.Context, req *org.GenerateOrganizationDomainValidationRequest) (*org.GenerateOrganizationDomainValidationResponse, error) { + token, url, err := s.command.GenerateOrgDomainValidation(ctx, GenerateOrgDomainValidationRequestToDomain(ctx, req)) + if err != nil { + return nil, err + } + return &org.GenerateOrganizationDomainValidationResponse{ + Token: token, + Url: url, + }, nil +} + +func (s *Server) VerifyOrganizationDomain(ctx context.Context, request *org.VerifyOrganizationDomainRequest) (*org.VerifyOrganizationDomainResponse, error) { + userIDs, err := s.getClaimedUserIDsOfOrgDomain(ctx, request.Domain, request.OrganizationId) + if err != nil { + return nil, err + } + details, err := s.command.ValidateOrgDomain(ctx, ValidateOrgDomainRequestToDomain(ctx, request), userIDs) + if err != nil { + return nil, err + } + return &org.VerifyOrganizationDomainResponse{ + Details: object.DomainToChangeDetailsPb(details), + }, nil +} func createOrganizationRequestToCommand(request *v2beta_org.CreateOrganizationRequest) (*command.OrgSetup, error) { admins, err := createOrganizationRequestAdminsToCommand(request.GetAdmins()) diff --git a/internal/command/org_domain.go b/internal/command/org_domain.go index 2e132f6c47..fe9999454f 100644 --- a/internal/command/org_domain.go +++ b/internal/command/org_domain.go @@ -3,6 +3,7 @@ package command import ( "context" "errors" + "fmt" "strings" "github.com/zitadel/logging" @@ -244,6 +245,7 @@ func (c *Commands) SetPrimaryOrgDomain(ctx context.Context, orgDomain *domain.Or return nil, err } if domainWriteModel.State != domain.OrgDomainStateActive { + fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> domainWriteModel.State = %+v\n", domainWriteModel.State) return nil, zerrors.ThrowNotFound(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg") } if !domainWriteModel.Verified { @@ -270,6 +272,7 @@ func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDom return nil, err } if domainWriteModel.State != domain.OrgDomainStateActive { + fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> domainWriteModel.State = %+v\n", domainWriteModel.State) return nil, zerrors.ThrowNotFound(nil, "ORG-GDfA3", "Errors.Org.DomainNotOnOrg") } if domainWriteModel.Primary { diff --git a/proto/zitadel/org/v2beta/org_service.proto b/proto/zitadel/org/v2beta/org_service.proto index 753ff0569a..3e6556853e 100644 --- a/proto/zitadel/org/v2beta/org_service.proto +++ b/proto/zitadel/org/v2beta/org_service.proto @@ -570,6 +570,95 @@ service OrganizationService { }; } + rpc DeleteOrganizationDomain(DeleteOrganizationDomainRequest) returns (DeleteOrganizationDomainResponse) { + option (google.api.http) = { + delete: "/v2beta/organization/domains" + }; + + option (zitadel.protoc_gen_zitadel.v2.options) = { + auth_option: { + permission: "org.write" + } + http_response: { + success_code: 200 + } + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Organizations"; + summary: "Delete Domain"; + description: "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." + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get/set a result of another organization include the header. Make sure the user has permission to access the requested data."; + type: STRING, + required: false; + }; + }; + }; + } + + rpc GenerateOrganizationDomainValidation(GenerateOrganizationDomainValidationRequest) returns (GenerateOrganizationDomainValidationResponse) { + option (google.api.http) = { + post: "/v2beta/organization/domains/validation/_generate" + body: "*" + }; + + option (zitadel.protoc_gen_zitadel.v2.options) = { + auth_option: { + permission: "org.write" + } + http_response: { + success_code: 201 + } + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Organizations"; + summary: "Generate Domain Verification"; + description: "Generate a new file to be able to verify your domain with DNS or HTTP challenge." + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get/set a result of another organization include the header. Make sure the user has permission to access the requested data."; + type: STRING, + required: false; + }; + }; + }; + } + + rpc VerifyOrganizationDomain(VerifyOrganizationDomainRequest) returns (VerifyOrganizationDomainResponse) { + option (google.api.http) = { + post: "/v2beta/organization/domains/validation/_validate" + body: "*" + }; + + option (zitadel.protoc_gen_zitadel.v2.options) = { + auth_option: { + permission: "org.write" + } + http_response: { + success_code: 200 + } + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + tags: "Organizations"; + summary: "Verify Domain"; + description: "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." + parameters: { + headers: { + name: "x-zitadel-orgid"; + description: "The default is always the organization of the requesting user. If you like to get/set a result of another organization include the header. Make sure the user has permission to access the requested data."; + type: STRING, + required: false; + }; + }; + }; + } + } message CreateOrganizationRequest{ @@ -766,13 +855,75 @@ message AddOrganizationDomainResponse { } message ListOrganizationDomainsRequest { + string organization_id = 1; //list limitations and ordering - zitadel.object.v2beta.ListQuery query = 1; + zitadel.object.v2beta.ListQuery query = 2; //criteria the client is looking for - repeated DomainSearchQuery queries = 2; + repeated DomainSearchQuery queries = 3; } message ListOrganizationDomainsResponse { zitadel.object.v2beta.ListDetails details = 1; repeated Domain result = 2; } + +message DeleteOrganizationDomainRequest { + // TODO maybe change organization_id to just Id + string organization_id = 1; + 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 { + zitadel.object.v2beta.Details details = 1; +} + +message GenerateOrganizationDomainValidationRequest { + string organization_id = 1; + 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\""; + } + ]; + DomainValidationType type = 3 [(validate.rules).enum = {defined_only: true, not_in: [0]}]; +} + +message GenerateOrganizationDomainValidationResponse { + string token = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"ofSBHsSAVHAoTIE4Iv2gwhaYhTjcY5QX\""; + } + ]; + string url = 2 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "\"https://testdomain.com/.well-known/zitadel-challenge/ofSBHsSAVHAoTIE4Iv2gwhaYhTjcY5QX\""; + } + ]; +} + +message VerifyOrganizationDomainRequest { + string organization_id = 1; + string domain = 2 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + min_length: 1; + max_length: 200; + example: "\"testdomain.com\""; + } + ]; +} + +message VerifyOrganizationDomainResponse { + zitadel.object.v2beta.Details details = 1; +}