feat: permit all features to every instance and organisation (#3566)

This commit is contained in:
Livio Amstutz 2022-05-02 11:18:17 +02:00 committed by GitHub
parent a9f71ba08e
commit 861cf07700
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 90 additions and 6589 deletions

View File

@ -50,7 +50,6 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
mig.zitadelRoles,
nil,
nil,
nil,
mig.externalDomain,
mig.externalSecure,
mig.externalPort,

View File

@ -1,6 +1,8 @@
package start
import (
"time"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/zitadel/logging"
@ -49,6 +51,7 @@ type Config struct {
SystemDefaults systemdefaults.SystemDefaults
EncryptionKeys *encryptionKeyConfig
DefaultInstance command.InstanceSetup
AuditLogRetention time.Duration
}
func MustNewConfig(v *viper.Viper) *Config {

View File

@ -116,7 +116,6 @@ func startZitadel(config *Config, masterKey string) error {
config.SystemDefaults,
config.InternalAuthZ.RolePermissionMappings,
storage,
authZRepo,
webAuthNConfig,
config.ExternalDomain,
config.ExternalSecure,
@ -168,10 +167,10 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, assets.HandlerPrefix, keys.User)); err != nil {
return err
}
if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure, oidc.HandlerPrefix)); err != nil {
if err := authenticatedAPIs.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure, oidc.HandlerPrefix, config.AuditLogRetention)); err != nil {
return err
}
if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure)); err != nil {
if err := authenticatedAPIs.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil {
return err
}

View File

@ -228,29 +228,6 @@ DefaultInstance:
IncludeUpperLetters: true
IncludeDigits: true
IncludeSymbols: false
Features:
TierName: Default Tier
TierDescription: ""
State: 1 #active
StateDescription: ""
Retention: 8760h #1year
LoginPolicyFactors: true
LoginPolicyIDP: true
LoginPolicyPasswordless: true
LoginPolicyRegistration: true
LoginPolicyUsernameLogin: true
LoginPolicyPasswordReset: true
PasswordComplexityPolicy: true
LabelPolicyPrivateLabel: true
LabelPolicyWatermark: true
CustomDomain: true
PrivacyPolicy: true
MetadataUser: true
CustomTextMessage: true
CustomTextLogin: true
LockoutPolicy: true
ActionsAllowed: 2 #ActionsAllowedUnlimited
MaxActions: #not necessary because of ActionsAllowedUnlimited
PasswordComplexityPolicy:
MinLength: 8
HasLowercase: true
@ -383,8 +360,6 @@ InternalAuthZ:
Permissions:
- "iam.read"
- "iam.write"
- "iam.features.read"
- "iam.features.write"
- "iam.policy.read"
- "iam.policy.write"
- "iam.policy.delete"
@ -425,7 +400,6 @@ InternalAuthZ:
- "user.grant.delete"
- "user.membership.read"
- "user.credential.write"
- "features.read"
- "policy.read"
- "policy.write"
- "policy.delete"
@ -451,7 +425,6 @@ InternalAuthZ:
- Role: "IAM_OWNER_VIEWER"
Permissions:
- "iam.read"
- "iam.features.read"
- "iam.policy.read"
- "iam.member.read"
- "iam.idp.read"
@ -466,7 +439,6 @@ InternalAuthZ:
- "user.global.read"
- "user.grant.read"
- "user.membership.read"
- "features.read"
- "policy.read"
- "project.read"
- "project.member.read"
@ -501,7 +473,6 @@ InternalAuthZ:
- "user.grant.delete"
- "user.membership.read"
- "user.credential.write"
- "features.read"
- "policy.read"
- "policy.write"
- "policy.delete"
@ -538,7 +509,6 @@ InternalAuthZ:
- "user.grant.write"
- "user.grant.delete"
- "user.membership.read"
- "features.read"
- "project.read"
- "project.member.read"
- "project.role.read"
@ -574,7 +544,6 @@ InternalAuthZ:
- "user.grant.delete"
- "user.membership.read"
- "user.credential.write"
- "features.read"
- "policy.read"
- "policy.write"
- "policy.delete"
@ -619,7 +588,6 @@ InternalAuthZ:
- "user.global.read"
- "user.grant.read"
- "user.membership.read"
- "features.read"
- "policy.read"
- "project.read"
- "project.member.read"

View File

@ -112,7 +112,6 @@
"NEWVALUE": "Neuer Wert",
"RESTORE": "Wiederherstellen",
"CONTINUEWITHOUTSAVE": "Ohne speichern fortfahren",
"GOTOFEATURES": "Zu den Features",
"OF": "von",
"PREVIOUS": "Zurück",
"NEXT": "Weiter",

View File

@ -112,7 +112,6 @@
"NEWVALUE": "New Value",
"RESTORE": "Restore",
"CONTINUEWITHOUTSAVE": "Continue without saving",
"GOTOFEATURES": "Go to features",
"OF": "of",
"PREVIOUS": "Previous",
"NEXT": "Next",

View File

@ -112,7 +112,6 @@
"NEWVALUE": "Nuovo valore",
"RESTORE": "Ripristina",
"CONTINUEWITHOUTSAVE": "Continua senza salvare",
"GOTOFEATURES": "Vai alle caratteristiche",
"OF": "di",
"PREVIOUS": "Precedente",
"NEXT": "Avanti",

View File

@ -423,66 +423,6 @@ all fields are updated. If no value is provided the field will be empty afterwar
PUT: /idps/{idp_id}/jwt_config
### GetDefaultFeatures
> **rpc** GetDefaultFeatures([GetDefaultFeaturesRequest](#getdefaultfeaturesrequest))
[GetDefaultFeaturesResponse](#getdefaultfeaturesresponse)
GET: /features
### SetDefaultFeatures
> **rpc** SetDefaultFeatures([SetDefaultFeaturesRequest](#setdefaultfeaturesrequest))
[SetDefaultFeaturesResponse](#setdefaultfeaturesresponse)
PUT: /features
### GetOrgFeatures
> **rpc** GetOrgFeatures([GetOrgFeaturesRequest](#getorgfeaturesrequest))
[GetOrgFeaturesResponse](#getorgfeaturesresponse)
GET: /orgs/{org_id}/features
### SetOrgFeatures
> **rpc** SetOrgFeatures([SetOrgFeaturesRequest](#setorgfeaturesrequest))
[SetOrgFeaturesResponse](#setorgfeaturesresponse)
PUT: /orgs/{org_id}/features
### ResetOrgFeatures
> **rpc** ResetOrgFeatures([ResetOrgFeaturesRequest](#resetorgfeaturesrequest))
[ResetOrgFeaturesResponse](#resetorgfeaturesresponse)
DELETE: /orgs/{org_id}/features
### GetOrgIAMPolicy
> **rpc** GetOrgIAMPolicy([GetOrgIAMPolicyRequest](#getorgiampolicyrequest))
@ -1962,23 +1902,6 @@ This is an empty request
### GetDefaultFeaturesRequest
### GetDefaultFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| features | zitadel.features.v1.Features | - | |
### GetDefaultInitMessageTextRequest
@ -2291,28 +2214,6 @@ This is an empty request
### GetOrgFeaturesRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| org_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
### GetOrgFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| features | zitadel.features.v1.Features | - | |
### GetOrgIAMPolicyRequest
@ -3217,28 +3118,6 @@ This is an empty request
### ResetOrgFeaturesRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| org_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
### ResetOrgFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### SetCustomLoginTextsRequest
@ -3324,50 +3203,6 @@ This is an empty request
### SetDefaultFeaturesRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| tier_name | string | - | string.max_len: 200<br /> |
| description | string | - | string.max_len: 200<br /> |
| audit_log_retention | google.protobuf.Duration | - | duration.gte.seconds: 0<br /> duration.gte.nanos: 0<br /> |
| login_policy_username_login | bool | - | |
| login_policy_registration | bool | - | |
| login_policy_idp | bool | - | |
| login_policy_factors | bool | - | |
| login_policy_passwordless | bool | - | |
| password_complexity_policy | bool | - | |
| label_policy | bool | - | |
| custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
| label_policy_private_label | bool | - | |
| label_policy_watermark | bool | - | |
| custom_text | bool | - | |
| privacy_policy | bool | - | |
| metadata_user | bool | - | |
| custom_text_message | bool | - | |
| custom_text_login | bool | - | |
| lockout_policy | bool | - | |
| actions | bool | - | |
| actions_allowed | zitadel.features.v1.ActionsAllowed | - | |
| max_actions | int32 | - | |
### SetDefaultFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### SetDefaultInitMessageTextRequest
@ -3535,53 +3370,6 @@ This is an empty request
### SetOrgFeaturesRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| org_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| tier_name | string | - | string.max_len: 200<br /> |
| description | string | - | string.max_len: 200<br /> |
| state | zitadel.features.v1.FeaturesState | - | |
| state_description | string | - | string.max_len: 200<br /> |
| audit_log_retention | google.protobuf.Duration | - | duration.gte.seconds: 0<br /> duration.gte.nanos: 0<br /> |
| login_policy_username_login | bool | - | |
| login_policy_registration | bool | - | |
| login_policy_idp | bool | - | |
| login_policy_factors | bool | - | |
| login_policy_passwordless | bool | - | |
| password_complexity_policy | bool | - | |
| label_policy | bool | - | |
| custom_domain | bool | - | |
| login_policy_password_reset | bool | - | |
| label_policy_private_label | bool | - | |
| label_policy_watermark | bool | - | |
| custom_text | bool | - | |
| privacy_policy | bool | - | |
| metadata_user | bool | - | |
| custom_text_message | bool | - | |
| custom_text_login | bool | - | |
| lockout_policy | bool | - | |
| actions | bool | - | |
| actions_allowed | zitadel.features.v1.ActionsAllowed | - | |
| max_actions | int32 | - | |
### SetOrgFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### SetUpOrgRequest

View File

@ -533,18 +533,6 @@ Returns a list of organisations where the authorized user has a user grant (auth
POST: /global/projectorgs/_search
### ListMyZitadelFeatures
> **rpc** ListMyZitadelFeatures([ListMyZitadelFeaturesRequest](#listmyzitadelfeaturesrequest))
[ListMyZitadelFeaturesResponse](#listmyzitadelfeaturesresponse)
Returns a list of features, which are allowed on these organisation based on the subscription of the organisation
POST: /features/zitadel/me/_search
### ListMyZitadelPermissions
> **rpc** ListMyZitadelPermissions([ListMyZitadelPermissionsRequest](#listmyzitadelpermissionsrequest))
@ -1087,23 +1075,6 @@ This is an empty request
### ListMyZitadelFeaturesRequest
This is an empty request
### ListMyZitadelFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| result | repeated string | - | |
### ListMyZitadelPermissionsRequest
This is an empty request

View File

@ -1,25 +0,0 @@
---
title: zitadel/features.proto
---
## Enums
### FeaturesState {#featuresstate}
| Name | Number | Description |
| ---- | ------ | ----------- |
| FEATURES_STATE_ACTIVE | 0 | - |
| FEATURES_STATE_ACTION_REQUIRED | 1 | - |
| FEATURES_STATE_CANCELED | 2 | - |
| FEATURES_STATE_GRANDFATHERED | 3 | - |

View File

@ -1680,18 +1680,6 @@ remove a list of user grants in one request
DELETE: /user_grants/_bulk
### GetFeatures
> **rpc** GetFeatures([GetFeaturesRequest](#getfeaturesrequest))
[GetFeaturesResponse](#getfeaturesresponse)
GET: /features
### GetOrgIAMPolicy
> **rpc** GetOrgIAMPolicy([GetOrgIAMPolicyRequest](#getorgiampolicyrequest))
@ -4570,23 +4558,6 @@ This is an empty request
### GetFeaturesRequest
### GetFeaturesResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| features | zitadel.features.v1.Features | - | |
### GetFlowRequest

View File

@ -17,7 +17,6 @@ title: zitadel/options.proto
| ----- | ---- | ----------- | ----------- |
| permission | string | - | |
| check_field_name | string | - | |
| feature | string | - | |

View File

@ -54,7 +54,6 @@ Services:
Methods:
OrgLabelPolicyLogo:
Path: "/policy/label/logo"
Feature: "label_policy.private_label"
HasDarkMode: true
Handlers:
- Name: Upload
@ -71,7 +70,6 @@ Services:
Permission: policy.read
OrgLabelPolicyIcon:
Path: "/policy/label/icon"
Feature: "label_policy.private_label"
HasDarkMode: true
Handlers:
- Name: Upload
@ -88,7 +86,6 @@ Services:
Permission: policy.read
OrgLabelPolicyFont:
Path: "/policy/label/font"
Feature: "label_policy.private_label"
Handlers:
- Name: Upload
Comment:
@ -107,7 +104,6 @@ Services:
Methods:
MyUserAvatar:
Path: "/me/avatar"
Features: "label_policy.private_label"
Handlers:
- Name: Upload
Comment:

View File

@ -31,7 +31,6 @@ func main() {
type Method struct {
Path string
Feature string
HasDarkMode bool
Handlers []Handler
}
@ -144,15 +143,13 @@ var {{.Name}}_AuthMethods = authz.MethodMapping {
{{ range $service := .Services}}
{{ range $method := .Methods}}
{{ range $handler := .Handlers}}
{{ if (or $method.Feature $handler.Permission) }}
{{ if $handler.Permission }}
"{{$handler.Method}}:{{$prefix}}{{$service.Prefix}}{{$method.Path}}{{$handler.PathSuffix}}": authz.Option{
Permission: "{{$handler.Permission}}",
Feature: "{{$method.Feature}}",
},
{{ if $method.HasDarkMode }}
"{{$handler.Method}}:{{$prefix}}{{$service.Prefix}}{{$method.Path}}/dark{{$handler.PathSuffix}}": authz.Option{
Permission: "{{$handler.Permission}}",
Feature: "{{$method.Feature}}",
},
{{end}}
{{end}}

View File

@ -23,13 +23,6 @@ func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID s
return nil, err
}
if requiredAuthOption.Feature != "" {
err = CheckOrgFeatures(ctx, verifier, ctxData.OrgID, requiredAuthOption.Feature)
if err != nil {
return nil, err
}
}
if requiredAuthOption.Permission == authenticated {
return func(parent context.Context) context.Context {
return context.WithValue(parent, dataKey, ctxData)
@ -56,10 +49,6 @@ func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID s
}, nil
}
func CheckOrgFeatures(ctx context.Context, t *TokenVerifier, orgID string, requiredFeatures ...string) error {
return t.authZRepo.CheckOrgFeatures(ctx, orgID, requiredFeatures...)
}
func checkUserPermissions(req interface{}, userPerms []string, authOpt Option) error {
if len(userPerms) == 0 {
return errors.ThrowPermissionDenied(nil, "AUTH-5mWD2", "No matching permissions found")

View File

@ -34,10 +34,6 @@ func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (st
return "clientID", "projectID", nil
}
func (v *testVerifier) CheckOrgFeatures(context.Context, string, ...string) error {
return nil
}
func equalStringArray(a, b []string) bool {
if len(a) != len(b) {
return false

View File

@ -25,7 +25,6 @@ type authZRepo interface {
SearchMyMemberships(ctx context.Context) ([]*Membership, error)
ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error)
ExistsOrg(ctx context.Context, orgID string) error
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
}
func Start(authZRepo authZRepo) (v *TokenVerifier) {

View File

@ -1,120 +0,0 @@
package admin
import (
"context"
features_grpc "github.com/zitadel/zitadel/internal/api/grpc/features"
object_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/domain"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
)
func (s *Server) GetDefaultFeatures(ctx context.Context, _ *admin_pb.GetDefaultFeaturesRequest) (*admin_pb.GetDefaultFeaturesResponse, error) {
features, err := s.query.DefaultFeatures(ctx)
if err != nil {
return nil, err
}
return &admin_pb.GetDefaultFeaturesResponse{
Features: features_grpc.ModelFeaturesToPb(features),
}, nil
}
func (s *Server) SetDefaultFeatures(ctx context.Context, req *admin_pb.SetDefaultFeaturesRequest) (*admin_pb.SetDefaultFeaturesResponse, error) {
details, err := s.command.SetDefaultFeatures(ctx, setDefaultFeaturesRequestToDomain(req))
if err != nil {
return nil, err
}
return &admin_pb.SetDefaultFeaturesResponse{
Details: object_grpc.DomainToChangeDetailsPb(details),
}, nil
}
func (s *Server) GetOrgFeatures(ctx context.Context, req *admin_pb.GetOrgFeaturesRequest) (*admin_pb.GetOrgFeaturesResponse, error) {
features, err := s.query.FeaturesByOrgID(ctx, req.OrgId)
if err != nil {
return nil, err
}
return &admin_pb.GetOrgFeaturesResponse{
Features: features_grpc.ModelFeaturesToPb(features),
}, nil
}
func (s *Server) SetOrgFeatures(ctx context.Context, req *admin_pb.SetOrgFeaturesRequest) (*admin_pb.SetOrgFeaturesResponse, error) {
details, err := s.command.SetOrgFeatures(ctx, req.OrgId, setOrgFeaturesRequestToDomain(req))
if err != nil {
return nil, err
}
return &admin_pb.SetOrgFeaturesResponse{
Details: object_grpc.DomainToChangeDetailsPb(details),
}, nil
}
func (s *Server) ResetOrgFeatures(ctx context.Context, req *admin_pb.ResetOrgFeaturesRequest) (*admin_pb.ResetOrgFeaturesResponse, error) {
details, err := s.command.RemoveOrgFeatures(ctx, req.OrgId)
if err != nil {
return nil, err
}
return &admin_pb.ResetOrgFeaturesResponse{
Details: object_grpc.DomainToChangeDetailsPb(details),
}, nil
}
func setDefaultFeaturesRequestToDomain(req *admin_pb.SetDefaultFeaturesRequest) *domain.Features {
actionsAllowed := features_grpc.ActionsAllowedToDomain(req.ActionsAllowed)
if req.Actions {
actionsAllowed = domain.ActionsAllowedUnlimited
}
return &domain.Features{
TierName: req.TierName,
TierDescription: req.Description,
AuditLogRetention: req.AuditLogRetention.AsDuration(),
LoginPolicyFactors: req.LoginPolicyFactors,
LoginPolicyIDP: req.LoginPolicyIdp,
LoginPolicyPasswordless: req.LoginPolicyPasswordless,
LoginPolicyRegistration: req.LoginPolicyRegistration,
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
PasswordComplexityPolicy: req.PasswordComplexityPolicy,
LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel,
LabelPolicyWatermark: req.LabelPolicyWatermark,
CustomDomain: req.CustomDomain,
PrivacyPolicy: req.PrivacyPolicy,
MetadataUser: req.MetadataUser,
CustomTextLogin: req.CustomTextLogin || req.CustomText,
CustomTextMessage: req.CustomTextMessage,
LockoutPolicy: req.LockoutPolicy,
ActionsAllowed: actionsAllowed,
MaxActions: int(req.MaxActions),
}
}
func setOrgFeaturesRequestToDomain(req *admin_pb.SetOrgFeaturesRequest) *domain.Features {
actionsAllowed := features_grpc.ActionsAllowedToDomain(req.ActionsAllowed)
if req.Actions {
actionsAllowed = domain.ActionsAllowedUnlimited
}
return &domain.Features{
TierName: req.TierName,
TierDescription: req.Description,
State: features_grpc.FeaturesStateToDomain(req.State),
StateDescription: req.StateDescription,
AuditLogRetention: req.AuditLogRetention.AsDuration(),
LoginPolicyFactors: req.LoginPolicyFactors,
LoginPolicyIDP: req.LoginPolicyIdp,
LoginPolicyPasswordless: req.LoginPolicyPasswordless,
LoginPolicyRegistration: req.LoginPolicyRegistration,
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
PasswordComplexityPolicy: req.PasswordComplexityPolicy,
LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel,
LabelPolicyWatermark: req.LabelPolicyWatermark,
CustomDomain: req.CustomDomain,
PrivacyPolicy: req.PrivacyPolicy,
MetadataUser: req.MetadataUser,
CustomTextLogin: req.CustomTextLogin || req.CustomText,
CustomTextMessage: req.CustomTextMessage,
LockoutPolicy: req.LockoutPolicy,
ActionsAllowed: actionsAllowed,
MaxActions: int(req.MaxActions),
}
}

View File

@ -1,18 +0,0 @@
package auth
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
auth_pb "github.com/zitadel/zitadel/pkg/grpc/auth"
)
func (s *Server) ListMyZitadelFeatures(ctx context.Context, _ *auth_pb.ListMyZitadelFeaturesRequest) (*auth_pb.ListMyZitadelFeaturesResponse, error) {
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &auth_pb.ListMyZitadelFeaturesResponse{
Result: features.EnabledFeatureTypes(),
}, nil
}

View File

@ -1,6 +1,8 @@
package auth
import (
"time"
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/authz"
@ -29,6 +31,7 @@ type Server struct {
assetsAPIDomain string
userCodeAlg crypto.EncryptionAlgorithm
externalSecure bool
auditLogRetention time.Duration
}
type Config struct {
@ -42,6 +45,7 @@ func CreateServer(command *command.Commands,
assetsAPIDomain string,
userCodeAlg crypto.EncryptionAlgorithm,
externalSecure bool,
auditLogRetention time.Duration,
) *Server {
return &Server{
command: command,
@ -51,6 +55,7 @@ func CreateServer(command *command.Commands,
assetsAPIDomain: assetsAPIDomain,
userCodeAlg: userCodeAlg,
externalSecure: externalSecure,
auditLogRetention: auditLogRetention,
}
}

View File

@ -56,11 +56,7 @@ func (s *Server) RemoveMyUser(ctx context.Context, _ *auth_pb.RemoveMyUserReques
func (s *Server) ListMyUserChanges(ctx context.Context, req *auth_pb.ListMyUserChangesRequest) (*auth_pb.ListMyUserChangesResponse, error) {
sequence, limit, asc := change.ChangeQueryToQuery(req.Query)
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).ResourceOwner)
if err != nil {
return nil, err
}
changes, err := s.query.UserChanges(ctx, authz.GetCtxData(ctx).UserID, sequence, limit, asc, features.AuditLogRetention)
changes, err := s.query.UserChanges(ctx, authz.GetCtxData(ctx).UserID, sequence, limit, asc, s.auditLogRetention)
if err != nil {
return nil, err
}

View File

@ -1,108 +0,0 @@
package features
import (
"google.golang.org/protobuf/types/known/durationpb"
object_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
features_pb "github.com/zitadel/zitadel/pkg/grpc/features"
)
func ModelFeaturesToPb(features *query.Features) *features_pb.Features {
return &features_pb.Features{
IsDefault: features.IsDefault,
Tier: FeatureTierToPb(features.TierName, features.TierDescription, features.State, features.StateDescription),
AuditLogRetention: durationpb.New(features.AuditLogRetention),
LoginPolicyFactors: features.LoginPolicyFactors,
LoginPolicyIdp: features.LoginPolicyIDP,
LoginPolicyPasswordless: features.LoginPolicyPasswordless,
LoginPolicyRegistration: features.LoginPolicyRegistration,
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
PasswordComplexityPolicy: features.PasswordComplexityPolicy,
LabelPolicy: features.LabelPolicyPrivateLabel,
CustomDomain: features.CustomDomain,
LabelPolicyPrivateLabel: features.LabelPolicyPrivateLabel,
LabelPolicyWatermark: features.LabelPolicyWatermark,
PrivacyPolicy: features.PrivacyPolicy,
CustomText: features.CustomTextMessage,
CustomTextMessage: features.CustomTextMessage,
CustomTextLogin: features.CustomTextLogin,
MetadataUser: features.MetadataUser,
LockoutPolicy: features.LockoutPolicy,
Actions: features.ActionsAllowed != domain.ActionsNotAllowed,
ActionsAllowed: ActionsAllowedToPb(features.ActionsAllowed),
MaxActions: features.MaxActions,
Details: object_grpc.ChangeToDetailsPb(
features.Sequence,
features.ChangeDate,
features.AggregateID,
),
}
}
func FeatureTierToPb(name, description string, status domain.FeaturesState, statusDescription string) *features_pb.FeatureTier {
return &features_pb.FeatureTier{
Name: name,
Description: description,
State: FeaturesStateToPb(status),
StatusInfo: statusDescription,
}
}
func FeaturesStateToPb(status domain.FeaturesState) features_pb.FeaturesState {
switch status {
case domain.FeaturesStateActive:
return features_pb.FeaturesState_FEATURES_STATE_ACTIVE
case domain.FeaturesStateActionRequired:
return features_pb.FeaturesState_FEATURES_STATE_ACTION_REQUIRED
case domain.FeaturesStateCanceled:
return features_pb.FeaturesState_FEATURES_STATE_CANCELED
case domain.FeaturesStateGrandfathered:
return features_pb.FeaturesState_FEATURES_STATE_GRANDFATHERED
default:
return features_pb.FeaturesState_FEATURES_STATE_ACTIVE
}
}
func FeaturesStateToDomain(status features_pb.FeaturesState) domain.FeaturesState {
switch status {
case features_pb.FeaturesState_FEATURES_STATE_ACTIVE:
return domain.FeaturesStateActive
case features_pb.FeaturesState_FEATURES_STATE_ACTION_REQUIRED:
return domain.FeaturesStateActionRequired
case features_pb.FeaturesState_FEATURES_STATE_CANCELED:
return domain.FeaturesStateCanceled
case features_pb.FeaturesState_FEATURES_STATE_GRANDFATHERED:
return domain.FeaturesStateGrandfathered
default:
return -1
}
}
func ActionsAllowedToDomain(allowed features_pb.ActionsAllowed) domain.ActionsAllowed {
switch allowed {
case features_pb.ActionsAllowed_ACTIONS_ALLOWED_NOT_ALLOWED:
return domain.ActionsNotAllowed
case features_pb.ActionsAllowed_ACTIONS_ALLOWED_MAX:
return domain.ActionsMaxAllowed
case features_pb.ActionsAllowed_ACTIONS_ALLOWED_UNLIMITED:
return domain.ActionsAllowedUnlimited
default:
return domain.ActionsNotAllowed
}
}
func ActionsAllowedToPb(allowed domain.ActionsAllowed) features_pb.ActionsAllowed {
switch allowed {
case domain.ActionsNotAllowed:
return features_pb.ActionsAllowed_ACTIONS_ALLOWED_NOT_ALLOWED
case domain.ActionsMaxAllowed:
return features_pb.ActionsAllowed_ACTIONS_ALLOWED_MAX
case domain.ActionsAllowedUnlimited:
return features_pb.ActionsAllowed_ACTIONS_ALLOWED_UNLIMITED
default:
return features_pb.ActionsAllowed_ACTIONS_ALLOWED_NOT_ALLOWED
}
}

View File

@ -1,19 +0,0 @@
package management
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
features_grpc "github.com/zitadel/zitadel/internal/api/grpc/features"
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
)
func (s *Server) GetFeatures(ctx context.Context, req *mgmt_pb.GetFeaturesRequest) (*mgmt_pb.GetFeaturesResponse, error) {
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
return &mgmt_pb.GetFeaturesResponse{
Features: features_grpc.ModelFeaturesToPb(features),
}, nil
}

View File

@ -34,11 +34,7 @@ func (s *Server) GetOrgByDomainGlobal(ctx context.Context, req *mgmt_pb.GetOrgBy
func (s *Server) ListOrgChanges(ctx context.Context, req *mgmt_pb.ListOrgChangesRequest) (*mgmt_pb.ListOrgChangesResponse, error) {
sequence, limit, asc := change_grpc.ChangeQueryToQuery(req.Query)
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
response, err := s.query.OrgChanges(ctx, authz.GetCtxData(ctx).OrgID, sequence, limit, asc, features.AuditLogRetention)
response, err := s.query.OrgChanges(ctx, authz.GetCtxData(ctx).OrgID, sequence, limit, asc, s.auditLogRetention)
if err != nil {
return nil, err
}

View File

@ -111,11 +111,7 @@ func (s *Server) ListGrantedProjectRoles(ctx context.Context, req *mgmt_pb.ListG
func (s *Server) ListProjectChanges(ctx context.Context, req *mgmt_pb.ListProjectChangesRequest) (*mgmt_pb.ListProjectChangesResponse, error) {
sequence, limit, asc := change_grpc.ChangeQueryToQuery(req.Query)
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
res, err := s.query.ProjectChanges(ctx, req.ProjectId, sequence, limit, asc, features.AuditLogRetention)
res, err := s.query.ProjectChanges(ctx, req.ProjectId, sequence, limit, asc, s.auditLogRetention)
if err != nil {
return nil, err
}

View File

@ -44,11 +44,7 @@ func (s *Server) ListApps(ctx context.Context, req *mgmt_pb.ListAppsRequest) (*m
func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChangesRequest) (*mgmt_pb.ListAppChangesResponse, error) {
sequence, limit, asc := change_grpc.ChangeQueryToQuery(req.Query)
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
res, err := s.query.ApplicationChanges(ctx, req.ProjectId, req.AppId, sequence, limit, asc, features.AuditLogRetention)
res, err := s.query.ApplicationChanges(ctx, req.ProjectId, req.AppId, sequence, limit, asc, s.auditLogRetention)
if err != nil {
return nil, err
}

View File

@ -1,6 +1,8 @@
package management
import (
"time"
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/authz"
@ -28,6 +30,7 @@ type Server struct {
userCodeAlg crypto.EncryptionAlgorithm
externalSecure bool
issuerPath string
auditLogRetention time.Duration
}
func CreateServer(
@ -38,6 +41,7 @@ func CreateServer(
userCodeAlg crypto.EncryptionAlgorithm,
externalSecure bool,
issuerPath string,
auditLogRetention time.Duration,
) *Server {
return &Server{
command: command,
@ -48,6 +52,7 @@ func CreateServer(
userCodeAlg: userCodeAlg,
externalSecure: externalSecure,
issuerPath: issuerPath,
auditLogRetention: auditLogRetention,
}
}

View File

@ -79,11 +79,7 @@ func (s *Server) ListUsers(ctx context.Context, req *mgmt_pb.ListUsersRequest) (
func (s *Server) ListUserChanges(ctx context.Context, req *mgmt_pb.ListUserChangesRequest) (*mgmt_pb.ListUserChangesResponse, error) {
sequence, limit, asc := change_grpc.ChangeQueryToQuery(req.Query)
features, err := s.query.FeaturesByOrgID(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
res, err := s.query.UserChanges(ctx, req.UserId, sequence, limit, asc, features.AuditLogRetention)
res, err := s.query.UserChanges(ctx, req.UserId, sequence, limit, asc, s.auditLogRetention)
if err != nil {
return nil, err
}

View File

@ -37,9 +37,6 @@ func (v *verifierMock) ExistsOrg(ctx context.Context, orgID string) error {
func (v *verifierMock) VerifierClientID(ctx context.Context, appName string) (string, string, error) {
return "", "", nil
}
func (v *verifierMock) CheckOrgFeatures(context.Context, string, ...string) error {
return nil
}
func Test_authorize(t *testing.T) {
type args struct {

View File

@ -11,7 +11,6 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/authz/repository/eventsourcing/view"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors"
v1 "github.com/zitadel/zitadel/internal/eventstore/v1"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
@ -105,133 +104,6 @@ func (repo *TokenVerifierRepo) ProjectIDAndOriginsByClientID(ctx context.Context
return app.ProjectID, app.OIDCConfig.AllowedOrigins, nil
}
func (repo *TokenVerifierRepo) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error {
features, err := repo.Query.FeaturesByOrgID(ctx, orgID)
if err != nil {
return err
}
return checkFeatures(features, requiredFeatures...)
}
func checkFeatures(features *query.Features, requiredFeatures ...string) error {
for _, requiredFeature := range requiredFeatures {
if strings.HasPrefix(requiredFeature, domain.FeatureLoginPolicy) {
if err := checkLoginPolicyFeatures(features, requiredFeature); err != nil {
return err
}
continue
}
if requiredFeature == domain.FeaturePasswordComplexityPolicy {
if !features.PasswordComplexityPolicy {
return MissingFeatureErr(requiredFeature)
}
continue
}
if strings.HasPrefix(requiredFeature, domain.FeatureLabelPolicy) {
if err := checkLabelPolicyFeatures(features, requiredFeature); err != nil {
return err
}
continue
}
if requiredFeature == domain.FeatureCustomDomain {
if !features.CustomDomain {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeatureCustomTextMessage {
if !features.CustomTextMessage {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeatureCustomTextLogin {
if !features.CustomTextLogin {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeaturePrivacyPolicy {
if !features.PrivacyPolicy {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeatureLockoutPolicy {
if !features.LockoutPolicy {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeatureMetadataUser {
if !features.MetadataUser {
return MissingFeatureErr(requiredFeature)
}
continue
}
if requiredFeature == domain.FeatureActions {
if features.ActionsAllowed == domain.ActionsNotAllowed {
return MissingFeatureErr(requiredFeature)
}
continue
}
return MissingFeatureErr(requiredFeature)
}
return nil
}
func checkLoginPolicyFeatures(features *query.Features, requiredFeature string) error {
switch requiredFeature {
case domain.FeatureLoginPolicyFactors:
if !features.LoginPolicyFactors {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLoginPolicyIDP:
if !features.LoginPolicyIDP {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLoginPolicyPasswordless:
if !features.LoginPolicyPasswordless {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLoginPolicyRegistration:
if !features.LoginPolicyRegistration {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLoginPolicyUsernameLogin:
if !features.LoginPolicyUsernameLogin {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLoginPolicyPasswordReset:
if !features.LoginPolicyPasswordReset {
return MissingFeatureErr(requiredFeature)
}
default:
if !features.LoginPolicyFactors && !features.LoginPolicyIDP && !features.LoginPolicyPasswordless && !features.LoginPolicyRegistration && !features.LoginPolicyUsernameLogin {
return MissingFeatureErr(requiredFeature)
}
}
return nil
}
func checkLabelPolicyFeatures(features *query.Features, requiredFeature string) error {
switch requiredFeature {
case domain.FeatureLabelPolicyPrivateLabel:
if !features.LabelPolicyPrivateLabel {
return MissingFeatureErr(requiredFeature)
}
case domain.FeatureLabelPolicyWatermark:
if !features.LabelPolicyWatermark {
return MissingFeatureErr(requiredFeature)
}
}
return nil
}
func MissingFeatureErr(feature string) error {
return caos_errs.ThrowPermissionDeniedf(nil, "AUTH-Dvgsf", "missing feature %v", feature)
}
func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName string) (clientID, projectID string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()

View File

@ -7,6 +7,5 @@ import (
type TokenVerifierRepository interface {
VerifyAccessToken(ctx context.Context, tokenString, verifierClientID, projectID string) (userID string, agentID string, clientID, prefLang, resourceOwner string, err error)
ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error)
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
VerifierClientID(ctx context.Context, appName string) (clientID, projectID string, err error)
}

View File

@ -1,12 +1,10 @@
package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/http"
authz_repo "github.com/zitadel/zitadel/internal/authz/repository"
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
@ -50,19 +48,12 @@ type Commands struct {
keyAlgorithm crypto.EncryptionAlgorithm
privateKeyLifetime time.Duration
publicKeyLifetime time.Duration
tokenVerifier orgFeatureChecker
}
type orgFeatureChecker interface {
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
}
func StartCommands(es *eventstore.Eventstore,
defaults sd.SystemDefaults,
zitadelRoles []authz.RoleMapping,
staticStore static.Storage,
authZRepo authz_repo.Repository,
webAuthN *webauthn_helper.Config,
externalDomain string,
externalSecure bool,
@ -119,8 +110,6 @@ func StartCommands(es *eventstore.Eventstore,
repo.domainVerificationGenerator = crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, repo.domainVerificationAlg)
repo.domainVerificationValidator = http.ValidateDomain
repo.tokenVerifier = authZRepo
return repo, nil
}

View File

@ -1,123 +0,0 @@
package command
import (
"time"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/features"
)
type FeaturesWriteModel struct {
eventstore.WriteModel
TierName string
TierDescription string
State domain.FeaturesState
StateDescription string
AuditLogRetention time.Duration
LoginPolicyFactors bool
LoginPolicyIDP bool
LoginPolicyPasswordless bool
LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool
LabelPolicyPrivateLabel bool
LabelPolicyWatermark bool
CustomDomain bool
PrivacyPolicy bool
MetadataUser bool
CustomTextMessage bool
CustomTextLogin bool
LockoutPolicy bool
ActionsAllowed domain.ActionsAllowed
MaxActions int
}
func (wm *FeaturesWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *features.FeaturesSetEvent:
if e.TierName != nil {
wm.TierName = *e.TierName
}
if e.TierDescription != nil {
wm.TierDescription = *e.TierDescription
}
wm.State = domain.FeaturesStateActive
if e.State != nil {
wm.State = *e.State
}
if e.StateDescription != nil {
wm.StateDescription = *e.StateDescription
}
if e.AuditLogRetention != nil {
wm.AuditLogRetention = *e.AuditLogRetention
}
if e.LoginPolicyFactors != nil {
wm.LoginPolicyFactors = *e.LoginPolicyFactors
}
if e.LoginPolicyIDP != nil {
wm.LoginPolicyIDP = *e.LoginPolicyIDP
}
if e.LoginPolicyPasswordless != nil {
wm.LoginPolicyPasswordless = *e.LoginPolicyPasswordless
}
if e.LoginPolicyRegistration != nil {
wm.LoginPolicyRegistration = *e.LoginPolicyRegistration
}
if e.LoginPolicyUsernameLogin != nil {
wm.LoginPolicyUsernameLogin = *e.LoginPolicyUsernameLogin
}
if e.LoginPolicyPasswordReset != nil {
wm.LoginPolicyPasswordReset = *e.LoginPolicyPasswordReset
}
if e.PasswordComplexityPolicy != nil {
wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy
}
if e.LabelPolicy != nil {
wm.LabelPolicyPrivateLabel = *e.LabelPolicy
}
if e.LabelPolicyPrivateLabel != nil {
wm.LabelPolicyPrivateLabel = *e.LabelPolicyPrivateLabel
}
if e.LabelPolicyWatermark != nil {
wm.LabelPolicyWatermark = *e.LabelPolicyWatermark
}
if e.CustomDomain != nil {
wm.CustomDomain = *e.CustomDomain
}
if e.PrivacyPolicy != nil {
wm.PrivacyPolicy = *e.PrivacyPolicy
}
if e.MetadataUser != nil {
wm.MetadataUser = *e.MetadataUser
}
if e.CustomTextMessage != nil {
wm.CustomTextMessage = *e.CustomTextMessage
}
if e.CustomTextLogin != nil {
wm.CustomTextLogin = *e.CustomTextLogin
}
if e.LockoutPolicy != nil {
wm.LockoutPolicy = *e.LockoutPolicy
}
if e.Actions != nil {
wm.ActionsAllowed = domain.ActionsNotAllowed
if *e.Actions {
wm.ActionsAllowed = domain.ActionsAllowedUnlimited
}
}
if e.ActionsAllowed != nil {
wm.ActionsAllowed = *e.ActionsAllowed
}
if e.MaxActions != nil {
wm.MaxActions = *e.MaxActions
}
case *features.FeaturesRemovedEvent:
wm.State = domain.FeaturesStateRemoved
}
}
return wm.WriteModel.Reduce()
}

View File

@ -33,30 +33,6 @@ type InstanceSetup struct {
InstanceName string
CustomDomain string
Org OrgSetup
Features struct {
TierName string
TierDescription string
Retention time.Duration
State domain.FeaturesState
StateDescription string
LoginPolicyFactors bool
LoginPolicyIDP bool
LoginPolicyPasswordless bool
LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool
LabelPolicyPrivateLabel bool
LabelPolicyWatermark bool
CustomDomain bool
PrivacyPolicy bool
MetadataUser bool
CustomTextMessage bool
CustomTextLogin bool
LockoutPolicy bool
ActionsAllowed domain.ActionsAllowed
MaxActions int
}
SecretGenerators struct {
PasswordSaltCost uint
ClientSecret *crypto.GeneratorConfig
@ -191,31 +167,6 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
validations := []preparation.Validation{
addInstance(instanceAgg, setup.InstanceName),
SetDefaultFeatures(
instanceAgg,
setup.Features.TierName,
setup.Features.TierDescription,
setup.Features.State,
setup.Features.StateDescription,
setup.Features.Retention,
setup.Features.LoginPolicyFactors,
setup.Features.LoginPolicyIDP,
setup.Features.LoginPolicyPasswordless,
setup.Features.LoginPolicyRegistration,
setup.Features.LoginPolicyUsernameLogin,
setup.Features.LoginPolicyPasswordReset,
setup.Features.PasswordComplexityPolicy,
setup.Features.LabelPolicyPrivateLabel,
setup.Features.LabelPolicyWatermark,
setup.Features.CustomDomain,
setup.Features.PrivacyPolicy,
setup.Features.MetadataUser,
setup.Features.CustomTextMessage,
setup.Features.CustomTextLogin,
setup.Features.LockoutPolicy,
setup.Features.ActionsAllowed,
setup.Features.MaxActions,
),
addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeAppSecret, setup.SecretGenerators.ClientSecret),
addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeInitCode, setup.SecretGenerators.InitializeUserCode),
addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyEmailCode, setup.SecretGenerators.EmailVerificationCode),

View File

@ -169,31 +169,3 @@ func writeModelToIDPProvider(wm *IdentityProviderWriteModel) *domain.IDPProvider
Type: wm.IDPProviderType,
}
}
func writeModelToFeatures(wm *FeaturesWriteModel) *domain.Features {
return &domain.Features{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
TierName: wm.TierName,
TierDescription: wm.TierDescription,
State: wm.State,
StateDescription: wm.StateDescription,
AuditLogRetention: wm.AuditLogRetention,
LoginPolicyFactors: wm.LoginPolicyFactors,
LoginPolicyIDP: wm.LoginPolicyIDP,
LoginPolicyPasswordless: wm.LoginPolicyPasswordless,
LoginPolicyRegistration: wm.LoginPolicyRegistration,
LoginPolicyUsernameLogin: wm.LoginPolicyUsernameLogin,
LoginPolicyPasswordReset: wm.LoginPolicyPasswordReset,
PasswordComplexityPolicy: wm.PasswordComplexityPolicy,
LabelPolicyPrivateLabel: wm.LabelPolicyPrivateLabel,
LabelPolicyWatermark: wm.LabelPolicyWatermark,
CustomDomain: wm.CustomDomain,
PrivacyPolicy: wm.PrivacyPolicy,
MetadataUser: wm.MetadataUser,
CustomTextMessage: wm.CustomTextMessage,
CustomTextLogin: wm.CustomTextLogin,
LockoutPolicy: wm.LockoutPolicy,
ActionsAllowed: wm.ActionsAllowed,
MaxActions: wm.MaxActions,
}
}

View File

@ -1,160 +0,0 @@
package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
)
func SetDefaultFeatures(
a *instance.Aggregate,
tierName,
tierDescription string,
state domain.FeaturesState,
stateDescription string,
retention time.Duration,
loginPolicyFactors,
loginPolicyIDP,
loginPolicyPasswordless,
loginPolicyRegistration,
loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain,
privacyPolicy,
metadataUser,
customTextMessage,
customTextLogin,
lockoutPolicy bool,
actionsAllowed domain.ActionsAllowed,
maxActions int,
) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if !state.Valid() || state == domain.FeaturesStateUnspecified || state == domain.FeaturesStateRemoved {
return nil, errors.ThrowInvalidArgument(nil, "INSTA-d3r1s", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
writeModel, err := defaultFeatures(ctx, filter)
if err != nil {
return nil, err
}
event, hasChanged := writeModel.NewSetEvent(ctx, &a.Aggregate,
tierName,
tierDescription,
state,
stateDescription,
retention,
loginPolicyFactors,
loginPolicyIDP,
loginPolicyPasswordless,
loginPolicyRegistration,
loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain,
privacyPolicy,
metadataUser,
customTextMessage,
customTextLogin,
lockoutPolicy,
actionsAllowed,
maxActions,
)
if !hasChanged {
return nil, errors.ThrowPreconditionFailed(nil, "INSTA-GE4h2", "Errors.Features.NotChanged")
}
return []eventstore.Command{
event,
}, nil
}, nil
}
}
func defaultFeatures(ctx context.Context, filter preparation.FilterToQueryReducer) (*InstanceFeaturesWriteModel, error) {
features := NewInstanceFeaturesWriteModel(ctx)
events, err := filter(ctx, features.Query())
if err != nil {
return nil, err
}
if len(events) == 0 {
return features, nil
}
features.AppendEvents(events...)
err = features.Reduce()
return features, err
}
func (c *Commands) SetDefaultFeatures(ctx context.Context, features *domain.Features) (*domain.ObjectDetails, error) {
existingFeatures := NewInstanceFeaturesWriteModel(ctx)
setEvent, err := c.setDefaultFeatures(ctx, existingFeatures, features)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, setEvent)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingFeatures, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
}
func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *InstanceFeaturesWriteModel, features *domain.Features) (*instance.FeaturesSetEvent, error) {
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
setEvent, hasChanged := existingFeatures.NewSetEvent(
ctx,
InstanceAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel),
features.TierName,
features.TierDescription,
features.State,
features.StateDescription,
features.AuditLogRetention,
features.LoginPolicyFactors,
features.LoginPolicyIDP,
features.LoginPolicyPasswordless,
features.LoginPolicyRegistration,
features.LoginPolicyUsernameLogin,
features.LoginPolicyPasswordReset,
features.PasswordComplexityPolicy,
features.LabelPolicyPrivateLabel,
features.LabelPolicyWatermark,
features.CustomDomain,
features.PrivacyPolicy,
features.MetadataUser,
features.CustomTextMessage,
features.CustomTextLogin,
features.LockoutPolicy,
features.ActionsAllowed,
features.MaxActions,
)
if !hasChanged {
return nil, errors.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged")
}
return setEvent, nil
}
func (c *Commands) getDefaultFeatures(ctx context.Context) (*domain.Features, error) {
existingFeatures := NewInstanceFeaturesWriteModel(ctx)
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
features := writeModelToFeatures(&existingFeatures.FeaturesWriteModel)
features.IsDefault = true
return features, nil
}

View File

@ -1,157 +0,0 @@
package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/features"
"github.com/zitadel/zitadel/internal/repository/instance"
)
type InstanceFeaturesWriteModel struct {
FeaturesWriteModel
}
func NewInstanceFeaturesWriteModel(ctx context.Context) *InstanceFeaturesWriteModel {
return &InstanceFeaturesWriteModel{
FeaturesWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: authz.GetInstance(ctx).InstanceID(),
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
},
},
}
}
func (wm *InstanceFeaturesWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *instance.FeaturesSetEvent:
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesSetEvent)
}
}
}
func (wm *InstanceFeaturesWriteModel) IsValid() bool {
return wm.AggregateID != ""
}
func (wm *InstanceFeaturesWriteModel) Reduce() error {
return wm.FeaturesWriteModel.Reduce()
}
func (wm *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
EventTypes(instance.FeaturesSetEventType).
AggregateTypes(instance.AggregateType).
Builder()
}
func (wm *InstanceFeaturesWriteModel) NewSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tierName, tierDescription string,
state domain.FeaturesState,
stateDescription string,
auditLogRetention time.Duration,
loginPolicyFactors,
loginPolicyIDP,
loginPolicyPasswordless,
loginPolicyRegistration,
loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain,
privacyPolicy,
metadataUser,
customTextMessage,
customTextLogin,
lockoutPolicy bool,
actionsAllowed domain.ActionsAllowed,
maxActions int,
) (*instance.FeaturesSetEvent, bool) {
changes := make([]features.FeaturesChanges, 0)
if tierName != "" && wm.TierName != tierName {
changes = append(changes, features.ChangeTierName(tierName))
}
if wm.TierDescription != tierDescription {
changes = append(changes, features.ChangeTierDescription(tierDescription))
}
if wm.State != state {
changes = append(changes, features.ChangeState(state))
}
if wm.StateDescription != stateDescription {
changes = append(changes, features.ChangeStateDescription(stateDescription))
}
if auditLogRetention != 0 && wm.AuditLogRetention != auditLogRetention {
changes = append(changes, features.ChangeAuditLogRetention(auditLogRetention))
}
if wm.LoginPolicyFactors != loginPolicyFactors {
changes = append(changes, features.ChangeLoginPolicyFactors(loginPolicyFactors))
}
if wm.LoginPolicyIDP != loginPolicyIDP {
changes = append(changes, features.ChangeLoginPolicyIDP(loginPolicyIDP))
}
if wm.LoginPolicyPasswordless != loginPolicyPasswordless {
changes = append(changes, features.ChangeLoginPolicyPasswordless(loginPolicyPasswordless))
}
if wm.LoginPolicyRegistration != loginPolicyRegistration {
changes = append(changes, features.ChangeLoginPolicyRegistration(loginPolicyRegistration))
}
if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin {
changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin))
}
if wm.LoginPolicyPasswordReset != loginPolicyPasswordReset {
changes = append(changes, features.ChangeLoginPolicyPasswordReset(loginPolicyPasswordReset))
}
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
}
if wm.LabelPolicyPrivateLabel != labelPolicyPrivateLabel {
changes = append(changes, features.ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel))
}
if wm.LabelPolicyWatermark != labelPolicyWatermark {
changes = append(changes, features.ChangeLabelPolicyWatermark(labelPolicyWatermark))
}
if wm.CustomDomain != customDomain {
changes = append(changes, features.ChangeCustomDomain(customDomain))
}
if wm.PrivacyPolicy != privacyPolicy {
changes = append(changes, features.ChangePrivacyPolicy(privacyPolicy))
}
if wm.MetadataUser != metadataUser {
changes = append(changes, features.ChangeMetadataUser(metadataUser))
}
if wm.CustomTextMessage != customTextMessage {
changes = append(changes, features.ChangeCustomTextMessage(customTextMessage))
}
if wm.CustomTextLogin != customTextLogin {
changes = append(changes, features.ChangeCustomTextLogin(customTextLogin))
}
if wm.LockoutPolicy != lockoutPolicy {
changes = append(changes, features.ChangeLockoutPolicy(lockoutPolicy))
}
if wm.ActionsAllowed != actionsAllowed {
changes = append(changes, features.ChangeActionsAllowed(actionsAllowed))
}
if wm.MaxActions != maxActions {
changes = append(changes, features.ChangeMaxActions(maxActions))
}
if len(changes) == 0 {
return nil, false
}
changedEvent, err := instance.NewFeaturesSetEvent(ctx, aggregate, changes)
if err != nil {
return nil, false
}
return changedEvent, true
}

View File

@ -1,152 +0,0 @@
package command
import (
"context"
"testing"
"time"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/features"
"github.com/zitadel/zitadel/internal/repository/instance"
)
func TestSetDefaultFeatures(t *testing.T) {
type args struct {
a *instance.Aggregate
tierName string
tierDescription string
state domain.FeaturesState
stateDescription string
retention time.Duration
loginPolicyFactors bool
loginPolicyIDP bool
loginPolicyPasswordless bool
loginPolicyRegistration bool
loginPolicyUsernameLogin bool
loginPolicyPasswordReset bool
passwordComplexityPolicy bool
labelPolicyPrivateLabel bool
labelPolicyWatermark bool
customDomain bool
privacyPolicy bool
metadataUser bool
customTextMessage bool
customTextLogin bool
lockoutPolicy bool
actionsAllowed domain.ActionsAllowed
maxActions int
}
tests := []struct {
name string
args args
want Want
}{
{
name: "invalid state",
args: args{
a: instance.NewAggregate("INSTANCE"),
tierName: "",
tierDescription: "",
state: 0,
stateDescription: "",
retention: 0,
loginPolicyFactors: false,
loginPolicyIDP: false,
loginPolicyPasswordless: false,
loginPolicyRegistration: false,
loginPolicyUsernameLogin: false,
loginPolicyPasswordReset: false,
passwordComplexityPolicy: false,
labelPolicyPrivateLabel: false,
labelPolicyWatermark: false,
customDomain: false,
privacyPolicy: false,
metadataUser: false,
customTextMessage: false,
customTextLogin: false,
lockoutPolicy: false,
actionsAllowed: 0,
maxActions: 0,
},
want: Want{
ValidationErr: errors.ThrowInvalidArgument(nil, "INSTA-d3r1s", "Errors.Invalid.Argument"),
},
},
{
name: "correct",
args: args{
a: instance.NewAggregate("INSTANCE"),
tierName: "",
tierDescription: "",
state: domain.FeaturesStateActive,
stateDescription: "",
retention: 0,
loginPolicyFactors: false,
loginPolicyIDP: false,
loginPolicyPasswordless: false,
loginPolicyRegistration: false,
loginPolicyUsernameLogin: false,
loginPolicyPasswordReset: false,
passwordComplexityPolicy: false,
labelPolicyPrivateLabel: false,
labelPolicyWatermark: false,
customDomain: false,
privacyPolicy: false,
metadataUser: false,
customTextMessage: false,
customTextLogin: false,
lockoutPolicy: false,
actionsAllowed: 0,
maxActions: 0,
},
want: Want{
Commands: []eventstore.Command{
func() *instance.FeaturesSetEvent {
event, _ := instance.NewFeaturesSetEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate,
[]features.FeaturesChanges{
features.ChangeState(domain.FeaturesStateActive),
},
)
return event
}(),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
AssertValidation(t, SetDefaultFeatures(
tt.args.a,
tt.args.tierName,
tt.args.tierDescription,
tt.args.state,
tt.args.stateDescription,
tt.args.retention,
tt.args.loginPolicyFactors,
tt.args.loginPolicyIDP,
tt.args.loginPolicyPasswordless,
tt.args.loginPolicyRegistration,
tt.args.loginPolicyUsernameLogin,
tt.args.loginPolicyPasswordReset,
tt.args.passwordComplexityPolicy,
tt.args.labelPolicyPrivateLabel,
tt.args.labelPolicyWatermark,
tt.args.customDomain,
tt.args.privacyPolicy,
tt.args.metadataUser,
tt.args.customTextMessage,
tt.args.customTextLogin,
tt.args.lockoutPolicy,
tt.args.actionsAllowed,
tt.args.maxActions,
), NewMultiFilter().
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return nil, nil
}).
Filter(),
tt.want)
})
}
}

View File

@ -9,9 +9,7 @@ import (
"github.com/golang/mock/gomock"
"golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/eventstore/repository/mock"
@ -213,51 +211,6 @@ func GetMockSecretGenerator(t *testing.T) crypto.Generator {
return generator
}
func GetMockVerifier(t *testing.T, features ...string) *testVerifier {
return &testVerifier{
features: features,
}
}
type testVerifier struct {
features []string
}
func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, string, error) {
return "userID", "agentID", "clientID", "de", "orgID", nil
}
func (v *testVerifier) SearchMyMemberships(ctx context.Context) ([]*authz.Membership, error) {
return nil, nil
}
func (v *testVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (string, []string, error) {
return "", nil, nil
}
func (v *testVerifier) ExistsOrg(ctx context.Context, orgID string) error {
return nil
}
func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (string, error) {
return "clientID", nil
}
func (v *testVerifier) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error {
for _, feature := range requiredFeatures {
hasFeature := false
for _, f := range v.features {
if f == feature {
hasFeature = true
break
}
}
if !hasFeature {
return errors.ThrowPermissionDenied(nil, "id", "missing feature")
}
}
return nil
}
type mockInstance struct{}
func (m *mockInstance) InstanceID() string {

View File

@ -15,10 +15,6 @@ func (c *Commands) AddAction(ctx context.Context, addAction *domain.Action, reso
if !addAction.IsValid() {
return "", nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-eg2gf", "Errors.Action.Invalid")
}
err = c.checkAdditionalActionAllowed(ctx, resourceOwner)
if err != nil {
return "", nil, err
}
addAction.AggregateID, err = c.idGenerator.Next()
if err != nil {
return "", nil, err
@ -44,27 +40,6 @@ func (c *Commands) AddAction(ctx context.Context, addAction *domain.Action, reso
return actionModel.AggregateID, writeModelToObjectDetails(&actionModel.WriteModel), nil
}
func (c *Commands) checkAdditionalActionAllowed(ctx context.Context, resourceOwner string) error {
features, err := c.getOrgFeaturesOrDefault(ctx, resourceOwner)
if err != nil {
return err
}
existingActions, err := c.getActionsByOrgWriteModelByID(ctx, resourceOwner)
if err != nil {
return err
}
activeActions := make([]*ActionWriteModel, 0, len(existingActions.Actions))
for _, existingAction := range existingActions.Actions {
if existingAction.State == domain.ActionStateActive {
activeActions = append(activeActions, existingAction)
}
}
if features.ActionsAllowed == domain.ActionsAllowedUnlimited || len(activeActions) < features.MaxActions {
return nil
}
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-dfwg2", "Errors.Action.MaxAllowed")
}
func (c *Commands) ChangeAction(ctx context.Context, actionChange *domain.Action, resourceOwner string) (*domain.ObjectDetails, error) {
if !actionChange.IsValid() || actionChange.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Df2f3", "Errors.Action.Invalid")
@ -145,10 +120,6 @@ func (c *Commands) ReactivateAction(ctx context.Context, actionID string, resour
if existingAction.State != domain.ActionStateInactive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-J53zh", "Errors.Action.NotInactive")
}
err = c.checkAdditionalActionAllowed(ctx, resourceOwner)
if err != nil {
return nil, err
}
actionAgg := ActionAggregateFromWriteModel(&existingAction.WriteModel)
events := []eventstore.Command{

View File

@ -6,8 +6,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/repository/features"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
@ -56,76 +54,10 @@ func TestCommands_AddAction(t *testing.T) {
err: errors.IsErrorInvalidArgument,
},
},
{
"no additional allowed, error",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
func() eventstore.Command {
e, _ := org.NewFeaturesSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
[]features.FeaturesChanges{
features.ChangeMaxActions(1),
},
)
return e
}(),
),
),
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
"name",
"name() {};",
0,
false,
),
),
),
),
},
args{
ctx: context.Background(),
addAction: &domain.Action{
Name: "name",
Script: "name() {};",
},
resourceOwner: "org1",
},
res{
err: errors.IsPreconditionFailed,
},
},
{
"unique constraint failed, error",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
func() eventstore.Command {
e, _ := org.NewFeaturesSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
[]features.FeaturesChanges{
features.ChangeMaxActions(2),
},
)
return e
}(),
),
),
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
"name",
"name() {};",
0,
false,
),
),
),
expectPushFailed(
errors.ThrowPreconditionFailed(nil, "id", "name already exists"),
[]*repository.Event{
@ -160,30 +92,6 @@ func TestCommands_AddAction(t *testing.T) {
"push ok",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
func() eventstore.Command {
e, _ := org.NewFeaturesSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
[]features.FeaturesChanges{
features.ChangeMaxActions(2),
},
)
return e
}(),
),
),
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
"name",
"name() {};",
0,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
@ -663,94 +571,10 @@ func TestCommands_ReactivateAction(t *testing.T) {
err: errors.IsPreconditionFailed,
},
},
{
"no additional allowed, error",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
"name",
"name() {};",
0,
false,
),
),
eventFromEventPusher(
action.NewDeactivatedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
),
),
),
expectFilter(
eventFromEventPusher(
func() eventstore.Command {
e, _ := org.NewFeaturesSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
[]features.FeaturesChanges{
features.ChangeMaxActions(1),
},
)
return e
}(),
),
),
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id2", "org1").Aggregate,
"name2",
"name2() {};",
0,
false,
),
),
),
),
},
args{
ctx: context.Background(),
actionID: "id1",
resourceOwner: "org1",
},
res{
err: errors.IsPreconditionFailed,
},
},
{
"reactivate ok",
fields{
eventstore: eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
"name",
"name() {};",
0,
false,
),
),
eventFromEventPusher(
action.NewDeactivatedEvent(context.Background(),
&action.NewAggregate("id1", "org1").Aggregate,
),
),
),
expectFilter(
eventFromEventPusher(
func() eventstore.Command {
e, _ := org.NewFeaturesSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
[]features.FeaturesChanges{
features.ChangeMaxActions(1),
},
)
return e
}(),
),
),
expectFilter(
eventFromEventPusher(
action.NewAddedEvent(context.Background(),

View File

@ -1,393 +0,0 @@
package command
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/org"
)
func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, features *domain.Features) (*domain.ObjectDetails, error) {
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Features-G5tg", "Errors.ResourceOwnerMissing")
}
err := c.checkOrgExists(ctx, resourceOwner)
if err != nil {
return nil, err
}
existingFeatures := NewOrgFeaturesWriteModel(resourceOwner)
err = c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
setEvent, hasChanged := existingFeatures.NewSetEvent(
ctx,
OrgAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel),
features.TierName,
features.TierDescription,
features.State,
features.StateDescription,
features.AuditLogRetention,
features.LoginPolicyFactors,
features.LoginPolicyIDP,
features.LoginPolicyPasswordless,
features.LoginPolicyRegistration,
features.LoginPolicyUsernameLogin,
features.LoginPolicyPasswordReset,
features.PasswordComplexityPolicy,
features.LabelPolicyPrivateLabel,
features.LabelPolicyWatermark,
features.CustomDomain,
features.PrivacyPolicy,
features.MetadataUser,
features.CustomTextMessage,
features.CustomTextLogin,
features.LockoutPolicy,
features.ActionsAllowed,
features.MaxActions,
)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged")
}
events, err := c.ensureOrgSettingsToFeatures(ctx, resourceOwner, features)
if err != nil {
return nil, err
}
events = append(events, setEvent)
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingFeatures, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
}
func (c *Commands) RemoveOrgFeatures(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Features-G5tg", "Errors.ResourceOwnerMissing")
}
existingFeatures := NewOrgFeaturesWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
if existingFeatures.State == domain.FeaturesStateUnspecified || existingFeatures.State == domain.FeaturesStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Features-Bg32G", "Errors.Features.NotFound")
}
removedEvent := org.NewFeaturesRemovedEvent(ctx, OrgAggregateFromWriteModel(&existingFeatures.FeaturesWriteModel.WriteModel))
features, err := c.getDefaultFeatures(ctx)
if err != nil {
return nil, err
}
events, err := c.ensureOrgSettingsToFeatures(ctx, orgID, features)
if err != nil {
return nil, err
}
events = append(events, removedEvent)
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(existingFeatures, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingFeatures.WriteModel), nil
}
func (c *Commands) ensureOrgSettingsToFeatures(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.Command, error) {
events, err := c.setAllowedLoginPolicy(ctx, orgID, features)
if err != nil {
return nil, err
}
if !features.PasswordComplexityPolicy {
removePasswordComplexityEvent, err := c.removePasswordComplexityPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removePasswordComplexityEvent != nil {
events = append(events, removePasswordComplexityEvent)
}
}
labelPolicyEvents, err := c.setAllowedLabelPolicy(ctx, orgID, features)
if err != nil {
return nil, err
}
events = append(events, labelPolicyEvents...)
if !features.CustomDomain {
removeCustomDomainsEvents, err := c.removeCustomDomains(ctx, orgID)
if err != nil {
return nil, err
}
if removeCustomDomainsEvents != nil {
events = append(events, removeCustomDomainsEvents...)
}
}
if !features.CustomTextMessage {
removeCustomMessageTextEvents, err := c.removeOrgMessageTextsIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removeCustomMessageTextEvents != nil {
events = append(events, removeCustomMessageTextEvents...)
}
}
if !features.CustomTextLogin {
removeCustomLoginTextEvents, err := c.removeOrgLoginTextsIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removeCustomLoginTextEvents != nil {
events = append(events, removeCustomLoginTextEvents...)
}
}
if !features.PrivacyPolicy {
removePrivacyPolicyEvent, err := c.removePrivacyPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removePrivacyPolicyEvent != nil {
events = append(events, removePrivacyPolicyEvent)
}
}
if !features.LockoutPolicy {
removeLockoutPolicyEvent, err := c.removeLockoutPolicyIfExists(ctx, orgID)
if err != nil {
return nil, err
}
if removeLockoutPolicyEvent != nil {
events = append(events, removeLockoutPolicyEvent)
}
}
if !features.MetadataUser {
removeOrgUserMetadatas, err := c.removeUserMetadataFromOrg(ctx, orgID)
if err != nil {
return nil, err
}
if len(removeOrgUserMetadatas) > 0 {
events = append(events, removeOrgUserMetadatas...)
}
}
if features.ActionsAllowed == domain.ActionsNotAllowed {
removeOrgActions, err := c.removeActionsFromOrg(ctx, orgID)
if err != nil {
return nil, err
}
if len(removeOrgActions) > 0 {
events = append(events, removeOrgActions...)
}
}
if features.ActionsAllowed == domain.ActionsMaxAllowed {
deactivateActions, err := c.deactivateNotAllowedActionsFromOrg(ctx, orgID, features.MaxActions)
if err != nil {
return nil, err
}
if len(deactivateActions) > 0 {
events = append(events, deactivateActions...)
}
}
return events, nil
}
func (c *Commands) setAllowedLoginPolicy(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.Command, error) {
events := make([]eventstore.Command, 0)
existingPolicy, err := c.orgLoginPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, nil
}
defaultPolicy, err := c.getDefaultLoginPolicy(ctx)
if err != nil {
return nil, err
}
policy := *existingPolicy
if !features.LoginPolicyFactors {
if defaultPolicy.ForceMFA != existingPolicy.ForceMFA {
policy.ForceMFA = defaultPolicy.ForceMFA
}
authFactorsEvents, err := c.setDefaultAuthFactorsInCustomLoginPolicy(ctx, orgID)
if err != nil {
return nil, err
}
events = append(events, authFactorsEvents...)
}
if !features.LoginPolicyIDP {
if defaultPolicy.AllowExternalIDP != existingPolicy.AllowExternalIDP {
policy.AllowExternalIDP = defaultPolicy.AllowExternalIDP
}
//TODO: handle idps
}
if !features.LoginPolicyRegistration && defaultPolicy.AllowRegister != existingPolicy.AllowRegister {
policy.AllowRegister = defaultPolicy.AllowRegister
}
if !features.LoginPolicyPasswordless && defaultPolicy.PasswordlessType != existingPolicy.PasswordlessType {
policy.PasswordlessType = defaultPolicy.PasswordlessType
}
if !features.LoginPolicyUsernameLogin && defaultPolicy.AllowUsernamePassword != existingPolicy.AllowUserNamePassword {
policy.AllowUserNamePassword = defaultPolicy.AllowUsernamePassword
}
if !features.LoginPolicyPasswordReset && defaultPolicy.HidePasswordReset != existingPolicy.HidePasswordReset {
policy.HidePasswordReset = defaultPolicy.HidePasswordReset
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx,
OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
policy.AllowUserNamePassword,
policy.AllowRegister,
policy.AllowExternalIDP,
policy.ForceMFA,
policy.HidePasswordReset,
policy.PasswordlessType,
policy.PasswordCheckLifetime,
policy.ExternalLoginCheckLifetime,
policy.MFAInitSkipLifetime,
policy.SecondFactorCheckLifetime,
policy.MultiFactorCheckLifetime)
if hasChanged {
events = append(events, changedEvent)
}
return events, nil
}
func (c *Commands) setDefaultAuthFactorsInCustomLoginPolicy(ctx context.Context, orgID string) ([]eventstore.Command, error) {
orgAuthFactors, err := c.orgLoginPolicyAuthFactorsWriteModel(ctx, orgID)
if err != nil {
return nil, err
}
events := make([]eventstore.Command, 0)
for _, factor := range domain.SecondFactorTypes() {
state := orgAuthFactors.SecondFactors[factor]
if state == nil || state.IAM == state.Org {
continue
}
secondFactorWriteModel := orgAuthFactors.ToSecondFactorWriteModel(factor)
if state.IAM == domain.FactorStateActive {
event, err := c.addSecondFactorToLoginPolicy(ctx, secondFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
continue
}
event, err := c.removeSecondFactorFromLoginPolicy(ctx, secondFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
}
for _, factor := range domain.MultiFactorTypes() {
state := orgAuthFactors.MultiFactors[factor]
if state == nil || state.IAM == state.Org {
continue
}
multiFactorWriteModel := orgAuthFactors.ToMultiFactorWriteModel(factor)
if state.IAM == domain.FactorStateActive {
event, err := c.addMultiFactorToLoginPolicy(ctx, multiFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
continue
}
event, err := c.removeMultiFactorFromLoginPolicy(ctx, multiFactorWriteModel, factor)
if err != nil {
return nil, err
}
if event != nil {
events = append(events, event)
}
}
return events, nil
}
func (c *Commands) setAllowedLabelPolicy(ctx context.Context, orgID string, features *domain.Features) ([]eventstore.Command, error) {
events := make([]eventstore.Command, 0)
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, nil
}
if !features.LabelPolicyPrivateLabel && !features.LabelPolicyWatermark {
removeEvent, err := c.removeLabelPolicy(ctx, existingPolicy)
if err != nil {
return nil, err
}
return append(events, removeEvent), nil
}
defaultPolicy, err := c.getDefaultLabelPolicy(ctx)
if err != nil {
return nil, err
}
policy := *existingPolicy
if !features.LabelPolicyWatermark && defaultPolicy.DisableWatermark != existingPolicy.DisableWatermark {
policy.DisableWatermark = defaultPolicy.DisableWatermark
}
if !features.LabelPolicyPrivateLabel {
if defaultPolicy.HideLoginNameSuffix != existingPolicy.HideLoginNameSuffix {
policy.HideLoginNameSuffix = defaultPolicy.HideLoginNameSuffix
}
policy.PrimaryColor = ""
policy.BackgroundColor = ""
policy.WarnColor = ""
policy.FontColor = ""
policy.PrimaryColorDark = ""
policy.BackgroundColorDark = ""
policy.WarnColorDark = ""
policy.FontColorDark = ""
assetsEvent, err := c.removeLabelPolicyAssets(ctx, existingPolicy)
if err != nil {
return nil, err
}
events = append(events, assetsEvent)
}
changedEvent, hasChangedEvent := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
policy.PrimaryColor, policy.BackgroundColor, policy.WarnColor, policy.FontColor,
policy.PrimaryColorDark, policy.BackgroundColorDark, policy.WarnColorDark, policy.FontColorDark,
policy.HideLoginNameSuffix, policy.ErrorMsgPopup, policy.HideLoginNameSuffix)
if hasChangedEvent {
events = append(events, changedEvent)
}
if len(events) > 0 {
events = append(events, org.NewLabelPolicyActivatedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel)))
}
return events, nil
}
func (c *Commands) getOrgFeaturesOrDefault(ctx context.Context, orgID string) (*domain.Features, error) {
existingFeatures := NewOrgFeaturesWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, existingFeatures)
if err != nil {
return nil, err
}
if existingFeatures.State != domain.FeaturesStateUnspecified && existingFeatures.State != domain.FeaturesStateRemoved {
return writeModelToFeatures(&existingFeatures.FeaturesWriteModel), nil
}
existingIAMFeatures := NewInstanceFeaturesWriteModel(ctx)
err = c.eventstore.FilterToQueryReducer(ctx, existingIAMFeatures)
if err != nil {
return nil, err
}
return writeModelToFeatures(&existingIAMFeatures.FeaturesWriteModel), nil
}

View File

@ -1,163 +0,0 @@
package command
import (
"context"
"time"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/features"
"github.com/zitadel/zitadel/internal/repository/org"
)
type OrgFeaturesWriteModel struct {
FeaturesWriteModel
}
func NewOrgFeaturesWriteModel(orgID string) *OrgFeaturesWriteModel {
return &OrgFeaturesWriteModel{
FeaturesWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: orgID,
ResourceOwner: orgID,
},
},
}
}
func (wm *OrgFeaturesWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *org.FeaturesSetEvent:
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesSetEvent)
case *org.FeaturesRemovedEvent:
wm.FeaturesWriteModel.AppendEvents(&e.FeaturesRemovedEvent)
}
}
}
func (wm *OrgFeaturesWriteModel) IsValid() bool {
return wm.AggregateID != ""
}
func (wm *OrgFeaturesWriteModel) Reduce() error {
return wm.FeaturesWriteModel.Reduce()
}
func (wm *OrgFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(org.AggregateType).
AggregateIDs(wm.FeaturesWriteModel.AggregateID).
EventTypes(
org.FeaturesSetEventType,
org.FeaturesRemovedEventType).
Builder()
}
func (wm *OrgFeaturesWriteModel) NewSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tierName,
tierDescription string,
state domain.FeaturesState,
stateDescription string,
auditLogRetention time.Duration,
loginPolicyFactors,
loginPolicyIDP,
loginPolicyPasswordless,
loginPolicyRegistration,
loginPolicyUsernameLogin,
loginPolicyPasswordReset,
passwordComplexityPolicy,
labelPolicyPrivateLabel,
labelPolicyWatermark,
customDomain,
privacyPolicy,
metadataUser,
customTextMessage,
customTextLogin,
lockoutPolicy bool,
actionsAllowed domain.ActionsAllowed,
maxActions int,
) (*org.FeaturesSetEvent, bool) {
changes := make([]features.FeaturesChanges, 0)
if tierName != "" && wm.TierName != tierName {
changes = append(changes, features.ChangeTierName(tierName))
}
if wm.TierDescription != tierDescription {
changes = append(changes, features.ChangeTierDescription(tierDescription))
}
if wm.State != state {
changes = append(changes, features.ChangeState(state))
}
if stateDescription != "" && wm.StateDescription != stateDescription {
changes = append(changes, features.ChangeStateDescription(stateDescription))
}
if auditLogRetention != 0 && wm.AuditLogRetention != auditLogRetention {
changes = append(changes, features.ChangeAuditLogRetention(auditLogRetention))
}
if wm.LoginPolicyFactors != loginPolicyFactors {
changes = append(changes, features.ChangeLoginPolicyFactors(loginPolicyFactors))
}
if wm.LoginPolicyIDP != loginPolicyIDP {
changes = append(changes, features.ChangeLoginPolicyIDP(loginPolicyIDP))
}
if wm.LoginPolicyPasswordless != loginPolicyPasswordless {
changes = append(changes, features.ChangeLoginPolicyPasswordless(loginPolicyPasswordless))
}
if wm.LoginPolicyRegistration != loginPolicyRegistration {
changes = append(changes, features.ChangeLoginPolicyRegistration(loginPolicyRegistration))
}
if wm.LoginPolicyUsernameLogin != loginPolicyUsernameLogin {
changes = append(changes, features.ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin))
}
if wm.LoginPolicyPasswordReset != loginPolicyPasswordReset {
changes = append(changes, features.ChangeLoginPolicyPasswordReset(loginPolicyPasswordReset))
}
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
}
if wm.LabelPolicyPrivateLabel != labelPolicyPrivateLabel {
changes = append(changes, features.ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel))
}
if wm.LabelPolicyWatermark != labelPolicyWatermark {
changes = append(changes, features.ChangeLabelPolicyWatermark(labelPolicyWatermark))
}
if wm.CustomDomain != customDomain {
changes = append(changes, features.ChangeCustomDomain(customDomain))
}
if wm.PrivacyPolicy != privacyPolicy {
changes = append(changes, features.ChangePrivacyPolicy(privacyPolicy))
}
if wm.MetadataUser != metadataUser {
changes = append(changes, features.ChangeMetadataUser(metadataUser))
}
if wm.CustomTextMessage != customTextMessage {
changes = append(changes, features.ChangeCustomTextMessage(customTextMessage))
}
if wm.CustomTextLogin != customTextLogin {
changes = append(changes, features.ChangeCustomTextLogin(customTextLogin))
}
if wm.LockoutPolicy != lockoutPolicy {
changes = append(changes, features.ChangeLockoutPolicy(lockoutPolicy))
}
if wm.ActionsAllowed != actionsAllowed {
changes = append(changes, features.ChangeActionsAllowed(actionsAllowed))
}
if wm.MaxActions != maxActions {
changes = append(changes, features.ChangeMaxActions(maxActions))
}
if len(changes) == 0 {
return nil, false
}
changedEvent, err := org.NewFeaturesSetEvent(ctx, aggregate, changes)
if err != nil {
return nil, false
}
return changedEvent, true
}

File diff suppressed because it is too large Load Diff

View File

@ -25,10 +25,6 @@ func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, pol
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-2B0ps", "Errors.Org.LabelPolicy.AlreadyExists")
}
err = c.checkLabelPolicyAllowed(ctx, resourceOwner, policy)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyAddedEvent(
ctx,
@ -70,11 +66,6 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
return nil, caos_errs.ThrowNotFound(nil, "Org-0K9dq", "Errors.Org.LabelPolicy.NotFound")
}
err = c.checkLabelPolicyAllowed(ctx, resourceOwner, policy)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
@ -105,28 +96,6 @@ func (c *Commands) ChangeLabelPolicy(ctx context.Context, resourceOwner string,
return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil
}
func (c *Commands) checkLabelPolicyAllowed(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) error {
defaultPolicy, err := c.getDefaultLabelPolicy(ctx)
if err != nil {
return err
}
requiredFeatures := make([]string, 0)
if defaultPolicy.PrimaryColor != policy.PrimaryColor || defaultPolicy.PrimaryColorDark != policy.PrimaryColorDark ||
defaultPolicy.FontColor != policy.FontColor || defaultPolicy.FontColorDark != policy.FontColorDark ||
defaultPolicy.BackgroundColor != policy.BackgroundColor || defaultPolicy.BackgroundColorDark != policy.BackgroundColorDark ||
defaultPolicy.WarnColor != defaultPolicy.WarnColor || defaultPolicy.WarnColorDark != defaultPolicy.WarnColorDark ||
defaultPolicy.LogoURL != policy.LogoURL || defaultPolicy.LogoDarkURL != policy.LogoDarkURL ||
defaultPolicy.IconURL != policy.IconURL || defaultPolicy.IconDarkURL != policy.IconDarkURL ||
defaultPolicy.Font != policy.Font ||
defaultPolicy.HideLoginNameSuffix != policy.HideLoginNameSuffix {
requiredFeatures = append(requiredFeatures, domain.FeatureLabelPolicyPrivateLabel)
}
if defaultPolicy.DisableWatermark != policy.DisableWatermark {
requiredFeatures = append(requiredFeatures, domain.FeatureLabelPolicyWatermark)
}
return c.tokenVerifier.CheckOrgFeatures(ctx, resourceOwner, requiredFeatures...)
}
func (c *Commands) ActivateLabelPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-KKd4X", "Errors.ResourceOwnerMissing")

View File

@ -13,7 +13,6 @@ import (
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/policy"
"github.com/zitadel/zitadel/internal/static"
@ -23,7 +22,6 @@ import (
func TestCommandSide_AddLabelPolicy(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
tokenVerifier orgFeatureChecker
}
type args struct {
ctx context.Context
@ -104,78 +102,12 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "add policy, permission denied",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewLabelPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
false,
),
),
),
),
tokenVerifier: GetMockVerifier(t),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LabelPolicy{
PrimaryColor: "#ffffff",
BackgroundColor: "#ffffff",
WarnColor: "#ffffff",
FontColor: "#ffffff",
PrimaryColorDark: "#ffffff",
BackgroundColorDark: "#ffffff",
WarnColorDark: "#ffffff",
FontColorDark: "#ffffff",
HideLoginNameSuffix: true,
ErrorMsgPopup: true,
DisableWatermark: true,
},
},
res: res{
err: caos_errs.IsPermissionDenied,
},
},
{
name: "add policy,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewLabelPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
@ -197,7 +129,6 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
},
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLabelPolicyPrivateLabel, domain.FeatureLabelPolicyWatermark),
},
args: args{
ctx: context.Background(),
@ -241,7 +172,6 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
tokenVerifier: tt.fields.tokenVerifier,
}
got, err := r.AddLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil {
@ -260,7 +190,6 @@ func TestCommandSide_AddLabelPolicy(t *testing.T) {
func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
tokenVerifier orgFeatureChecker
}
type args struct {
ctx context.Context
@ -316,71 +245,6 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
err: caos_errs.IsNotFound,
},
},
{
name: "permission denied error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLabelPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
true,
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLabelPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
false,
),
),
),
),
tokenVerifier: GetMockVerifier(t),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LabelPolicy{
PrimaryColor: "#000000",
BackgroundColor: "#000000",
WarnColor: "#000000",
FontColor: "#000000",
PrimaryColorDark: "#000000",
BackgroundColorDark: "#000000",
WarnColorDark: "#000000",
FontColorDark: "#000000",
HideLoginNameSuffix: true,
ErrorMsgPopup: true,
DisableWatermark: true,
},
},
res: res{
err: caos_errs.IsPermissionDenied,
},
},
{
name: "no changes, precondition error",
fields: fields{
@ -404,26 +268,7 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLabelPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
false,
),
),
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLabelPolicyPrivateLabel, domain.FeatureLabelPolicyWatermark),
},
args: args{
ctx: context.Background(),
@ -469,24 +314,6 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLabelPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
"#ffffff",
true,
true,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
@ -508,7 +335,6 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
},
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLabelPolicyPrivateLabel, domain.FeatureLabelPolicyWatermark),
},
args: args{
ctx: context.Background(),
@ -552,7 +378,6 @@ func TestCommandSide_ChangeLabelPolicy(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
tokenVerifier: tt.fields.tokenVerifier,
}
got, err := r.ChangeLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil {

View File

@ -2,7 +2,6 @@ package command
import (
"context"
"reflect"
"github.com/zitadel/logging"
@ -26,11 +25,6 @@ func (c *Commands) AddLoginPolicy(ctx context.Context, resourceOwner string, pol
return nil, caos_errs.ThrowAlreadyExists(nil, "Org-Dgfb2", "Errors.Org.LoginPolicy.AlreadyExists")
}
err = c.checkLoginPolicyAllowed(ctx, resourceOwner, policy)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
pushedEvents, err := c.eventstore.Push(
ctx,
@ -91,11 +85,6 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
return nil, caos_errs.ThrowNotFound(nil, "Org-M0sif", "Errors.Org.LoginPolicy.NotFound")
}
err = c.checkLoginPolicyAllowed(ctx, resourceOwner, policy)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
ctx,
@ -127,33 +116,6 @@ func (c *Commands) ChangeLoginPolicy(ctx context.Context, resourceOwner string,
return writeModelToLoginPolicy(&existingPolicy.LoginPolicyWriteModel), nil
}
func (c *Commands) checkLoginPolicyAllowed(ctx context.Context, resourceOwner string, policy *domain.LoginPolicy) error {
defaultPolicy, err := c.getDefaultLoginPolicy(ctx)
if err != nil {
return err
}
requiredFeatures := make([]string, 0)
if defaultPolicy.ForceMFA != policy.ForceMFA || !reflect.DeepEqual(defaultPolicy.MultiFactors, policy.MultiFactors) || !reflect.DeepEqual(defaultPolicy.SecondFactors, policy.SecondFactors) {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyFactors)
}
if defaultPolicy.AllowExternalIDP != policy.AllowExternalIDP || !reflect.DeepEqual(defaultPolicy.IDPProviders, policy.IDPProviders) {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyIDP)
}
if defaultPolicy.AllowRegister != policy.AllowRegister {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyRegistration)
}
if defaultPolicy.PasswordlessType != policy.PasswordlessType {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordless)
}
if defaultPolicy.AllowUsernamePassword != policy.AllowUsernamePassword {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyUsernameLogin)
}
if defaultPolicy.HidePasswordReset != policy.HidePasswordReset {
requiredFeatures = append(requiredFeatures, domain.FeatureLoginPolicyPasswordReset)
}
return c.tokenVerifier.CheckOrgFeatures(ctx, resourceOwner, requiredFeatures...)
}
func (c *Commands) RemoveLoginPolicy(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-55Mg9", "Errors.ResourceOwnerMissing")

View File

@ -12,7 +12,6 @@ import (
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/policy"
"github.com/zitadel/zitadel/internal/repository/user"
@ -29,7 +28,6 @@ var (
func TestCommandSide_AddLoginPolicy(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
tokenVerifier orgFeatureChecker
}
type args struct {
ctx context.Context
@ -110,77 +108,12 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "loginpolicy not allowed, permission denied error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
false,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
),
tokenVerifier: GetMockVerifier(t),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
err: caos_errs.IsPermissionDenied,
},
},
{
name: "add policy,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
false,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
@ -202,7 +135,6 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
},
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
},
args: args{
ctx: context.Background(),
@ -246,7 +178,6 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
tokenVerifier: tt.fields.tokenVerifier,
}
got, err := r.AddLoginPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil {
@ -265,7 +196,6 @@ func TestCommandSide_AddLoginPolicy(t *testing.T) {
func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
tokenVerifier orgFeatureChecker
}
type args struct {
ctx context.Context
@ -326,70 +256,6 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
err: caos_errs.IsNotFound,
},
},
{
name: "not allowed, permission denied error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewLoginPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
true,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
false,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
),
tokenVerifier: GetMockVerifier(t),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.LoginPolicy{
AllowRegister: true,
AllowUsernamePassword: true,
AllowExternalIDP: true,
ForceMFA: true,
PasswordlessType: domain.PasswordlessTypeAllowed,
PasswordCheckLifetime: time.Hour * 1,
ExternalLoginCheckLifetime: time.Hour * 2,
MFAInitSkipLifetime: time.Hour * 3,
SecondFactorCheckLifetime: time.Hour * 4,
MultiFactorCheckLifetime: time.Hour * 5,
},
},
res: res{
err: caos_errs.IsPermissionDenied,
},
},
{
name: "no changes, precondition error",
fields: fields{
@ -413,26 +279,7 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
false,
true,
true,
true,
true,
domain.PasswordlessTypeAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
},
args: args{
ctx: context.Background(),
@ -478,24 +325,6 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewLoginPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
false,
false,
false,
false,
false,
domain.PasswordlessTypeNotAllowed,
time.Hour*1,
time.Hour*2,
time.Hour*3,
time.Hour*4,
time.Hour*5,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
@ -517,7 +346,6 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
},
),
),
tokenVerifier: GetMockVerifier(t, domain.FeatureLoginPolicyUsernameLogin),
},
args: args{
ctx: context.Background(),
@ -560,7 +388,6 @@ func TestCommandSide_ChangeLoginPolicy(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
tokenVerifier: tt.fields.tokenVerifier,
}
got, err := r.ChangeLoginPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
if tt.res.err == nil {

View File

@ -1,76 +0,0 @@
package domain
import (
"time"
es_models "github.com/zitadel/zitadel/internal/eventstore/v1/models"
)
const (
FeatureLoginPolicy = "login_policy"
FeatureLoginPolicyFactors = FeatureLoginPolicy + ".factors"
FeatureLoginPolicyIDP = FeatureLoginPolicy + ".idp"
FeatureLoginPolicyPasswordless = FeatureLoginPolicy + ".passwordless"
FeatureLoginPolicyRegistration = FeatureLoginPolicy + ".registration"
FeatureLoginPolicyUsernameLogin = FeatureLoginPolicy + ".username_login"
FeatureLoginPolicyPasswordReset = FeatureLoginPolicy + ".password_reset"
FeaturePasswordComplexityPolicy = "password_complexity_policy"
FeatureLabelPolicy = "label_policy"
FeatureLabelPolicyPrivateLabel = FeatureLabelPolicy + ".private_label"
FeatureLabelPolicyWatermark = FeatureLabelPolicy + ".watermark"
FeatureCustomDomain = "custom_domain"
FeaturePrivacyPolicy = "privacy_policy"
FeatureLockoutPolicy = "lockout_policy"
FeatureMetadata = "metadata"
FeatureCustomText = "custom_text"
FeatureCustomTextMessage = FeatureCustomText + ".message"
FeatureCustomTextLogin = FeatureCustomText + ".login"
FeatureMetadataUser = FeatureMetadata + ".user"
FeatureActions = "actions"
)
type Features struct {
es_models.ObjectRoot
TierName string
TierDescription string
State FeaturesState
StateDescription string
IsDefault bool
AuditLogRetention time.Duration
LoginPolicyFactors bool
LoginPolicyIDP bool
LoginPolicyPasswordless bool
LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool
LabelPolicyPrivateLabel bool
LabelPolicyWatermark bool
CustomDomain bool
CustomTextMessage bool
CustomTextLogin bool
PrivacyPolicy bool
MetadataUser bool
LockoutPolicy bool
ActionsAllowed ActionsAllowed
MaxActions int
}
type FeaturesState int32
const (
FeaturesStateUnspecified FeaturesState = iota
FeaturesStateActive
FeaturesStateActionRequired
FeaturesStateCanceled
FeaturesStateGrandfathered
FeaturesStateRemoved
featuresStateCount
)
func (f FeaturesState) Valid() bool {
return f >= 0 && f < featuresStateCount
}

View File

@ -21,11 +21,10 @@ const {{$s.Name}}_MethodPrefix = "{{$.File.Package}}.{{$s.Name}}"
var {{$s.Name}}_AuthMethods = authz.MethodMapping {
{{ range $m := $s.Method}}
{{ $mAuthOpt := option $m.Options "zitadel.v1.auth_option" }}
{{ if and $mAuthOpt (or $mAuthOpt.Permission $mAuthOpt.Feature) }}
{{ if and $mAuthOpt $mAuthOpt.Permission }}
"/{{$.File.Package}}.{{$s.Name}}/{{.Name}}": authz.Option{
Permission: "{{$mAuthOpt.Permission}}",
CheckParam: "{{$mAuthOpt.CheckFieldName}}",
Feature: "{{$mAuthOpt.Feature}}",
},
{{end}}
{{ end}}

View File

@ -87,7 +87,7 @@ func (q *Queries) changes(ctx context.Context, query func(query *eventstore.Sear
}
changes := make([]*Change, 0, len(events))
for _, event := range events {
if event.CreationDate().Before(time.Now().Add(-auditLogRetention)) {
if auditLogRetention != 0 && event.CreationDate().Before(time.Now().Add(-auditLogRetention)) {
continue
}
change := &Change{

View File

@ -1,327 +0,0 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/query/projection"
)
type Features struct {
AggregateID string
ChangeDate time.Time
Sequence uint64
IsDefault bool
TierName string
TierDescription string
State domain.FeaturesState
StateDescription string
AuditLogRetention time.Duration
LoginPolicyFactors bool
LoginPolicyIDP bool
LoginPolicyPasswordless bool
LoginPolicyRegistration bool
LoginPolicyUsernameLogin bool
LoginPolicyPasswordReset bool
PasswordComplexityPolicy bool
LabelPolicyPrivateLabel bool
LabelPolicyWatermark bool
CustomDomain bool
PrivacyPolicy bool
MetadataUser bool
CustomTextMessage bool
CustomTextLogin bool
LockoutPolicy bool
ActionsAllowed domain.ActionsAllowed
MaxActions int32
}
var (
featureTable = table{
name: projection.FeatureTable,
}
FeatureColumnAggregateID = Column{
name: projection.FeatureAggregateIDCol,
table: featureTable,
}
FeatureColumnInstanceID = Column{
name: projection.FeatureInstanceIDCol,
table: featureTable,
}
FeatureColumnChangeDate = Column{
name: projection.FeatureChangeDateCol,
table: featureTable,
}
FeatureColumnSequence = Column{
name: projection.FeatureSequenceCol,
table: featureTable,
}
FeatureColumnIsDefault = Column{
name: projection.FeatureIsDefaultCol,
table: featureTable,
}
FeatureTierName = Column{
name: projection.FeatureTierNameCol,
table: featureTable,
}
FeatureTierDescription = Column{
name: projection.FeatureTierDescriptionCol,
table: featureTable,
}
FeatureState = Column{
name: projection.FeatureStateCol,
table: featureTable,
}
FeatureStateDescription = Column{
name: projection.FeatureStateDescriptionCol,
table: featureTable,
}
FeatureAuditLogRetention = Column{
name: projection.FeatureAuditLogRetentionCol,
table: featureTable,
}
FeatureLoginPolicyFactors = Column{
name: projection.FeatureLoginPolicyFactorsCol,
table: featureTable,
}
FeatureLoginPolicyIDP = Column{
name: projection.FeatureLoginPolicyIDPCol,
table: featureTable,
}
FeatureLoginPolicyPasswordless = Column{
name: projection.FeatureLoginPolicyPasswordlessCol,
table: featureTable,
}
FeatureLoginPolicyRegistration = Column{
name: projection.FeatureLoginPolicyRegistrationCol,
table: featureTable,
}
FeatureLoginPolicyUsernameLogin = Column{
name: projection.FeatureLoginPolicyUsernameLoginCol,
table: featureTable,
}
FeatureLoginPolicyPasswordReset = Column{
name: projection.FeatureLoginPolicyPasswordResetCol,
table: featureTable,
}
FeaturePasswordComplexityPolicy = Column{
name: projection.FeaturePasswordComplexityPolicyCol,
table: featureTable,
}
FeatureLabelPolicyPrivateLabel = Column{
name: projection.FeatureLabelPolicyPrivateLabelCol,
table: featureTable,
}
FeatureLabelPolicyWatermark = Column{
name: projection.FeatureLabelPolicyWatermarkCol,
table: featureTable,
}
FeatureCustomDomain = Column{
name: projection.FeatureCustomDomainCol,
table: featureTable,
}
FeaturePrivacyPolicy = Column{
name: projection.FeaturePrivacyPolicyCol,
table: featureTable,
}
FeatureMetadataUser = Column{
name: projection.FeatureMetadataUserCol,
table: featureTable,
}
FeatureCustomTextMessage = Column{
name: projection.FeatureCustomTextMessageCol,
table: featureTable,
}
FeatureCustomTextLogin = Column{
name: projection.FeatureCustomTextLoginCol,
table: featureTable,
}
FeatureLockoutPolicy = Column{
name: projection.FeatureLockoutPolicyCol,
table: featureTable,
}
FeatureActionsAllowed = Column{
name: projection.FeatureActionsAllowedCol,
table: featureTable,
}
FeatureMaxActions = Column{
name: projection.FeatureMaxActionsCol,
table: featureTable,
}
)
func (q *Queries) FeaturesByOrgID(ctx context.Context, orgID string) (*Features, error) {
query, scan := prepareFeaturesQuery()
stmt, args, err := query.Where(
sq.And{
sq.Eq{
FeatureColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
},
sq.Or{
sq.Eq{
FeatureColumnAggregateID.identifier(): orgID,
},
sq.Eq{
FeatureColumnAggregateID.identifier(): authz.GetInstance(ctx).InstanceID(),
},
},
}).
OrderBy(FeatureColumnIsDefault.identifier()).
Limit(1).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-P9gwg", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
func (q *Queries) DefaultFeatures(ctx context.Context) (*Features, error) {
query, scan := prepareFeaturesQuery()
stmt, args, err := query.Where(sq.Eq{
FeatureColumnAggregateID.identifier(): authz.GetInstance(ctx).InstanceID(),
FeatureColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-1Ndlg", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
func prepareFeaturesQuery() (sq.SelectBuilder, func(*sql.Row) (*Features, error)) {
return sq.Select(
FeatureColumnAggregateID.identifier(),
FeatureColumnChangeDate.identifier(),
FeatureColumnSequence.identifier(),
FeatureColumnIsDefault.identifier(),
FeatureTierName.identifier(),
FeatureTierDescription.identifier(),
FeatureState.identifier(),
FeatureStateDescription.identifier(),
FeatureAuditLogRetention.identifier(),
FeatureLoginPolicyFactors.identifier(),
FeatureLoginPolicyIDP.identifier(),
FeatureLoginPolicyPasswordless.identifier(),
FeatureLoginPolicyRegistration.identifier(),
FeatureLoginPolicyUsernameLogin.identifier(),
FeatureLoginPolicyPasswordReset.identifier(),
FeaturePasswordComplexityPolicy.identifier(),
FeatureLabelPolicyPrivateLabel.identifier(),
FeatureLabelPolicyWatermark.identifier(),
FeatureCustomDomain.identifier(),
FeaturePrivacyPolicy.identifier(),
FeatureMetadataUser.identifier(),
FeatureCustomTextMessage.identifier(),
FeatureCustomTextLogin.identifier(),
FeatureLockoutPolicy.identifier(),
FeatureActionsAllowed.identifier(),
FeatureMaxActions.identifier(),
).From(featureTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Features, error) {
p := new(Features)
tierName := sql.NullString{}
tierDescription := sql.NullString{}
stateDescription := sql.NullString{}
err := row.Scan(
&p.AggregateID,
&p.ChangeDate,
&p.Sequence,
&p.IsDefault,
&tierName,
&tierDescription,
&p.State,
&stateDescription,
&p.AuditLogRetention,
&p.LoginPolicyFactors,
&p.LoginPolicyIDP,
&p.LoginPolicyPasswordless,
&p.LoginPolicyRegistration,
&p.LoginPolicyUsernameLogin,
&p.LoginPolicyPasswordReset,
&p.PasswordComplexityPolicy,
&p.LabelPolicyPrivateLabel,
&p.LabelPolicyWatermark,
&p.CustomDomain,
&p.PrivacyPolicy,
&p.MetadataUser,
&p.CustomTextMessage,
&p.CustomTextLogin,
&p.LockoutPolicy,
&p.ActionsAllowed,
&p.MaxActions,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-M9fse", "Errors.Features.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-3o9gd", "Errors.Internal")
}
p.TierName = tierName.String
p.TierDescription = tierDescription.String
p.StateDescription = stateDescription.String
return p, nil
}
}
func (f *Features) EnabledFeatureTypes() []string {
list := make([]string, 0)
if f.LoginPolicyFactors {
list = append(list, domain.FeatureLoginPolicyFactors)
}
if f.LoginPolicyIDP {
list = append(list, domain.FeatureLoginPolicyIDP)
}
if f.LoginPolicyPasswordless {
list = append(list, domain.FeatureLoginPolicyPasswordless)
}
if f.LoginPolicyRegistration {
list = append(list, domain.FeatureLoginPolicyRegistration)
}
if f.LoginPolicyUsernameLogin {
list = append(list, domain.FeatureLoginPolicyUsernameLogin)
}
if f.LoginPolicyPasswordReset {
list = append(list, domain.FeatureLoginPolicyPasswordReset)
}
if f.PasswordComplexityPolicy {
list = append(list, domain.FeaturePasswordComplexityPolicy)
}
if f.LabelPolicyPrivateLabel {
list = append(list, domain.FeatureLabelPolicyPrivateLabel)
}
if f.LabelPolicyWatermark {
list = append(list, domain.FeatureLabelPolicyWatermark)
}
if f.CustomDomain {
list = append(list, domain.FeatureCustomDomain)
}
if f.PrivacyPolicy {
list = append(list, domain.FeaturePrivacyPolicy)
}
if f.MetadataUser {
list = append(list, domain.FeatureMetadataUser)
}
if f.CustomTextMessage {
list = append(list, domain.FeatureCustomTextMessage)
}
if f.CustomTextLogin {
list = append(list, domain.FeatureCustomTextLogin)
}
if f.LockoutPolicy {
list = append(list, domain.FeatureLockoutPolicy)
}
if f.ActionsAllowed != domain.ActionsNotAllowed {
list = append(list, domain.FeatureActions)
}
return list
}

View File

@ -1,358 +0,0 @@
package query
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"regexp"
"testing"
"time"
"github.com/zitadel/zitadel/internal/domain"
errs "github.com/zitadel/zitadel/internal/errors"
)
func Test_FeaturesPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := []struct {
name string
prepare interface{}
want want
object interface{}
}{
{
name: "prepareFeaturesQuery no result",
prepare: prepareFeaturesQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT projections.features.aggregate_id,`+
` projections.features.change_date,`+
` projections.features.sequence,`+
` projections.features.is_default,`+
` projections.features.tier_name,`+
` projections.features.tier_description,`+
` projections.features.state,`+
` projections.features.state_description,`+
` projections.features.audit_log_retention,`+
` projections.features.login_policy_factors,`+
` projections.features.login_policy_idp,`+
` projections.features.login_policy_passwordless,`+
` projections.features.login_policy_registration,`+
` projections.features.login_policy_username_login,`+
` projections.features.login_policy_password_reset,`+
` projections.features.password_complexity_policy,`+
` projections.features.label_policy_private_label,`+
` projections.features.label_policy_watermark,`+
` projections.features.custom_domain,`+
` projections.features.privacy_policy,`+
` projections.features.metadata_user,`+
` projections.features.custom_text_message,`+
` projections.features.custom_text_login,`+
` projections.features.lockout_policy,`+
` projections.features.actions_allowed,`+
` projections.features.max_actions`+
` FROM projections.features`),
nil,
nil,
),
err: func(err error) (error, bool) {
if !errs.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: (*Features)(nil),
},
{
name: "prepareFeaturesQuery found",
prepare: prepareFeaturesQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT projections.features.aggregate_id,`+
` projections.features.change_date,`+
` projections.features.sequence,`+
` projections.features.is_default,`+
` projections.features.tier_name,`+
` projections.features.tier_description,`+
` projections.features.state,`+
` projections.features.state_description,`+
` projections.features.audit_log_retention,`+
` projections.features.login_policy_factors,`+
` projections.features.login_policy_idp,`+
` projections.features.login_policy_passwordless,`+
` projections.features.login_policy_registration,`+
` projections.features.login_policy_username_login,`+
` projections.features.login_policy_password_reset,`+
` projections.features.password_complexity_policy,`+
` projections.features.label_policy_private_label,`+
` projections.features.label_policy_watermark,`+
` projections.features.custom_domain,`+
` projections.features.privacy_policy,`+
` projections.features.metadata_user,`+
` projections.features.custom_text_message,`+
` projections.features.custom_text_login,`+
` projections.features.lockout_policy,`+
` projections.features.actions_allowed,`+
` projections.features.max_actions`+
` FROM projections.features`),
[]string{
"aggregate_id",
"change_date",
"sequence",
"is_default",
"tier_name",
"tier_description",
"state",
"state_description",
"audit_log_retention",
"login_policy_factors",
"login_policy_idp",
"login_policy_passwordless",
"login_policy_registration",
"login_policy_username_login",
"login_policy_password_reset",
"password_complexity_policy",
"label_policy_private_label",
"label_policy_watermark",
"custom_domain",
"privacy_policy",
"metadata_user",
"custom_text_message",
"custom_text_login",
"lockout_policy",
"actions_allowed",
"max_actions",
},
[]driver.Value{
"aggregate-id",
testNow,
uint64(20211115),
true,
"tier-name",
"tier-description",
1,
"state-description",
uint(604800000000000), // 7days in nanoseconds
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsMaxAllowed,
10,
},
),
},
object: &Features{
AggregateID: "aggregate-id",
ChangeDate: testNow,
Sequence: 20211115,
IsDefault: true,
TierName: "tier-name",
TierDescription: "tier-description",
State: domain.FeaturesStateActive,
StateDescription: "state-description",
AuditLogRetention: 7 * 24 * time.Hour,
LoginPolicyFactors: true,
LoginPolicyIDP: true,
LoginPolicyPasswordless: true,
LoginPolicyRegistration: true,
LoginPolicyUsernameLogin: true,
LoginPolicyPasswordReset: true,
PasswordComplexityPolicy: true,
LabelPolicyPrivateLabel: true,
LabelPolicyWatermark: true,
CustomDomain: true,
PrivacyPolicy: true,
MetadataUser: true,
CustomTextMessage: true,
CustomTextLogin: true,
LockoutPolicy: true,
ActionsAllowed: domain.ActionsMaxAllowed,
MaxActions: 10,
},
},
{
name: "prepareFeaturesQuery found with empty",
prepare: prepareFeaturesQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT projections.features.aggregate_id,`+
` projections.features.change_date,`+
` projections.features.sequence,`+
` projections.features.is_default,`+
` projections.features.tier_name,`+
` projections.features.tier_description,`+
` projections.features.state,`+
` projections.features.state_description,`+
` projections.features.audit_log_retention,`+
` projections.features.login_policy_factors,`+
` projections.features.login_policy_idp,`+
` projections.features.login_policy_passwordless,`+
` projections.features.login_policy_registration,`+
` projections.features.login_policy_username_login,`+
` projections.features.login_policy_password_reset,`+
` projections.features.password_complexity_policy,`+
` projections.features.label_policy_private_label,`+
` projections.features.label_policy_watermark,`+
` projections.features.custom_domain,`+
` projections.features.privacy_policy,`+
` projections.features.metadata_user,`+
` projections.features.custom_text_message,`+
` projections.features.custom_text_login,`+
` projections.features.lockout_policy,`+
` projections.features.actions_allowed,`+
` projections.features.max_actions`+
` FROM projections.features`),
[]string{
"aggregate_id",
"change_date",
"sequence",
"is_default",
"tier_name",
"tier_description",
"state",
"state_description",
"audit_log_retention",
"login_policy_factors",
"login_policy_idp",
"login_policy_passwordless",
"login_policy_registration",
"login_policy_username_login",
"login_policy_password_reset",
"password_complexity_policy",
"label_policy_private_label",
"label_policy_watermark",
"custom_domain",
"privacy_policy",
"metadata_user",
"custom_text_message",
"custom_text_login",
"lockout_policy",
"actions_allowed",
"max_actions",
},
[]driver.Value{
"aggregate-id",
testNow,
uint64(20211115),
true,
nil,
nil,
1,
nil,
uint(604800000000000), // 7days in nanoseconds
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsMaxAllowed,
10,
},
),
},
object: &Features{
AggregateID: "aggregate-id",
ChangeDate: testNow,
Sequence: 20211115,
IsDefault: true,
TierName: "",
TierDescription: "",
State: domain.FeaturesStateActive,
StateDescription: "",
AuditLogRetention: 7 * 24 * time.Hour,
LoginPolicyFactors: true,
LoginPolicyIDP: true,
LoginPolicyPasswordless: true,
LoginPolicyRegistration: true,
LoginPolicyUsernameLogin: true,
LoginPolicyPasswordReset: true,
PasswordComplexityPolicy: true,
LabelPolicyPrivateLabel: true,
LabelPolicyWatermark: true,
CustomDomain: true,
PrivacyPolicy: true,
MetadataUser: true,
CustomTextMessage: true,
CustomTextLogin: true,
LockoutPolicy: true,
ActionsAllowed: domain.ActionsMaxAllowed,
MaxActions: 10,
},
},
{
name: "prepareFeaturesQuery sql err",
prepare: prepareFeaturesQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT projections.features.aggregate_id,`+
` projections.features.change_date,`+
` projections.features.sequence,`+
` projections.features.is_default,`+
` projections.features.tier_name,`+
` projections.features.tier_description,`+
` projections.features.state,`+
` projections.features.state_description,`+
` projections.features.audit_log_retention,`+
` projections.features.login_policy_factors,`+
` projections.features.login_policy_idp,`+
` projections.features.login_policy_passwordless,`+
` projections.features.login_policy_registration,`+
` projections.features.login_policy_username_login,`+
` projections.features.login_policy_password_reset,`+
` projections.features.password_complexity_policy,`+
` projections.features.label_policy_private_label,`+
` projections.features.label_policy_watermark,`+
` projections.features.custom_domain,`+
` projections.features.privacy_policy,`+
` projections.features.metadata_user,`+
` projections.features.custom_text_message,`+
` projections.features.custom_text_login,`+
` projections.features.lockout_policy,`+
` projections.features.actions_allowed,`+
` projections.features.max_actions`+
` FROM projections.features`),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
})
}
}

View File

@ -1,237 +0,0 @@
package projection
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
"github.com/zitadel/zitadel/internal/repository/features"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
)
const (
FeatureTable = "projections.features"
FeatureAggregateIDCol = "aggregate_id"
FeatureInstanceIDCol = "instance_id"
FeatureChangeDateCol = "change_date"
FeatureSequenceCol = "sequence"
FeatureIsDefaultCol = "is_default"
FeatureTierNameCol = "tier_name"
FeatureTierDescriptionCol = "tier_description"
FeatureStateCol = "state"
FeatureStateDescriptionCol = "state_description"
FeatureAuditLogRetentionCol = "audit_log_retention"
FeatureLoginPolicyFactorsCol = "login_policy_factors"
FeatureLoginPolicyIDPCol = "login_policy_idp"
FeatureLoginPolicyPasswordlessCol = "login_policy_passwordless"
FeatureLoginPolicyRegistrationCol = "login_policy_registration"
FeatureLoginPolicyUsernameLoginCol = "login_policy_username_login"
FeatureLoginPolicyPasswordResetCol = "login_policy_password_reset"
FeaturePasswordComplexityPolicyCol = "password_complexity_policy"
FeatureLabelPolicyPrivateLabelCol = "label_policy_private_label"
FeatureLabelPolicyWatermarkCol = "label_policy_watermark"
FeatureCustomDomainCol = "custom_domain"
FeaturePrivacyPolicyCol = "privacy_policy"
FeatureMetadataUserCol = "metadata_user"
FeatureCustomTextMessageCol = "custom_text_message"
FeatureCustomTextLoginCol = "custom_text_login"
FeatureLockoutPolicyCol = "lockout_policy"
FeatureActionsAllowedCol = "actions_allowed"
FeatureMaxActionsCol = "max_actions"
)
type FeatureProjection struct {
crdb.StatementHandler
}
func NewFeatureProjection(ctx context.Context, config crdb.StatementHandlerConfig) *FeatureProjection {
p := new(FeatureProjection)
config.ProjectionName = FeatureTable
config.Reducers = p.reducers()
config.InitCheck = crdb.NewTableCheck(
crdb.NewTable([]*crdb.Column{
crdb.NewColumn(FeatureAggregateIDCol, crdb.ColumnTypeText),
crdb.NewColumn(FeatureInstanceIDCol, crdb.ColumnTypeText),
crdb.NewColumn(FeatureChangeDateCol, crdb.ColumnTypeTimestamp),
crdb.NewColumn(FeatureSequenceCol, crdb.ColumnTypeInt64),
crdb.NewColumn(FeatureIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureTierNameCol, crdb.ColumnTypeText),
crdb.NewColumn(FeatureTierDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()),
crdb.NewColumn(FeatureStateCol, crdb.ColumnTypeEnum, crdb.Default(0)),
crdb.NewColumn(FeatureStateDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()),
crdb.NewColumn(FeatureAuditLogRetentionCol, crdb.ColumnTypeInt64, crdb.Default(0)),
crdb.NewColumn(FeatureLoginPolicyFactorsCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLoginPolicyIDPCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLoginPolicyPasswordlessCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLoginPolicyRegistrationCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLoginPolicyUsernameLoginCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLoginPolicyPasswordResetCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeaturePasswordComplexityPolicyCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLabelPolicyPrivateLabelCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLabelPolicyWatermarkCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureCustomDomainCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeaturePrivacyPolicyCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureMetadataUserCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureCustomTextMessageCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureCustomTextLoginCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureLockoutPolicyCol, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(FeatureActionsAllowedCol, crdb.ColumnTypeEnum, crdb.Default(0)),
crdb.NewColumn(FeatureMaxActionsCol, crdb.ColumnTypeInt64, crdb.Default(0)),
},
crdb.NewPrimaryKey(FeatureInstanceIDCol, FeatureAggregateIDCol),
),
)
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *FeatureProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: org.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: org.FeaturesSetEventType,
Reduce: p.reduceFeatureSet,
},
{
Event: org.FeaturesRemovedEventType,
Reduce: p.reduceFeatureRemoved,
},
},
},
{
Aggregate: instance.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: instance.FeaturesSetEventType,
Reduce: p.reduceFeatureSet,
},
},
},
}
}
func (p *FeatureProjection) reduceFeatureSet(event eventstore.Event) (*handler.Statement, error) {
var featureEvent features.FeaturesSetEvent
var isDefault bool
switch e := event.(type) {
case *instance.FeaturesSetEvent:
featureEvent = e.FeaturesSetEvent
isDefault = true
case *org.FeaturesSetEvent:
featureEvent = e.FeaturesSetEvent
isDefault = false
default:
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-K0erf", "reduce.wrong.event.type %v", []eventstore.EventType{org.FeaturesSetEventType, instance.FeaturesSetEventType})
}
cols := []handler.Column{
handler.NewCol(FeatureAggregateIDCol, featureEvent.Aggregate().ID),
handler.NewCol(FeatureInstanceIDCol, featureEvent.Aggregate().InstanceID),
handler.NewCol(FeatureChangeDateCol, featureEvent.CreationDate()),
handler.NewCol(FeatureSequenceCol, featureEvent.Sequence()),
handler.NewCol(FeatureIsDefaultCol, isDefault),
}
if featureEvent.TierName != nil {
cols = append(cols, handler.NewCol(FeatureTierNameCol, *featureEvent.TierName))
}
if featureEvent.TierDescription != nil {
cols = append(cols, handler.NewCol(FeatureTierDescriptionCol, *featureEvent.TierDescription))
}
if featureEvent.State != nil {
cols = append(cols, handler.NewCol(FeatureStateCol, *featureEvent.State))
}
if featureEvent.StateDescription != nil {
cols = append(cols, handler.NewCol(FeatureStateDescriptionCol, *featureEvent.StateDescription))
}
if featureEvent.AuditLogRetention != nil {
cols = append(cols, handler.NewCol(FeatureAuditLogRetentionCol, *featureEvent.AuditLogRetention))
}
if featureEvent.LoginPolicyFactors != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyFactorsCol, *featureEvent.LoginPolicyFactors))
}
if featureEvent.LoginPolicyIDP != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyIDPCol, *featureEvent.LoginPolicyIDP))
}
if featureEvent.LoginPolicyPasswordless != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyPasswordlessCol, *featureEvent.LoginPolicyPasswordless))
}
if featureEvent.LoginPolicyRegistration != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyRegistrationCol, *featureEvent.LoginPolicyRegistration))
}
if featureEvent.LoginPolicyUsernameLogin != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyUsernameLoginCol, *featureEvent.LoginPolicyUsernameLogin))
}
if featureEvent.LoginPolicyPasswordReset != nil {
cols = append(cols, handler.NewCol(FeatureLoginPolicyPasswordResetCol, *featureEvent.LoginPolicyPasswordReset))
}
if featureEvent.PasswordComplexityPolicy != nil {
cols = append(cols, handler.NewCol(FeaturePasswordComplexityPolicyCol, *featureEvent.PasswordComplexityPolicy))
}
if featureEvent.LabelPolicyPrivateLabel != nil || featureEvent.LabelPolicy != nil {
var value bool
if featureEvent.LabelPolicyPrivateLabel != nil {
value = *featureEvent.LabelPolicyPrivateLabel
} else {
value = *featureEvent.LabelPolicy
}
cols = append(cols, handler.NewCol(FeatureLabelPolicyPrivateLabelCol, value))
}
if featureEvent.LabelPolicyWatermark != nil {
cols = append(cols, handler.NewCol(FeatureLabelPolicyWatermarkCol, *featureEvent.LabelPolicyWatermark))
}
if featureEvent.CustomDomain != nil {
cols = append(cols, handler.NewCol(FeatureCustomDomainCol, *featureEvent.CustomDomain))
}
if featureEvent.PrivacyPolicy != nil {
cols = append(cols, handler.NewCol(FeaturePrivacyPolicyCol, *featureEvent.PrivacyPolicy))
}
if featureEvent.MetadataUser != nil {
cols = append(cols, handler.NewCol(FeatureMetadataUserCol, *featureEvent.MetadataUser))
}
if featureEvent.CustomTextMessage != nil {
cols = append(cols, handler.NewCol(FeatureCustomTextMessageCol, *featureEvent.CustomTextMessage))
}
if featureEvent.CustomTextLogin != nil {
cols = append(cols, handler.NewCol(FeatureCustomTextLoginCol, *featureEvent.CustomTextLogin))
}
if featureEvent.LockoutPolicy != nil {
cols = append(cols, handler.NewCol(FeatureLockoutPolicyCol, *featureEvent.LockoutPolicy))
}
if featureEvent.Actions != nil {
actionsAllowed := domain.ActionsNotAllowed
if *featureEvent.Actions {
actionsAllowed = domain.ActionsAllowedUnlimited
}
cols = append(cols, handler.NewCol(FeatureActionsAllowedCol, actionsAllowed))
}
if featureEvent.ActionsAllowed != nil {
cols = append(cols, handler.NewCol(FeatureActionsAllowedCol, *featureEvent.ActionsAllowed))
}
if featureEvent.MaxActions != nil {
cols = append(cols, handler.NewCol(FeatureMaxActionsCol, *featureEvent.MaxActions))
}
return crdb.NewUpsertStatement(
&featureEvent,
cols), nil
}
func (p *FeatureProjection) reduceFeatureRemoved(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*org.FeaturesRemovedEvent)
if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-0p4rf", "reduce.wrong.event.type %s", org.FeaturesRemovedEventType)
}
return crdb.NewDeleteStatement(
e,
[]handler.Condition{
handler.NewCond(FeatureAggregateIDCol, e.Aggregate().ID),
},
), nil
}

