mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 20:07:23 +00:00
feat: permit all features to every instance and organisation (#3566)
This commit is contained in:
parent
a9f71ba08e
commit
861cf07700
@ -50,7 +50,6 @@ func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
mig.zitadelRoles,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
mig.externalDomain,
|
||||
mig.externalSecure,
|
||||
mig.externalPort,
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -112,7 +112,6 @@
|
||||
"NEWVALUE": "Neuer Wert",
|
||||
"RESTORE": "Wiederherstellen",
|
||||
"CONTINUEWITHOUTSAVE": "Ohne speichern fortfahren",
|
||||
"GOTOFEATURES": "Zu den Features",
|
||||
"OF": "von",
|
||||
"PREVIOUS": "Zurück",
|
||||
"NEXT": "Weiter",
|
||||
|
@ -112,7 +112,6 @@
|
||||
"NEWVALUE": "New Value",
|
||||
"RESTORE": "Restore",
|
||||
"CONTINUEWITHOUTSAVE": "Continue without saving",
|
||||
"GOTOFEATURES": "Go to features",
|
||||
"OF": "of",
|
||||
"PREVIOUS": "Previous",
|
||||
"NEXT": "Next",
|
||||
|
@ -112,7 +112,6 @@
|
||||
"NEWVALUE": "Nuovo valore",
|
||||
"RESTORE": "Ripristina",
|
||||
"CONTINUEWITHOUTSAVE": "Continua senza salvare",
|
||||
"GOTOFEATURES": "Vai alle caratteristiche",
|
||||
"OF": "di",
|
||||
"PREVIOUS": "Precedente",
|
||||
"NEXT": "Avanti",
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 | - |
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -17,7 +17,6 @@ title: zitadel/options.proto
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| permission | string | - | |
|
||||
| check_field_name | string | - | |
|
||||
| feature | string | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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}}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) }()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
@ -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),
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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{
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
}
|
@ -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
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
@ -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}}
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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"]))
|
||||
|
@ -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
|
||||
}
|
@ -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).
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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 {
|
||||
|
@ -14,5 +14,4 @@ extend google.protobuf.MethodOptions {
|
||||
message AuthOption {
|
||||
string permission = 1;
|
||||
string check_field_name = 2;
|
||||
string feature = 3;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user