Files
zitadel/internal/api/grpc/settings/v2/query.go
T
Livio SpringandGitHub 8d5f92f80a feat(api): move organization settings endpoints to v2 (#10910)
# Which Problems Are Solved

As part of our efforts to simplify the structure and versions of our
APIs, were moving all existing v2beta endpoints to v2 and deprecate
them. They will be removed in Zitadel V5.

# How the Problems Are Solved

- This PR adds the organization settings endpoints to the settings
service v2. This was split from #10909, since that PR targets v4.x and
the corresponding feature is not yet available in v4.x, but only v5.
- The comments and have been improved and, where not already done, moved
from swagger annotations to proto.

# Additional Changes

None

# Additional Context

- relates to #10909 
- relates to #10772
2025-10-17 11:21:01 +02:00

314 lines
12 KiB
Go

package settings
import (
"context"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
filter_pb "github.com/zitadel/zitadel/pkg/grpc/filter/v2"
object_pb "github.com/zitadel/zitadel/pkg/grpc/object/v2"
"github.com/zitadel/zitadel/pkg/grpc/settings/v2"
)
func (s *Server) GetLoginSettings(ctx context.Context, req *connect.Request[settings.GetLoginSettingsRequest]) (*connect.Response[settings.GetLoginSettingsResponse], error) {
current, err := s.query.LoginPolicyByID(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetLoginSettingsResponse{
Settings: loginSettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.OrgID,
},
}), nil
}
func (s *Server) GetPasswordComplexitySettings(ctx context.Context, req *connect.Request[settings.GetPasswordComplexitySettingsRequest]) (*connect.Response[settings.GetPasswordComplexitySettingsResponse], error) {
current, err := s.query.PasswordComplexityPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetPasswordComplexitySettingsResponse{
Settings: passwordComplexitySettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetPasswordExpirySettings(ctx context.Context, req *connect.Request[settings.GetPasswordExpirySettingsRequest]) (*connect.Response[settings.GetPasswordExpirySettingsResponse], error) {
current, err := s.query.PasswordAgePolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetPasswordExpirySettingsResponse{
Settings: passwordExpirySettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetBrandingSettings(ctx context.Context, req *connect.Request[settings.GetBrandingSettingsRequest]) (*connect.Response[settings.GetBrandingSettingsResponse], error) {
current, err := s.query.ActiveLabelPolicyByOrg(ctx, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetBrandingSettingsResponse{
Settings: brandingSettingsToPb(current, s.assetsAPIDomain(ctx)),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetDomainSettings(ctx context.Context, req *connect.Request[settings.GetDomainSettingsRequest]) (*connect.Response[settings.GetDomainSettingsResponse], error) {
current, err := s.query.DomainPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetDomainSettingsResponse{
Settings: domainSettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetLegalAndSupportSettings(ctx context.Context, req *connect.Request[settings.GetLegalAndSupportSettingsRequest]) (*connect.Response[settings.GetLegalAndSupportSettingsResponse], error) {
current, err := s.query.PrivacyPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetLegalAndSupportSettingsResponse{
Settings: legalAndSupportSettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetLockoutSettings(ctx context.Context, req *connect.Request[settings.GetLockoutSettingsRequest]) (*connect.Response[settings.GetLockoutSettingsResponse], error) {
current, err := s.query.LockoutPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()))
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetLockoutSettingsResponse{
Settings: lockoutSettingsToPb(current),
Details: &object_pb.Details{
Sequence: current.Sequence,
CreationDate: timestamppb.New(current.CreationDate),
ChangeDate: timestamppb.New(current.ChangeDate),
ResourceOwner: current.ResourceOwner,
},
}), nil
}
func (s *Server) GetActiveIdentityProviders(ctx context.Context, req *connect.Request[settings.GetActiveIdentityProvidersRequest]) (*connect.Response[settings.GetActiveIdentityProvidersResponse], error) {
queries, err := activeIdentityProvidersToQuery(req.Msg)
if err != nil {
return nil, err
}
links, err := s.query.IDPLoginPolicyLinks(ctx, object.ResourceOwnerFromReq(ctx, req.Msg.GetCtx()), &query.IDPLoginPolicyLinksSearchQuery{Queries: queries}, false)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetActiveIdentityProvidersResponse{
Details: object.ToListDetails(links.SearchResponse),
IdentityProviders: identityProvidersToPb(links.Links),
}), nil
}
func activeIdentityProvidersToQuery(req *settings.GetActiveIdentityProvidersRequest) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, 0, 4)
if req.CreationAllowed != nil {
creationQuery, err := query.NewIDPTemplateIsCreationAllowedSearchQuery(*req.CreationAllowed)
if err != nil {
return nil, err
}
q = append(q, creationQuery)
}
if req.LinkingAllowed != nil {
creationQuery, err := query.NewIDPTemplateIsLinkingAllowedSearchQuery(*req.LinkingAllowed)
if err != nil {
return nil, err
}
q = append(q, creationQuery)
}
if req.AutoCreation != nil {
creationQuery, err := query.NewIDPTemplateIsAutoCreationSearchQuery(*req.AutoCreation)
if err != nil {
return nil, err
}
q = append(q, creationQuery)
}
if req.AutoLinking != nil {
compare := query.NumberEquals
if *req.AutoLinking {
compare = query.NumberNotEquals
}
creationQuery, err := query.NewIDPTemplateAutoLinkingSearchQuery(0, compare)
if err != nil {
return nil, err
}
q = append(q, creationQuery)
}
return q, nil
}
func (s *Server) GetGeneralSettings(ctx context.Context, _ *connect.Request[settings.GetGeneralSettingsRequest]) (*connect.Response[settings.GetGeneralSettingsResponse], error) {
instance := authz.GetInstance(ctx)
return connect.NewResponse(&settings.GetGeneralSettingsResponse{
SupportedLanguages: domain.LanguagesToStrings(i18n.SupportedLanguages()),
DefaultOrgId: instance.DefaultOrganisationID(),
DefaultLanguage: instance.DefaultLanguage().String(),
}), nil
}
func (s *Server) GetSecuritySettings(ctx context.Context, req *connect.Request[settings.GetSecuritySettingsRequest]) (*connect.Response[settings.GetSecuritySettingsResponse], error) {
policy, err := s.query.SecurityPolicy(ctx)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.GetSecuritySettingsResponse{
Settings: securityPolicyToSettingsPb(policy),
}), nil
}
func (s *Server) GetHostedLoginTranslation(ctx context.Context, req *connect.Request[settings.GetHostedLoginTranslationRequest]) (*connect.Response[settings.GetHostedLoginTranslationResponse], error) {
translation, err := s.query.GetHostedLoginTranslation(ctx, req.Msg)
if err != nil {
return nil, err
}
return connect.NewResponse(translation), nil
}
func (s *Server) ListOrganizationSettings(ctx context.Context, req *connect.Request[settings.ListOrganizationSettingsRequest]) (*connect.Response[settings.ListOrganizationSettingsResponse], error) {
queries, err := s.listOrganizationSettingsRequestToModel(req.Msg)
if err != nil {
return nil, err
}
resp, err := s.query.SearchOrganizationSettings(ctx, queries, s.checkPermission)
if err != nil {
return nil, err
}
return connect.NewResponse(&settings.ListOrganizationSettingsResponse{
OrganizationSettings: organizationSettingsListToPb(resp.OrganizationSettingsList),
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
}), nil
}
func (s *Server) listOrganizationSettingsRequestToModel(req *settings.ListOrganizationSettingsRequest) (*query.OrganizationSettingsSearchQueries, error) {
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
if err != nil {
return nil, err
}
queries, err := organizationSettingsFiltersToQuery(req.Filters)
if err != nil {
return nil, err
}
return &query.OrganizationSettingsSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
SortingColumn: organizationSettingsFieldNameToSortingColumn(req.SortingColumn),
},
Queries: queries,
}, nil
}
func organizationSettingsFieldNameToSortingColumn(field *settings.OrganizationSettingsFieldName) query.Column {
if field == nil {
return query.OrganizationSettingsColumnCreationDate
}
switch *field {
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_CREATION_DATE:
return query.OrganizationSettingsColumnCreationDate
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_ORGANIZATION_ID:
return query.OrganizationSettingsColumnID
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_CHANGE_DATE:
return query.OrganizationSettingsColumnChangeDate
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_UNSPECIFIED:
return query.OrganizationSettingsColumnCreationDate
default:
return query.OrganizationSettingsColumnCreationDate
}
}
func organizationSettingsFiltersToQuery(queries []*settings.OrganizationSettingsSearchFilter) (_ []query.SearchQuery, err error) {
q := make([]query.SearchQuery, len(queries))
for i, qry := range queries {
q[i], err = organizationSettingsToModel(qry)
if err != nil {
return nil, err
}
}
return q, nil
}
func organizationSettingsToModel(filter *settings.OrganizationSettingsSearchFilter) (query.SearchQuery, error) {
switch q := filter.Filter.(type) {
case *settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter:
return organizationInIDsFilterToQuery(q.InOrganizationIdsFilter)
case *settings.OrganizationSettingsSearchFilter_OrganizationScopedUsernamesFilter:
return organizationScopedUsernamesFilterToQuery(q.OrganizationScopedUsernamesFilter)
default:
return nil, zerrors.ThrowInvalidArgument(nil, "SETTINGS-uvTDqZHlvS", "List.Query.Invalid")
}
}
func organizationInIDsFilterToQuery(q *filter_pb.InIDsFilter) (query.SearchQuery, error) {
return query.NewOrganizationSettingsOrganizationIDSearchQuery(q.Ids)
}
func organizationScopedUsernamesFilterToQuery(q *settings.OrganizationScopedUsernamesFilter) (query.SearchQuery, error) {
return query.NewOrganizationSettingsOrganizationScopedUsernamesSearchQuery(q.OrganizationScopedUsernames)
}
func organizationSettingsListToPb(settingsList []*query.OrganizationSettings) []*settings.OrganizationSettings {
o := make([]*settings.OrganizationSettings, len(settingsList))
for i, organizationSettings := range settingsList {
o[i] = organizationSettingsToPb(organizationSettings)
}
return o
}
func organizationSettingsToPb(organizationSettings *query.OrganizationSettings) *settings.OrganizationSettings {
return &settings.OrganizationSettings{
OrganizationId: organizationSettings.ID,
CreationDate: timestamppb.New(organizationSettings.CreationDate),
ChangeDate: timestamppb.New(organizationSettings.ChangeDate),
OrganizationScopedUsernames: organizationSettings.OrganizationScopedUsernames,
}
}