View File

@ -1,398 +0,0 @@
package projection
import (
"testing"
"time"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
)
func TestFeatureProjection_reduces(t *testing.T) {
type args struct {
event func(t *testing.T) eventstore.Event
}
tests := []struct {
name string
args args
reduce func(event eventstore.Event) (*handler.Statement, error)
want wantReduce
}{
{
name: "org.reduceFeatureSet new",
args: args{
event: getEvent(testEvent(
repository.EventType(org.FeaturesSetEventType),
org.AggregateType,
[]byte(`{
"tierName": "TierName",
"tierDescription": "TierDescription",
"state": 1,
"stateDescription": "StateDescription",
"auditLogRetention": 1,
"loginPolicyFactors": true,
"loginPolicyIDP": true,
"loginPolicyPasswordless": true,
"loginPolicyRegistration": true,
"loginPolicyUsernameLogin": true,
"loginPolicyPasswordReset": true,
"passwordComplexityPolicy": true,
"labelPolicyPrivateLabel": true,
"labelPolicyWatermark": true,
"customDomain": true,
"privacyPolicy": true,
"metadataUser": true,
"customTextMessage": true,
"customTextLogin": true,
"lockoutPolicy": true,
"actionsAllowed": 1,
"maxActions": 10
}`),
), org.FeaturesSetEventMapper),
},
reduce: (&FeatureProjection{}).reduceFeatureSet,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPSERT INTO projections.features (aggregate_id, instance_id, change_date, sequence, is_default, tier_name, tier_description, state, state_description, audit_log_retention, login_policy_factors, login_policy_idp, login_policy_passwordless, login_policy_registration, login_policy_username_login, login_policy_password_reset, password_complexity_policy, label_policy_private_label, label_policy_watermark, custom_domain, privacy_policy, metadata_user, custom_text_message, custom_text_login, lockout_policy, actions_allowed, max_actions) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
anyArg{},
uint64(15),
false,
"TierName",
"TierDescription",
domain.FeaturesStateActive,
"StateDescription",
time.Nanosecond,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsMaxAllowed,
10,
},
},
},
},
},
},
{
name: "org.reduceFeatureSet old",
args: args{
event: getEvent(testEvent(
repository.EventType(org.FeaturesSetEventType),
org.AggregateType,
[]byte(`{
"tierName": "TierName",
"tierDescription": "TierDescription",
"state": 1,
"stateDescription": "StateDescription",
"auditLogRetention": 1,
"loginPolicyFactors": true,
"loginPolicyIDP": true,
"loginPolicyPasswordless": true,
"loginPolicyRegistration": true,
"loginPolicyUsernameLogin": true,
"loginPolicyPasswordReset": true,
"passwordComplexityPolicy": true,
"labelPolicy": true,
"labelPolicyWatermark": true,
"customDomain": true,
"privacyPolicy": true,
"metadataUser": true,
"customTextMessage": true,
"customTextLogin": true,
"lockoutPolicy": true,
"actions": true
}`),
), org.FeaturesSetEventMapper),
},
reduce: (&FeatureProjection{}).reduceFeatureSet,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPSERT INTO projections.features (aggregate_id, instance_id, change_date, sequence, is_default, tier_name, tier_description, state, state_description, audit_log_retention, login_policy_factors, login_policy_idp, login_policy_passwordless, login_policy_registration, login_policy_username_login, login_policy_password_reset, password_complexity_policy, label_policy_private_label, label_policy_watermark, custom_domain, privacy_policy, metadata_user, custom_text_message, custom_text_login, lockout_policy, actions_allowed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
anyArg{},
uint64(15),
false,
"TierName",
"TierDescription",
domain.FeaturesStateActive,
"StateDescription",
time.Nanosecond,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsAllowedUnlimited,
},
},
},
},
},
},
{
name: "org.reduceFeatureSet required values only",
args: args{
event: getEvent(testEvent(
repository.EventType(org.FeaturesSetEventType),
org.AggregateType,
[]byte(`{}`),
), org.FeaturesSetEventMapper),
},
reduce: (&FeatureProjection{}).reduceFeatureSet,
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPSERT INTO projections.features (aggregate_id, instance_id, change_date, sequence, is_default) VALUES ($1, $2, $3, $4, $5)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
anyArg{},
uint64(15),
false,
},
},
},
},
},
},
{
name: "org.reduceFeatureRemoved",
reduce: (&FeatureProjection{}).reduceFeatureRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(org.FeaturesRemovedEventType),
org.AggregateType,
nil,
), org.FeaturesRemovedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("org"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.features WHERE (aggregate_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
},
},
},
},
},
{
name: "instance.reduceFeatureSet old",
reduce: (&FeatureProjection{}).reduceFeatureSet,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.FeaturesSetEventType),
instance.AggregateType,
[]byte(`{
"tierName": "TierName",
"tierDescription": "TierDescription",
"state": 1,
"stateDescription": "StateDescription",
"auditLogRetention": 1,
"loginPolicyFactors": true,
"loginPolicyIDP": true,
"loginPolicyPasswordless": true,
"loginPolicyRegistration": true,
"loginPolicyUsernameLogin": true,
"loginPolicyPasswordReset": true,
"passwordComplexityPolicy": true,
"labelPolicy": true,
"labelPolicyWatermark": true,
"customDomain": true,
"privacyPolicy": true,
"metadataUser": true,
"customTextMessage": true,
"customTextLogin": true,
"lockoutPolicy": true,
"actions": true
}`),
), instance.FeaturesSetEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPSERT INTO projections.features (aggregate_id, instance_id, change_date, sequence, is_default, tier_name, tier_description, state, state_description, audit_log_retention, login_policy_factors, login_policy_idp, login_policy_passwordless, login_policy_registration, login_policy_username_login, login_policy_password_reset, password_complexity_policy, label_policy_private_label, label_policy_watermark, custom_domain, privacy_policy, metadata_user, custom_text_message, custom_text_login, lockout_policy, actions_allowed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
anyArg{},
uint64(15),
true,
"TierName",
"TierDescription",
domain.FeaturesStateActive,
"StateDescription",
time.Nanosecond,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsAllowedUnlimited,
},
},
},
},
},
},
{
name: "instance.reduceFeatureSet new",
reduce: (&FeatureProjection{}).reduceFeatureSet,
args: args{
event: getEvent(testEvent(
repository.EventType(instance.FeaturesSetEventType),
instance.AggregateType,
[]byte(`{
"tierName": "TierName",
"tierDescription": "TierDescription",
"state": 1,
"stateDescription": "StateDescription",
"auditLogRetention": 1,
"loginPolicyFactors": true,
"loginPolicyIDP": true,
"loginPolicyPasswordless": true,
"loginPolicyRegistration": true,
"loginPolicyUsernameLogin": true,
"loginPolicyPasswordReset": true,
"passwordComplexityPolicy": true,
"labelPolicyPrivateLabel": true,
"labelPolicyWatermark": true,
"customDomain": true,
"privacyPolicy": true,
"metadataUser": true,
"customTextMessage": true,
"customTextLogin": true,
"lockoutPolicy": true,
"actionsAllowed": 1,
"maxActions": 10
}`),
), instance.FeaturesSetEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
previousSequence: 10,
projection: FeatureTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPSERT INTO projections.features (aggregate_id, instance_id, change_date, sequence, is_default, tier_name, tier_description, state, state_description, audit_log_retention, login_policy_factors, login_policy_idp, login_policy_passwordless, login_policy_registration, login_policy_username_login, login_policy_password_reset, password_complexity_policy, label_policy_private_label, label_policy_watermark, custom_domain, privacy_policy, metadata_user, custom_text_message, custom_text_login, lockout_policy, actions_allowed, max_actions) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
anyArg{},
uint64(15),
true,
"TierName",
"TierDescription",
domain.FeaturesStateActive,
"StateDescription",
time.Nanosecond,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
domain.ActionsMaxAllowed,
10,
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := baseEvent(t)
got, err := tt.reduce(event)
if !errors.IsErrorInvalidArgument(err) {
t.Errorf("no wrong event mapping: %v, got: %v", err, got)
}
event = tt.args.event(t)
got, err = tt.reduce(event)
assertReduce(t, got, err, tt.want)
})
}
}

View File

@ -55,7 +55,6 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
NewMailTemplateProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["mail_templates"]))
NewMessageTextProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["message_texts"]))
NewCustomTextProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["custom_texts"]))
NewFeatureProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["features"]))
NewUserProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["users"]))
NewLoginNameProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["login_names"]))
NewOrgMemberProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["org_members"]))

View File

@ -1,245 +0,0 @@
package features
import (
"encoding/json"
"time"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
)
const (
featuresPrefix = "features."
FeaturesSetEventType = featuresPrefix + "set"
FeaturesRemovedEventType = featuresPrefix + "removed"
)
type FeaturesSetEvent struct {
eventstore.BaseEvent `json:"-"`
TierName *string `json:"tierName,omitempty"`
TierDescription *string `json:"tierDescription,omitempty"`
State *domain.FeaturesState `json:"state,omitempty"`
StateDescription *string `json:"stateDescription,omitempty"`
AuditLogRetention *time.Duration `json:"auditLogRetention,omitempty"`
LoginPolicyFactors *bool `json:"loginPolicyFactors,omitempty"`
LoginPolicyIDP *bool `json:"loginPolicyIDP,omitempty"`
LoginPolicyPasswordless *bool `json:"loginPolicyPasswordless,omitempty"`
LoginPolicyRegistration *bool `json:"loginPolicyRegistration,omitempty"`
LoginPolicyUsernameLogin *bool `json:"loginPolicyUsernameLogin,omitempty"`
LoginPolicyPasswordReset *bool `json:"loginPolicyPasswordReset,omitempty"`
PasswordComplexityPolicy *bool `json:"passwordComplexityPolicy,omitempty"`
LabelPolicy *bool `json:"labelPolicy,omitempty"`
LabelPolicyPrivateLabel *bool `json:"labelPolicyPrivateLabel,omitempty"`
LabelPolicyWatermark *bool `json:"labelPolicyWatermark,omitempty"`
CustomDomain *bool `json:"customDomain,omitempty"`
PrivacyPolicy *bool `json:"privacyPolicy,omitempty"`
MetadataUser *bool `json:"metadataUser,omitempty"`
CustomTextMessage *bool `json:"customTextMessage,omitempty"`
CustomTextLogin *bool `json:"customTextLogin,omitempty"`
LockoutPolicy *bool `json:"lockoutPolicy,omitempty"`
Actions *bool `json:"actions,omitempty"`
ActionsAllowed *domain.ActionsAllowed `json:"actionsAllowed,omitempty"`
MaxActions *int `json:"maxActions,omitempty"`
}
func (e *FeaturesSetEvent) Data() interface{} {
return e
}
func (e *FeaturesSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewFeaturesSetEvent(
base *eventstore.BaseEvent,
changes []FeaturesChanges,
) (*FeaturesSetEvent, error) {
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "FEATURES-d34F4", "Errors.NoChangesFound")
}
changeEvent := &FeaturesSetEvent{
BaseEvent: *base,
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type FeaturesChanges func(*FeaturesSetEvent)
func ChangeTierName(tierName string) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.TierName = &tierName
}
}
func ChangeTierDescription(tierDescription string) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.TierDescription = &tierDescription
}
}
func ChangeState(State domain.FeaturesState) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.State = &State
}
}
func ChangeStateDescription(statusDescription string) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.StateDescription = &statusDescription
}
}
func ChangeAuditLogRetention(retention time.Duration) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.AuditLogRetention = &retention
}
}
func ChangeLoginPolicyFactors(loginPolicyFactors bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyFactors = &loginPolicyFactors
}
}
func ChangeLoginPolicyIDP(loginPolicyIDP bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyIDP = &loginPolicyIDP
}
}
func ChangeLoginPolicyPasswordless(loginPolicyPasswordless bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyPasswordless = &loginPolicyPasswordless
}
}
func ChangeLoginPolicyRegistration(loginPolicyRegistration bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyRegistration = &loginPolicyRegistration
}
}
func ChangeLoginPolicyUsernameLogin(loginPolicyUsernameLogin bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyUsernameLogin = &loginPolicyUsernameLogin
}
}
func ChangeLoginPolicyPasswordReset(loginPolicyPasswordReset bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LoginPolicyPasswordReset = &loginPolicyPasswordReset
}
}
func ChangePasswordComplexityPolicy(passwordComplexityPolicy bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.PasswordComplexityPolicy = &passwordComplexityPolicy
}
}
func ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LabelPolicyPrivateLabel = &labelPolicyPrivateLabel
}
}
func ChangeLabelPolicyWatermark(labelPolicyWatermark bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LabelPolicyWatermark = &labelPolicyWatermark
}
}
func ChangeCustomDomain(customDomain bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.CustomDomain = &customDomain
}
}
func ChangePrivacyPolicy(privacyPolicy bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.PrivacyPolicy = &privacyPolicy
}
}
func ChangeMetadataUser(metadataUser bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.MetadataUser = &metadataUser
}
}
func ChangeCustomTextMessage(customTextMessage bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.CustomTextMessage = &customTextMessage
}
}
func ChangeCustomTextLogin(customTextLogin bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.CustomTextLogin = &customTextLogin
}
}
func ChangeLockoutPolicy(lockoutPolicy bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.LockoutPolicy = &lockoutPolicy
}
}
func ChangeActionsAllowed(allowedType domain.ActionsAllowed) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.ActionsAllowed = &allowedType
}
}
func ChangeMaxActions(maxActions int) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.MaxActions = &maxActions
}
}
func FeaturesSetEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &FeaturesSetEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "FEATURES-fdgDg", "unable to unmarshal features")
}
return e, nil
}
type FeaturesRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *FeaturesRemovedEvent) Data() interface{} {
return nil
}
func (e *FeaturesRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func (e *FeaturesRemovedEvent) Assets() []*eventstore.Asset {
return nil
}
func NewFeaturesRemovedEvent(base *eventstore.BaseEvent) *FeaturesRemovedEvent {
return &FeaturesRemovedEvent{
BaseEvent: *base,
}
}
func FeaturesRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
return &FeaturesRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}

View File

@ -85,7 +85,6 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(CustomTextSetEventType, CustomTextSetEventMapper).
RegisterFilterEventMapper(CustomTextRemovedEventType, CustomTextRemovedEventMapper).
RegisterFilterEventMapper(CustomTextTemplateRemovedEventType, CustomTextTemplateRemovedEventMapper).
RegisterFilterEventMapper(FeaturesSetEventType, FeaturesSetEventMapper).
RegisterFilterEventMapper(InstanceDomainAddedEventType, DomainAddedEventMapper).
RegisterFilterEventMapper(InstanceDomainPrimarySetEventType, DomainPrimarySetEventMapper).
RegisterFilterEventMapper(InstanceDomainRemovedEventType, DomainRemovedEventMapper).

View File

@ -1,44 +0,0 @@
package instance
import (
"context"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/repository/features"
)
var (
FeaturesSetEventType = instanceEventTypePrefix + features.FeaturesSetEventType
)
type FeaturesSetEvent struct {
features.FeaturesSetEvent
}
func NewFeaturesSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []features.FeaturesChanges,
) (*FeaturesSetEvent, error) {
changedEvent, err := features.NewFeaturesSetEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
FeaturesSetEventType),
changes,
)
if err != nil {
return nil, err
}
return &FeaturesSetEvent{FeaturesSetEvent: *changedEvent}, nil
}
func FeaturesSetEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := features.FeaturesSetEventMapper(event)
if err != nil {
return nil, err
}
return &FeaturesSetEvent{FeaturesSetEvent: *e.(*features.FeaturesSetEvent)}, nil
}

View File

@ -77,8 +77,6 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(IDPOIDCConfigChangedEventType, IDPOIDCConfigChangedEventMapper).
RegisterFilterEventMapper(IDPJWTConfigAddedEventType, IDPJWTConfigAddedEventMapper).
RegisterFilterEventMapper(IDPJWTConfigChangedEventType, IDPJWTConfigChangedEventMapper).
RegisterFilterEventMapper(FeaturesSetEventType, FeaturesSetEventMapper).
RegisterFilterEventMapper(FeaturesRemovedEventType, FeaturesRemovedEventMapper).
RegisterFilterEventMapper(TriggerActionsSetEventType, TriggerActionsSetEventMapper).
RegisterFilterEventMapper(TriggerActionsCascadeRemovedEventType, TriggerActionsCascadeRemovedEventMapper).
RegisterFilterEventMapper(FlowClearedEventType, FlowClearedEventMapper)

View File

@ -1,72 +0,0 @@
package org
import (
"context"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/repository/features"
)
var (
FeaturesSetEventType = orgEventTypePrefix + features.FeaturesSetEventType
FeaturesRemovedEventType = orgEventTypePrefix + features.FeaturesRemovedEventType
)
type FeaturesSetEvent struct {
features.FeaturesSetEvent
}
func NewFeaturesSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []features.FeaturesChanges,
) (*FeaturesSetEvent, error) {
changedEvent, err := features.NewFeaturesSetEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
FeaturesSetEventType),
changes,
)
if err != nil {
return nil, err
}
return &FeaturesSetEvent{FeaturesSetEvent: *changedEvent}, nil
}
func FeaturesSetEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := features.FeaturesSetEventMapper(event)
if err != nil {
return nil, err
}
return &FeaturesSetEvent{FeaturesSetEvent: *e.(*features.FeaturesSetEvent)}, nil
}
type FeaturesRemovedEvent struct {
features.FeaturesRemovedEvent
}
func NewFeaturesRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *FeaturesRemovedEvent {
return &FeaturesRemovedEvent{
FeaturesRemovedEvent: *features.NewFeaturesRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
FeaturesRemovedEventType),
),
}
}
func FeaturesRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := features.FeaturesRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &FeaturesRemovedEvent{FeaturesRemovedEvent: *e.(*features.FeaturesRemovedEvent)}, nil
}

View File

@ -379,8 +379,6 @@ Errors:
AlreadyExists: Schritt gestartet existiert bereits
Done:
AlreadyExists: Schritt ausgeführt existiert bereits
Features:
NotChanged: Feature hat nicht geändert
CustomText:
AlreadyExists: Kundenspezifischer Text existiert bereits
Invalid: Kundenspezifischer Text ist ungültig
@ -613,9 +611,6 @@ EventTypes:
set: Primäre Domäne gesetzt
reserved: Domäne reserviert
released: Domäne freigegeben
features:
set: Feature hinzugefügt
removed: Feature entfernt
name:
reserved: Name der Organisation reserviert
released: Name der Organisation freigegeben

View File

@ -379,8 +379,6 @@ Errors:
AlreadyExists: Step started already exists
Done:
AlreadyExists: Step done already exists
Features:
NotChanged: Feature hat nicht geändert
CustomText:
AlreadyExists: Custom text already exists
Invalid: Custom text invalid
@ -613,9 +611,6 @@ EventTypes:
set: Primary domain set
reserved: Domain reserved
released: Domain released
features:
set: Feature added
removed: Feature removed
name:
reserved: Organization name reserved
released: Organization name released

View File

@ -377,8 +377,6 @@ Errors:
AlreadyExists: Il passo iniziato già esistente
Done:
AlreadyExists: Il passo fatto già esistente
Features:
NotChanged: La caratteristica non è cambiata
CustomText:
AlreadyExists: Il testo personalizzato già esistente
Invalid: Testo personalizzato non valido
@ -611,9 +609,6 @@ EventTypes:
set: Set di dominio primario
reserved: Dominio riservato
released: Dominio rilasciato
features:
set: Caratteristica aggiunta
removed: Caratteristica rimossa
name:
reserved: Nome dell'organizzazione riservato
released: Nome dell'organizzazione rilasciata

View File

@ -10,7 +10,6 @@ import "zitadel/policy.proto";
import "zitadel/settings.proto";
import "zitadel/text.proto";
import "zitadel/member.proto";
import "zitadel/features.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
@ -893,58 +892,6 @@ service AdminService {
};
}
rpc GetDefaultFeatures(GetDefaultFeaturesRequest) returns (GetDefaultFeaturesResponse) {
option(google.api.http) = {
get: "/features"
};
option (zitadel.v1.auth_option) = {
permission: "iam.features.read"
};
}
rpc SetDefaultFeatures(SetDefaultFeaturesRequest) returns (SetDefaultFeaturesResponse) {
option(google.api.http) = {
put: "/features"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.features.write"
};
}
rpc GetOrgFeatures(GetOrgFeaturesRequest) returns (GetOrgFeaturesResponse) {
option(google.api.http) = {
get: "/orgs/{org_id}/features"
};
option (zitadel.v1.auth_option) = {
permission: "iam.features.read"
};
}
rpc SetOrgFeatures(SetOrgFeaturesRequest) returns (SetOrgFeaturesResponse) {
option(google.api.http) = {
put: "/orgs/{org_id}/features"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.features.write"
};
}
rpc ResetOrgFeatures(ResetOrgFeaturesRequest) returns (ResetOrgFeaturesResponse) {
option(google.api.http) = {
delete: "/orgs/{org_id}/features"
};
option (zitadel.v1.auth_option) = {
permission: "iam.features.write"
};
}
//deprecated: please use DomainPolicy instead
//Returns the Org IAM policy defined by the administrators of ZITADEL
rpc GetOrgIAMPolicy(GetOrgIAMPolicyRequest) returns (GetOrgIAMPolicyResponse) {
@ -3380,92 +3327,6 @@ message UpdateIDPJWTConfigResponse {
zitadel.v1.ObjectDetails details = 1;
}
message GetDefaultFeaturesRequest {}
message GetDefaultFeaturesResponse {
zitadel.features.v1.Features features = 1;
}
message SetDefaultFeaturesRequest {
string tier_name = 1 [(validate.rules).string = {max_len: 200}];
string description = 2 [(validate.rules).string = {max_len: 200}];
google.protobuf.Duration audit_log_retention = 5 [(validate.rules).duration = {gte: {seconds: 0}}];
bool login_policy_username_login = 6;
bool login_policy_registration = 7;
bool login_policy_idp = 8;
bool login_policy_factors = 9;
bool login_policy_passwordless = 10;
bool password_complexity_policy = 11;
bool label_policy = 12;
bool custom_domain = 13;
bool login_policy_password_reset = 14;
bool label_policy_private_label = 15;
bool label_policy_watermark = 16;
bool custom_text = 17;
bool privacy_policy = 18;
bool metadata_user = 19;
bool custom_text_message = 20;
bool custom_text_login = 21;
bool lockout_policy = 22;
bool actions = 23;
zitadel.features.v1.ActionsAllowed actions_allowed = 24;
int32 max_actions = 25;
}
message SetDefaultFeaturesResponse {
zitadel.v1.ObjectDetails details = 1;
}
message GetOrgFeaturesRequest {
string org_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message GetOrgFeaturesResponse {
zitadel.features.v1.Features features = 1;
}
message SetOrgFeaturesRequest {
string org_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
string tier_name = 2 [(validate.rules).string = {max_len: 200}];
string description = 3 [(validate.rules).string = {max_len: 200}];
zitadel.features.v1.FeaturesState state = 4;
string state_description = 5 [(validate.rules).string = {max_len: 200}];
google.protobuf.Duration audit_log_retention = 6 [(validate.rules).duration = {gte: {seconds: 0}}];
bool login_policy_username_login = 7;
bool login_policy_registration = 8;
bool login_policy_idp = 9;
bool login_policy_factors = 10;
bool login_policy_passwordless = 11;
bool password_complexity_policy = 12;
bool label_policy = 13;
bool custom_domain = 14;
bool login_policy_password_reset = 15;
bool label_policy_private_label = 16;
bool label_policy_watermark = 17;
bool custom_text = 18;
bool privacy_policy = 19;
bool metadata_user = 20;
bool custom_text_message = 21;
bool custom_text_login = 22;
bool lockout_policy = 23;
bool actions = 24;
zitadel.features.v1.ActionsAllowed actions_allowed = 25;
int32 max_actions = 26;
}
message SetOrgFeaturesResponse {
zitadel.v1.ObjectDetails details = 1;
}
message ResetOrgFeaturesRequest {
string org_id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message ResetOrgFeaturesResponse {
zitadel.v1.ObjectDetails details = 1;
}
message GetOrgIAMPolicyRequest {}
message GetOrgIAMPolicyResponse {

View File

@ -551,17 +551,6 @@ service AuthService {
};
}
// Returns a list of features, which are allowed on these organisation based on the subscription of the organisation
rpc ListMyZitadelFeatures(ListMyZitadelFeaturesRequest) returns (ListMyZitadelFeaturesResponse) {
option (google.api.http) = {
post: "/features/zitadel/me/_search"
};
option (zitadel.v1.auth_option) = {
permission: "authenticated"
};
}
// Returns the permissions the authorized user has in ZITADEL based on his manager roles (e.g ORG_OWNER)
rpc ListMyZitadelPermissions(ListMyZitadelPermissionsRequest) returns (ListMyZitadelPermissionsResponse) {
option (google.api.http) = {
@ -1003,13 +992,6 @@ message ListMyProjectOrgsResponse {
repeated zitadel.org.v1.Org result = 2;
}
//This is an empty request
message ListMyZitadelFeaturesRequest {}
message ListMyZitadelFeaturesResponse {
repeated string result = 1;
}
//This is an empty request
message ListMyZitadelPermissionsRequest {}

View File

@ -1,57 +0,0 @@
syntax = "proto3";
import "zitadel/object.proto";
import "google/protobuf/duration.proto";
package zitadel.features.v1;
option go_package = "github.com/zitadel/zitadel/pkg/grpc/features";
message Features {
zitadel.v1.ObjectDetails details = 1;
FeatureTier tier = 2;
bool is_default = 3;
google.protobuf.Duration audit_log_retention = 4;
bool login_policy_username_login = 5;
bool login_policy_registration = 6;
bool login_policy_idp = 7;
bool login_policy_factors = 8;
bool login_policy_passwordless = 9;
bool password_complexity_policy = 10;
bool label_policy = 11;
bool custom_domain = 12;
bool login_policy_password_reset = 13;
bool label_policy_private_label = 14;
bool label_policy_watermark = 15;
bool custom_text = 16;
bool privacy_policy = 17;
bool metadata_user = 18;
bool custom_text_message = 19;
bool custom_text_login = 20;
bool lockout_policy = 21;
bool actions = 22;
ActionsAllowed actions_allowed = 23;
int32 max_actions = 24;
}
message FeatureTier {
string name = 1;
string description = 2;
FeaturesState state = 3;
string status_info = 4;
}
enum FeaturesState {
FEATURES_STATE_ACTIVE = 0;
FEATURES_STATE_ACTION_REQUIRED = 1;
FEATURES_STATE_CANCELED = 2;
FEATURES_STATE_GRANDFATHERED = 3;
}
enum ActionsAllowed {
ACTIONS_ALLOWED_NOT_ALLOWED = 0;
ACTIONS_ALLOWED_MAX = 1;
ACTIONS_ALLOWED_UNLIMITED = 2;
}

View File

@ -13,7 +13,6 @@ import "zitadel/text.proto";
import "zitadel/message.proto";
import "zitadel/change.proto";
import "zitadel/auth_n_key.proto";
import "zitadel/features.proto";
import "zitadel/metadata.proto";
import "zitadel/action.proto";
@ -307,7 +306,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.write"
feature: "metadata.user"
};
}
@ -320,7 +318,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.write"
feature: "metadata.user"
};
}
@ -333,7 +330,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.read"
feature: "metadata.user"
};
}
@ -345,7 +341,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.read"
feature: "metadata.user"
};
}
@ -357,7 +352,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.write"
feature: "metadata.user"
};
}
@ -370,7 +364,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "user.write"
feature: "metadata.user"
};
}
@ -871,7 +864,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.write"
feature: "custom_domain"
};
}
@ -895,7 +887,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.write"
feature: "custom_domain"
};
}
@ -909,7 +900,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.write"
feature: "custom_domain"
};
}
@ -922,7 +912,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.write"
feature: "custom_domain"
};
}
@ -1740,16 +1729,6 @@ service ManagementService {
};
}
rpc GetFeatures(GetFeaturesRequest) returns (GetFeaturesResponse) {
option (google.api.http) = {
get: "/features"
};
option (zitadel.v1.auth_option) = {
permission: "features.read"
};
}
//deprecated: please use DomainPolicy instead
// Returns the domain policy (this policy is managed by the iam administrator)
rpc GetOrgIAMPolicy(GetOrgIAMPolicyRequest) returns (GetOrgIAMPolicyResponse) {
@ -1806,7 +1785,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy"
};
}
@ -1820,7 +1798,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy"
};
}
@ -1858,7 +1835,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.idp"
};
}
@ -1870,7 +1846,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.idp"
};
}
@ -1894,7 +1869,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.factors"
};
}
@ -1906,7 +1880,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.factors"
};
}
@ -1930,7 +1903,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.factors"
};
}
@ -1942,7 +1914,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "login_policy.factors"
};
}
@ -1980,7 +1951,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "password_complexity_policy"
};
}
@ -1994,7 +1964,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "password_complexity_policy"
};
}
@ -2154,7 +2123,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "privacy_policy"
};
}
@ -2169,7 +2137,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "privacy_policy"
};
}
@ -2218,7 +2185,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.read"
feature: "label_policy"
};
}
@ -2232,7 +2198,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2246,7 +2211,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2260,7 +2224,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2272,7 +2235,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2284,7 +2246,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2296,7 +2257,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2308,7 +2268,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2320,7 +2279,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write"
feature: "label_policy"
};
}
@ -2369,7 +2327,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2417,7 +2374,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2466,7 +2422,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2515,7 +2470,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2564,7 +2518,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2613,7 +2566,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.message"
};
}
@ -2661,7 +2613,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "policy.write";
feature: "custom_text.login"
};
}
@ -2711,7 +2662,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2724,7 +2674,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2739,7 +2688,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2753,7 +2701,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2766,7 +2713,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2779,7 +2725,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2792,7 +2737,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2805,7 +2749,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.idp.write"
feature: "login_policy.idp"
};
}
@ -2817,7 +2760,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.read"
feature: "actions"
};
}
@ -2828,7 +2770,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.read"
feature: "actions"
};
}
@ -2840,7 +2781,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.write"
feature: "actions"
};
}
@ -2852,7 +2792,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.write"
feature: "actions"
};
}
@ -2864,7 +2803,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.write"
feature: "actions"
};
}
@ -2876,7 +2814,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.write"
feature: "actions"
};
}
@ -2888,7 +2825,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.action.delete"
feature: "actions"
};
}
@ -2899,7 +2835,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.flow.read"
feature: "actions"
};
}
@ -2910,7 +2845,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.flow.delete"
feature: "actions"
};
}
@ -2922,7 +2856,6 @@ service ManagementService {
option (zitadel.v1.auth_option) = {
permission: "org.flow.write"
feature: "actions"
};
}
}
@ -4378,12 +4311,6 @@ message BulkRemoveUserGrantRequest {
message BulkRemoveUserGrantResponse {}
message GetFeaturesRequest {}
message GetFeaturesResponse {
zitadel.features.v1.Features features = 1;
}
message GetOrgIAMPolicyRequest {}
message GetOrgIAMPolicyResponse {

View File

@ -14,5 +14,4 @@ extend google.protobuf.MethodOptions {
message AuthOption {
string permission = 1;
string check_field_name = 2;
string feature = 3;
}

View File

@ -3,7 +3,6 @@ syntax = "proto3";
import "zitadel/object.proto";
import "validate/validate.proto";
import "google/protobuf/duration.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
package zitadel.settings.v1;

View File

@ -3,14 +3,10 @@ syntax = "proto3";
import "zitadel/object.proto";
import "zitadel/options.proto";
import "zitadel/instance.proto";
import "zitadel/text.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
package zitadel.system.v1;