Merge branch 'main' into next

# Conflicts:
#	cmd/start/start.go
#	internal/auth/repository/eventsourcing/eventstore/auth_request.go
#	internal/auth/repository/eventsourcing/repository.go
#	internal/command/main_test.go
#	internal/command/quota.go
#	internal/command/quota_model.go
#	internal/command/quota_model_test.go
This commit is contained in:
Livio Spring 2023-10-09 14:18:29 +02:00
commit dee9d8d3a7
No known key found for this signature in database
GPG Key ID: 26BB1C2FA5952CF0
451 changed files with 10373 additions and 2165 deletions

View File

@ -11,31 +11,30 @@ permissions:
pull-requests: write pull-requests: write
jobs: jobs:
core: core:
uses: ./.github/workflows/core.yml uses: ./.github/workflows/core.yml
with: with:
node_version: '18' node_version: "20"
buf_version: 'latest' buf_version: "latest"
go_version: '1.21' go_version: "1.21"
console: console:
uses: ./.github/workflows/console.yml uses: ./.github/workflows/console.yml
with: with:
node_version: '18' node_version: "20"
buf_version: 'latest' buf_version: "latest"
version: version:
uses: ./.github/workflows/version.yml uses: ./.github/workflows/version.yml
with: with:
semantic_version: '19.0.2' semantic_version: "19.0.2"
dry_run: true dry_run: true
compile: compile:
needs: [core, console, version] needs: [core, console, version]
uses: ./.github/workflows/compile.yml uses: ./.github/workflows/compile.yml
with: with:
go_version: '1.21' go_version: "1.21"
core_cache_key: ${{ needs.core.outputs.cache_key }} core_cache_key: ${{ needs.core.outputs.cache_key }}
console_cache_key: ${{ needs.console.outputs.cache_key }} console_cache_key: ${{ needs.console.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }} core_cache_path: ${{ needs.core.outputs.cache_path }}
@ -46,7 +45,7 @@ jobs:
needs: core needs: core
uses: ./.github/workflows/core-unit-test.yml uses: ./.github/workflows/core-unit-test.yml
with: with:
go_version: '1.21' go_version: "1.21"
core_cache_key: ${{ needs.core.outputs.cache_key }} core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }} core_cache_path: ${{ needs.core.outputs.cache_path }}
@ -54,7 +53,7 @@ jobs:
needs: core needs: core
uses: ./.github/workflows/core-integration-test.yml uses: ./.github/workflows/core-integration-test.yml
with: with:
go_version: '1.21' go_version: "1.21"
core_cache_key: ${{ needs.core.outputs.cache_key }} core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }} core_cache_path: ${{ needs.core.outputs.cache_path }}
@ -62,10 +61,10 @@ jobs:
needs: [core, console] needs: [core, console]
uses: ./.github/workflows/lint.yml uses: ./.github/workflows/lint.yml
with: with:
go_version: '1.21' go_version: "1.21"
node_version: '18' node_version: "18"
buf_version: 'latest' buf_version: "latest"
go_lint_version: 'v1.53.2' go_lint_version: "v1.53.2"
core_cache_key: ${{ needs.core.outputs.cache_key }} core_cache_key: ${{ needs.core.outputs.cache_key }}
core_cache_path: ${{ needs.core.outputs.cache_path }} core_cache_path: ${{ needs.core.outputs.cache_path }}
@ -77,7 +76,7 @@ jobs:
packages: write packages: write
if: ${{ github.event_name == 'workflow_dispatch' }} if: ${{ github.event_name == 'workflow_dispatch' }}
with: with:
build_image_name: 'ghcr.io/zitadel/zitadel-build' build_image_name: "ghcr.io/zitadel/zitadel-build"
e2e: e2e:
uses: ./.github/workflows/e2e.yml uses: ./.github/workflows/e2e.yml
@ -90,12 +89,13 @@ jobs:
contents: write contents: write
issues: write issues: write
pull-requests: write pull-requests: write
needs: [version, core-unit-test, core-integration-test, lint, container, e2e] needs:
[version, core-unit-test, core-integration-test, lint, container, e2e]
if: ${{ needs.version.outputs.published == 'true' && github.event_name == 'workflow_dispatch' }} if: ${{ needs.version.outputs.published == 'true' && github.event_name == 'workflow_dispatch' }}
secrets: secrets:
GCR_JSON_KEY_BASE64: ${{ secrets.GCR_JSON_KEY_BASE64 }} GCR_JSON_KEY_BASE64: ${{ secrets.GCR_JSON_KEY_BASE64 }}
with: with:
build_image_name: ${{ needs.container.outputs.build_image }} build_image_name: ${{ needs.container.outputs.build_image }}
semantic_version: '19.0.2' semantic_version: "19.0.2"
image_name: 'ghcr.io/zitadel/zitadel' image_name: "ghcr.io/zitadel/zitadel"
google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel" google_image_name: "europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel"

View File

@ -595,7 +595,7 @@ DefaultInstance:
MaxAgeDays: 0 # ZITADEL_DEFAULTINSTANCE_PASSWORDAGEPOLICY_MAXAGEDAYS MaxAgeDays: 0 # ZITADEL_DEFAULTINSTANCE_PASSWORDAGEPOLICY_MAXAGEDAYS
DomainPolicy: DomainPolicy:
UserLoginMustBeDomain: false # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_USERLOGINMUSTBEDOMAIN UserLoginMustBeDomain: false # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_USERLOGINMUSTBEDOMAIN
ValidateOrgDomains: true # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_VALIDATEORGDOMAINS ValidateOrgDomains: false # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_VALIDATEORGDOMAINS
SMTPSenderAddressMatchesInstanceDomain: false # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN SMTPSenderAddressMatchesInstanceDomain: false # ZITADEL_DEFAULTINSTANCE_DOMAINPOLICY_SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN
LoginPolicy: LoginPolicy:
AllowUsernamePassword: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWUSERNAMEPASSWORD AllowUsernamePassword: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWUSERNAMEPASSWORD
@ -604,7 +604,7 @@ DefaultInstance:
ForceMFA: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_FORCEMFA ForceMFA: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_FORCEMFA
HidePasswordReset: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_HIDEPASSWORDRESET HidePasswordReset: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_HIDEPASSWORDRESET
IgnoreUnknownUsernames: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_IGNOREUNKNOWNUSERNAMES IgnoreUnknownUsernames: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_IGNOREUNKNOWNUSERNAMES
AllowDomainDiscovery: false # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWDOMAINDISCOVERY AllowDomainDiscovery: true # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_ALLOWDOMAINDISCOVERY
# 1 is allowed, 0 is not allowed # 1 is allowed, 0 is not allowed
PasswordlessType: 1 # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_PASSWORDLESSTYPE PasswordlessType: 1 # ZITADEL_DEFAULTINSTANCE_LOGINPOLICY_PASSWORDLESSTYPE
# DefaultRedirectURL is empty by default because we use the Console UI # DefaultRedirectURL is empty by default because we use the Console UI
@ -761,6 +761,8 @@ DefaultInstance:
Greeting: Hello {{.DisplayName}}, Greeting: Hello {{.DisplayName}},
Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password. Text: The password of your user has changed. If this change was not done by you, please be advised to immediately reset your password.
ButtonText: Login ButtonText: Login
Features:
- FeatureLoginDefaultOrg: true
Quotas: Quotas:
# Items take a slice of quota configurations, whereas, for each unit type and instance, one or zero quotas may exist. # Items take a slice of quota configurations, whereas, for each unit type and instance, one or zero quotas may exist.
@ -819,6 +821,7 @@ InternalAuthZ:
- "iam.flow.read" - "iam.flow.read"
- "iam.flow.write" - "iam.flow.write"
- "iam.flow.delete" - "iam.flow.delete"
- "iam.feature.write"
- "org.read" - "org.read"
- "org.global.read" - "org.global.read"
- "org.create" - "org.create"

View File

@ -23,6 +23,7 @@ func MustNewConfig(v *viper.Viper) *Config {
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToTimeHookFunc(time.RFC3339), mapstructure.StringToTimeHookFunc(time.RFC3339),
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
hook.StringToFeatureHookFunc(),
)), )),
) )
logging.OnError(err).Fatal("unable to read default config") logging.OnError(err).Fatal("unable to read default config")

View File

@ -24,6 +24,7 @@ type FirstInstance struct {
Org command.InstanceOrgSetup Org command.InstanceOrgSetup
MachineKeyPath string MachineKeyPath string
PatPath string PatPath string
Features map[domain.Feature]any
instanceSetup command.InstanceSetup instanceSetup command.InstanceSetup
userEncryptionKey *crypto.KeyConfig userEncryptionKey *crypto.KeyConfig

View File

@ -43,6 +43,7 @@ func MustNewConfig(v *viper.Viper) *Config {
mapstructure.StringToTimeHookFunc(time.RFC3339), mapstructure.StringToTimeHookFunc(time.RFC3339),
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
database.DecodeHook, database.DecodeHook,
hook.StringToFeatureHookFunc(),
)), )),
) )
logging.OnError(err).Fatal("unable to read default config") logging.OnError(err).Fatal("unable to read default config")
@ -99,6 +100,7 @@ func MustNewSteps(v *viper.Viper) *Steps {
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToTimeHookFunc(time.RFC3339), mapstructure.StringToTimeHookFunc(time.RFC3339),
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
hook.StringToFeatureHookFunc(),
)), )),
) )
logging.OnError(err).Fatal("unable to read steps") logging.OnError(err).Fatal("unable to read steps")

View File

@ -92,6 +92,7 @@ func MustNewConfig(v *viper.Viper) *Config {
database.DecodeHook, database.DecodeHook,
actions.HTTPConfigDecodeHook, actions.HTTPConfigDecodeHook,
systemAPIUsersDecodeHook, systemAPIUsersDecodeHook,
hook.StringToFeatureHookFunc(),
)), )),
) )
logging.OnError(err).Fatal("unable to read config") logging.OnError(err).Fatal("unable to read config")

View File

@ -27,6 +27,7 @@ import (
"github.com/zitadel/zitadel/cmd/build" "github.com/zitadel/zitadel/cmd/build"
"github.com/zitadel/zitadel/cmd/key" "github.com/zitadel/zitadel/cmd/key"
cmd_tls "github.com/zitadel/zitadel/cmd/tls" cmd_tls "github.com/zitadel/zitadel/cmd/tls"
"github.com/zitadel/zitadel/feature"
"github.com/zitadel/zitadel/internal/actions" "github.com/zitadel/zitadel/internal/actions"
admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing" admin_es "github.com/zitadel/zitadel/internal/admin/repository/eventsourcing"
"github.com/zitadel/zitadel/internal/api" "github.com/zitadel/zitadel/internal/api"
@ -40,7 +41,7 @@ import (
"github.com/zitadel/zitadel/internal/api/grpc/session/v2" "github.com/zitadel/zitadel/internal/api/grpc/session/v2"
"github.com/zitadel/zitadel/internal/api/grpc/settings/v2" "github.com/zitadel/zitadel/internal/api/grpc/settings/v2"
"github.com/zitadel/zitadel/internal/api/grpc/system" "github.com/zitadel/zitadel/internal/api/grpc/system"
"github.com/zitadel/zitadel/internal/api/grpc/user/v2" user_v2 "github.com/zitadel/zitadel/internal/api/grpc/user/v2"
http_util "github.com/zitadel/zitadel/internal/api/http" http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/api/http/middleware"
"github.com/zitadel/zitadel/internal/api/idp" "github.com/zitadel/zitadel/internal/api/idp"
@ -356,7 +357,7 @@ func startAPIs(
if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil { if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, keys.User, config.ExternalSecure, config.AuditLogRetention)); err != nil {
return err return err
} }
if err := apis.RegisterService(ctx, user.CreateServer(commands, queries, keys.User, keys.IDPConfig, idp.CallbackURL(config.ExternalSecure))); err != nil { if err := apis.RegisterService(ctx, user_v2.CreateServer(commands, queries, keys.User, keys.IDPConfig, idp.CallbackURL(config.ExternalSecure), idp.SAMLRootURL(config.ExternalSecure))); err != nil {
return err return err
} }
if err := apis.RegisterService(ctx, session.CreateServer(commands, queries, permissionCheck)); err != nil { if err := apis.RegisterService(ctx, session.CreateServer(commands, queries, permissionCheck)); err != nil {
@ -375,7 +376,7 @@ func startAPIs(
apis.RegisterHandlerOnPrefix(idp.HandlerPrefix, idp.NewHandler(commands, queries, keys.IDPConfig, config.ExternalSecure, instanceInterceptor.Handler)) apis.RegisterHandlerOnPrefix(idp.HandlerPrefix, idp.NewHandler(commands, queries, keys.IDPConfig, config.ExternalSecure, instanceInterceptor.Handler))
userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, id.SonyFlakeGenerator(), config.ExternalSecure, login.EndpointResources, login.EndpointExternalLoginCallbackFormPost) userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, id.SonyFlakeGenerator(), config.ExternalSecure, login.EndpointResources, login.EndpointExternalLoginCallbackFormPost, login.EndpointSAMLACS)
if err != nil { if err != nil {
return err return err
} }
@ -412,7 +413,27 @@ func startAPIs(
} }
apis.RegisterHandlerOnPrefix(console.HandlerPrefix, c) apis.RegisterHandlerOnPrefix(console.HandlerPrefix, c)
l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, console.HandlerPrefix+"/", op.AuthCallbackURL(oidcProvider), provider.AuthCallbackURL(samlProvider), config.ExternalSecure, userAgentInterceptor, op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler, provider.NewIssuerInterceptor(samlProvider.IssuerFromRequest).Handler, instanceInterceptor.Handler, assetsCache.Handler, limitingAccessInterceptor.WithoutLimiting().Handle, keys.User, keys.IDPConfig, keys.CSRFCookieKey) l, err := login.CreateLogin(
config.Login,
commands,
queries,
authRepo,
store,
console.HandlerPrefix+"/",
op.AuthCallbackURL(oidcProvider),
provider.AuthCallbackURL(samlProvider),
config.ExternalSecure,
userAgentInterceptor,
op.NewIssuerInterceptor(oidcProvider.IssuerFromRequest).Handler,
provider.NewIssuerInterceptor(samlProvider.IssuerFromRequest).Handler,
instanceInterceptor.Handler,
assetsCache.Handler,
limitingAccessInterceptor.WithoutLimiting().Handle,
keys.User,
keys.IDPConfig,
keys.CSRFCookieKey,
feature.NewCheck(eventstore),
)
if err != nil { if err != nil {
return fmt.Errorf("unable to start login: %w", err) return fmt.Errorf("unable to start login: %w", err)
} }

View File

@ -25,7 +25,7 @@
"@angular/router": "^16.2.0", "@angular/router": "^16.2.0",
"@angular/service-worker": "^16.2.0", "@angular/service-worker": "^16.2.0",
"@ctrl/ngx-codemirror": "^6.1.0", "@ctrl/ngx-codemirror": "^6.1.0",
"@grpc/grpc-js": "^1.8.14", "@grpc/grpc-js": "^1.9.3",
"@ngx-translate/core": "^14.0.0", "@ngx-translate/core": "^14.0.0",
"angular-oauth2-oidc": "^15.0.1", "angular-oauth2-oidc": "^15.0.1",
"angularx-qrcode": "^16.0.0", "angularx-qrcode": "^16.0.0",
@ -41,8 +41,8 @@
"libphonenumber-js": "^1.10.30", "libphonenumber-js": "^1.10.30",
"material-design-icons-iconfont": "^6.1.1", "material-design-icons-iconfont": "^6.1.1",
"moment": "^2.29.4", "moment": "^2.29.4",
"opentype.js": "^1.3.4",
"ngx-color": "^9.0.0", "ngx-color": "^9.0.0",
"opentype.js": "^1.3.4",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tinycolor2": "^1.6.0", "tinycolor2": "^1.6.0",
"tslib": "^2.4.1", "tslib": "^2.4.1",
@ -62,17 +62,17 @@
"@bufbuild/buf": "^1.23.1", "@bufbuild/buf": "^1.23.1",
"@types/file-saver": "^2.0.2", "@types/file-saver": "^2.0.2",
"@types/google-protobuf": "^3.15.3", "@types/google-protobuf": "^3.15.3",
"@types/jasmine": "~4.3.3", "@types/jasmine": "~4.3.6",
"@types/jasminewd2": "~2.0.10", "@types/jasminewd2": "~2.0.10",
"@types/jsonwebtoken": "^9.0.1", "@types/jsonwebtoken": "^9.0.1",
"@types/node": "^18.15.11", "@types/node": "^20.7.0",
"@types/opentype.js": "^1.3.4", "@types/opentype.js": "^1.3.4",
"@types/qrcode": "^1.5.0", "@types/qrcode": "^1.5.2",
"@types/uuid": "^9.0.2", "@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.59.11", "@typescript-eslint/eslint-plugin": "^5.59.11",
"@typescript-eslint/parser": "^5.60.1", "@typescript-eslint/parser": "^5.60.1",
"codelyzer": "^6.0.2", "codelyzer": "^6.0.2",
"eslint": "^8.44.0", "eslint": "^8.50.0",
"jasmine-core": "~4.6.0", "jasmine-core": "~4.6.0",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "^6.4.2", "karma": "^6.4.2",
@ -80,7 +80,7 @@
"karma-coverage-istanbul-reporter": "^3.0.3", "karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "^5.1.0", "karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.1.0", "karma-jasmine-html-reporter": "^2.1.0",
"prettier": "^2.8.7", "prettier": "^3.0.3",
"prettier-plugin-organize-imports": "^3.2.2", "prettier-plugin-organize-imports": "^3.2.2",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"typescript": "^4.9.5" "typescript": "^4.9.5"

View File

@ -158,14 +158,6 @@ const routes: Routes = [
requiresAll: true, requiresAll: true,
}, },
}, },
{
path: 'domains',
loadChildren: () => import('./pages/domains/domains.module'),
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['org.read'],
},
},
{ {
path: 'org-settings', path: 'org-settings',
loadChildren: () => import('./pages/org-settings/org-settings.module'), loadChildren: () => import('./pages/org-settings/org-settings.module'),

View File

@ -9,7 +9,10 @@ import { GrpcAuthService } from '../services/grpc-auth.service';
providedIn: 'root', providedIn: 'root',
}) })
export class UserGuard { export class UserGuard {
constructor(private authService: GrpcAuthService, private router: Router) {} constructor(
private authService: GrpcAuthService,
private router: Router,
) {}
public canActivate( public canActivate(
route: ActivatedRouteSnapshot, route: ActivatedRouteSnapshot,

View File

@ -18,7 +18,11 @@ export class AccountsCardComponent implements OnInit {
public sessions: Session.AsObject[] = []; public sessions: Session.AsObject[] = [];
public loadingUsers: boolean = false; public loadingUsers: boolean = false;
public UserState: any = UserState; public UserState: any = UserState;
constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) { constructor(
public authService: AuthenticationService,
private router: Router,
private userService: GrpcAuthService,
) {
this.userService this.userService
.listMyUserSessions() .listMyUserSessions()
.then((sessions) => { .then((sessions) => {

View File

@ -22,7 +22,10 @@ export class AddKeyDialogComponent {
public type!: KeyType; public type!: KeyType;
public dateControl: UntypedFormControl = new UntypedFormControl('', []); public dateControl: UntypedFormControl = new UntypedFormControl('', []);
constructor(public dialogRef: MatDialogRef<AddKeyDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<AddKeyDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.types = [KeyType.KEY_TYPE_JSON]; this.types = [KeyType.KEY_TYPE_JSON];
this.type = KeyType.KEY_TYPE_JSON; this.type = KeyType.KEY_TYPE_JSON;
const today = new Date(); const today = new Date();

View File

@ -14,7 +14,10 @@ export class AddMemberRolesDialogComponent {
public allRoles: string[] = []; public allRoles: string[] = [];
public selectedRoles: string[] = []; public selectedRoles: string[] = [];
constructor(public dialogRef: MatDialogRef<AddMemberRolesDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<AddMemberRolesDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.allRoles = Object.assign([], data.allRoles); this.allRoles = Object.assign([], data.allRoles);
this.selectedRoles = Object.assign([], data.selectedRoles); this.selectedRoles = Object.assign([], data.selectedRoles);
} }

View File

@ -14,7 +14,10 @@ export class AddTokenDialogComponent {
public startDate: Date = new Date(); public startDate: Date = new Date();
public dateControl: UntypedFormControl = new UntypedFormControl('', []); public dateControl: UntypedFormControl = new UntypedFormControl('', []);
constructor(public dialogRef: MatDialogRef<AddTokenDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<AddTokenDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
const today = new Date(); const today = new Date();
this.startDate.setDate(today.getDate() + 1); this.startDate.setDate(today.getDate() + 1);
} }

View File

@ -17,7 +17,11 @@
outline: none; outline: none;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-family: 'Lato', -apple-system, BlinkMacSystemFont, sans-serif; font-family:
'Lato',
-apple-system,
BlinkMacSystemFont,
sans-serif;
// box-shadow: 0 0 3px #0000001a; // box-shadow: 0 0 3px #0000001a;
img { img {

View File

@ -70,7 +70,10 @@ export class ChangesComponent implements OnInit, OnDestroy {
); );
public changes!: ListChanges; public changes!: ListChanges;
private destroyed$: Subject<void> = new Subject(); private destroyed$: Subject<void> = new Subject();
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) {} constructor(
private mgmtUserService: ManagementService,
private authUserService: GrpcAuthService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.init(); this.init();

View File

@ -16,7 +16,10 @@ export class DisplayJsonDialogComponent {
public payload: any = ''; public payload: any = '';
public opened$ = this.dialogRef.afterOpened().pipe(mapTo(true)); public opened$ = this.dialogRef.afterOpened().pipe(mapTo(true));
constructor(public dialogRef: MatDialogRef<DisplayJsonDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<DisplayJsonDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.event = data.event; this.event = data.event;
if ((data.event as Event) && data.event.payload) { if ((data.event as Event) && data.event.payload) {
} }

View File

@ -11,7 +11,10 @@ import {
}) })
export class AddDomainDialogComponent { export class AddDomainDialogComponent {
public newdomain: string = ''; public newdomain: string = '';
constructor(public dialogRef: MatDialogRef<AddDomainDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<AddDomainDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);

View File

@ -0,0 +1,67 @@
<ng-container *ngIf="['org.write$'] | hasRole as canwrite$">
<div class="domain-top-view">
<div>
<div class="domain-title-row">
<h2>{{ 'ORG.DOMAINS.TITLE' | translate }}</h2>
<a
mat-icon-button
href="https://zitadel.com/docs/guides/manage/console/organizations#how-zitadel-handles-usernames"
rel="noreferrer"
target="_blank"
>
<i class="las la-info-circle"></i>
</a>
</div>
<p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}</p>
</div>
<span class="fill-space"></span>
<button
[disabled]="(canwrite$ | async) === false"
matTooltip="Add domain"
mat-raised-button
color="primary"
class="cnsl-action-button"
(click)="addNewDomain()"
>
<mat-icon>add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys (actionTriggered)="addNewDomain()"> </cnsl-action-keys>
</button>
</div>
<cnsl-card *ngFor="let domain of domains" class="domain-card">
<div class="domain">
<span class="title">{{ domain.domainName }}</span>
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
<a
*ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)"
class="primaryset"
(click)="setPrimary(domain)"
>{{ 'ORG.DOMAINS.SETPRIMARY' | translate }}</a
>
<span class="fill-space"></span>
<button
mat-icon-button
[disabled]="(canwrite$ | async) === false || domain.isVerified"
*ngIf="verifyOrgDomains"
(click)="verifyDomain(domain)"
>
<i class="las la-pen"></i>
</button>
<button
class="domain-rem-button"
[disabled]="(canwrite$ | async) === false || domain.isPrimary"
matTooltip="Remove domain"
color="warn"
mat-icon-button
(click)="removeDomain(domain.domainName)"
>
<i class="las la-trash"></i>
</button>
</div>
</cnsl-card>
</ng-container>

View File

@ -1,7 +1,6 @@
.domain-top-view { .domain-top-view {
display: flex; display: flex;
align-items: center; align-items: center;
padding-top: 2rem;
.domain-title-row { .domain-title-row {
display: flex; display: flex;

View File

@ -19,6 +19,7 @@ export class DomainsComponent implements OnInit {
public domains: Domain.AsObject[] = []; public domains: Domain.AsObject[] = [];
public primaryDomain: string = ''; public primaryDomain: string = '';
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
public verifyOrgDomains: boolean | undefined;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
@ -38,6 +39,10 @@ export class DomainsComponent implements OnInit {
} }
public loadDomains(): void { public loadDomains(): void {
this.mgmtService.getDomainPolicy().then((result) => {
this.verifyOrgDomains = result.policy?.validateOrgDomains;
});
this.mgmtService.listOrgDomains().then((result) => { this.mgmtService.listOrgDomains().then((result) => {
this.domains = result.resultList; this.domains = result.resultList;
this.primaryDomain = this.domains.find((domain) => domain.isPrimary)?.domainName ?? ''; this.primaryDomain = this.domains.find((domain) => domain.isPrimary)?.domainName ?? '';
@ -68,13 +73,14 @@ export class DomainsComponent implements OnInit {
.addOrgDomain(domainName) .addOrgDomain(domainName)
.then(() => { .then(() => {
this.toast.showInfo('ORG.TOAST.DOMAINADDED', true); this.toast.showInfo('ORG.TOAST.DOMAINADDED', true);
if (this.verifyOrgDomains) {
this.verifyDomain({ this.verifyDomain({
domainName: domainName, domainName: domainName,
validationType: DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED, validationType: DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED,
}); });
setTimeout(() => { } else {
this.loadDomains(); this.loadDomains();
}, 1000); }
}) })
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
@ -120,10 +126,8 @@ export class DomainsComponent implements OnInit {
width: '500px', width: '500px',
}); });
dialogRef.afterClosed().subscribe((reload: boolean) => { dialogRef.afterClosed().subscribe(() => {
if (reload) {
this.loadDomains(); this.loadDomains();
}
}); });
} }
} }

View File

@ -14,13 +14,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
import { AddDomainDialogModule } from './add-domain-dialog/add-domain-dialog.module'; import { AddDomainDialogModule } from './add-domain-dialog/add-domain-dialog.module';
import { DomainVerificationComponent } from './domain-verification/domain-verification.component'; import { DomainVerificationComponent } from './domain-verification/domain-verification.component';
import { DomainsRoutingModule } from './domains-routing.module';
import { DomainsComponent } from './domains.component'; import { DomainsComponent } from './domains.component';
@NgModule({ @NgModule({
declarations: [DomainsComponent, DomainVerificationComponent], declarations: [DomainsComponent, DomainVerificationComponent],
imports: [ imports: [
DomainsRoutingModule,
AddDomainDialogModule, AddDomainDialogModule,
CommonModule, CommonModule,
MatIconModule, MatIconModule,
@ -36,5 +34,6 @@ import { DomainsComponent } from './domains.component';
InfoSectionModule, InfoSectionModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
], ],
exports: [DomainsComponent],
}) })
export default class DomainsModule {} export default class DomainsModule {}

View File

@ -24,7 +24,10 @@ export class FilterOrgComponent extends FilterComponent implements OnInit {
public states: OrgState[] = [OrgState.ORG_STATE_ACTIVE, OrgState.ORG_STATE_INACTIVE, OrgState.ORG_STATE_REMOVED]; public states: OrgState[] = [OrgState.ORG_STATE_ACTIVE, OrgState.ORG_STATE_INACTIVE, OrgState.ORG_STATE_REMOVED];
constructor(router: Router, protected override route: ActivatedRoute) { constructor(
router: Router,
protected override route: ActivatedRoute,
) {
super(router, route); super(router, route);
} }

View File

@ -64,7 +64,10 @@ export class FilterComponent implements OnDestroy {
this.destroy$.complete(); this.destroy$.complete();
} }
constructor(private router: Router, protected route: ActivatedRoute) { constructor(
private router: Router,
protected route: ActivatedRoute,
) {
const changes$ = this.filterChanged.asObservable(); const changes$ = this.filterChanged.asObservable();
changes$.pipe(takeUntil(this.destroy$)).subscribe((queries) => { changes$.pipe(takeUntil(this.destroy$)).subscribe((queries) => {
const filters: Array<FilterSearchQueryAsObject | {}> | undefined = queries const filters: Array<FilterSearchQueryAsObject | {}> | undefined = queries

View File

@ -195,7 +195,7 @@
</a> </a>
</div> </div>
<ng-container *ngIf="user"> <ng-container *ngIf="user && user.id">
<div class="account-card-wrapper"> <div class="account-card-wrapper">
<button <button
cdkOverlayOrigin cdkOverlayOrigin

View File

@ -14,7 +14,10 @@ import { InfoSectionType } from '../info-section/info-section.component';
export class InfoDialogComponent { export class InfoDialogComponent {
public confirm: string = ''; public confirm: string = '';
InfoSectionType: any = InfoSectionType; InfoSectionType: any = InfoSectionType;
constructor(public dialogRef: MatDialogRef<InfoDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<InfoDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);

View File

@ -9,5 +9,8 @@ export const OVERLAY_DATA = new InjectionToken<any>('OVERLAY_DATA');
styleUrls: ['./info-overlay.component.scss'], styleUrls: ['./info-overlay.component.scss'],
}) })
export class InfoOverlayComponent { export class InfoOverlayComponent {
constructor(public workflowService: OverlayWorkflowService, @Inject(OVERLAY_DATA) public data: any) {} constructor(
public workflowService: OverlayWorkflowService,
@Inject(OVERLAY_DATA) public data: any,
) {}
} }

View File

@ -13,7 +13,10 @@ export class MembershipsDataSource extends DataSource<Membership.AsObject> {
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private auth: GrpcAuthService, private service: ManagementService) { constructor(
private auth: GrpcAuthService,
private service: ManagementService,
) {
super(); super();
} }

View File

@ -11,7 +11,10 @@ import {
}) })
export class NameDialogComponent { export class NameDialogComponent {
public name: string = ''; public name: string = '';
constructor(public dialogRef: MatDialogRef<NameDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<NameDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.name = data.name ?? ''; this.name = data.name ?? '';
} }

View File

@ -30,7 +30,11 @@
position: relative; position: relative;
background: none; background: none;
cursor: pointer; cursor: pointer;
font-family: 'Lato', -apple-system, BlinkMacSystemFont, sans-serif; font-family:
'Lato',
-apple-system,
BlinkMacSystemFont,
sans-serif;
box-sizing: border-box; box-sizing: border-box;
height: 27px; height: 27px;

View File

@ -177,17 +177,6 @@
</a> </a>
</ng-template> </ng-template>
<ng-template cnslHasRole [hasRole]="['org.read']">
<a
class="nav-item"
[routerLinkActive]="['active']"
[routerLink]="['/domains']"
[routerLinkActiveOptions]="{ exact: true }"
>
<span class="label">{{ 'MENU.DOMAINS' | translate }}</span>
</a>
</ng-template>
<ng-template cnslHasRole [hasRole]="['org.read']"> <ng-template cnslHasRole [hasRole]="['org.read']">
<a <a
class="nav-item" class="nav-item"

View File

@ -11,7 +11,10 @@ import { ONBOARDING_EVENTS } from 'src/app/utils/onboarding';
export class OnboardingComponent { export class OnboardingComponent {
public actions = this.adminService.progressEvents; public actions = this.adminService.progressEvents;
constructor(public adminService: AdminService, public themeService: ThemeService) { constructor(
public adminService: AdminService,
public themeService: ThemeService,
) {
this.adminService.loadEvents.next(ONBOARDING_EVENTS); this.adminService.loadEvents.next(ONBOARDING_EVENTS);
} }
} }

View File

@ -23,7 +23,10 @@ export class OrgContextComponent implements OnInit {
@Output() public closedCard: EventEmitter<void> = new EventEmitter(); @Output() public closedCard: EventEmitter<void> = new EventEmitter();
@Output() public setOrg: EventEmitter<Org.AsObject> = new EventEmitter(); @Output() public setOrg: EventEmitter<Org.AsObject> = new EventEmitter();
constructor(public authService: AuthenticationService, private auth: GrpcAuthService) { constructor(
public authService: AuthenticationService,
private auth: GrpcAuthService,
) {
this.filterControl.valueChanges.pipe(debounceTime(500)).subscribe((value) => { this.filterControl.valueChanges.pipe(debounceTime(500)).subscribe((value) => {
this.loadOrgs(value.trim().toLowerCase()); this.loadOrgs(value.trim().toLowerCase());
}); });

View File

@ -5,7 +5,6 @@
<h2>{{ 'POLICY.DOMAIN_POLICY.TITLE' | translate }}</h2> <h2>{{ 'POLICY.DOMAIN_POLICY.TITLE' | translate }}</h2>
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> <cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
<!-- <ng-template cnslHasRole [hasRole]="['domain.policy.delete']"> -->
<button <button
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
matTooltip="{{ 'POLICY.RESET' | translate }}" matTooltip="{{ 'POLICY.RESET' | translate }}"
@ -26,7 +25,6 @@
> >
{{ 'POLICY.RESET' | translate }} {{ 'POLICY.RESET' | translate }}
</button> </button>
<!-- </ng-template> -->
<div class="domain-policy-content" *ngIf="domainData"> <div class="domain-policy-content" *ngIf="domainData">
<div class="row"> <div class="row">

View File

@ -13,7 +13,10 @@ export class GeneralSettingsComponent implements OnInit {
public defaultLanguageOptions: string[] = []; public defaultLanguageOptions: string[] = [];
public loading: boolean = false; public loading: boolean = false;
constructor(private service: AdminService, private toast: ToastService) {} constructor(
private service: AdminService,
private toast: ToastService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.fetchData(); this.fetchData();

View File

@ -20,7 +20,10 @@ export class DialogAddTypeComponent {
public availableMfaTypes: Array<MultiFactorType | SecondFactorType> = []; public availableMfaTypes: Array<MultiFactorType | SecondFactorType> = [];
public newMfaType!: MultiFactorType | SecondFactorType; public newMfaType!: MultiFactorType | SecondFactorType;
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<DialogAddTypeComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.availableMfaTypes = data.types; this.availableMfaTypes = data.types;
this.newMfaType = data.types && data.types[0] ? data.types[0] : undefined; this.newMfaType = data.types && data.types[0] ? data.types[0] : undefined;
} }

View File

@ -51,7 +51,11 @@ export class FactorTableComponent {
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
constructor(public translate: TranslateService, private toast: ToastService, private dialog: MatDialog) {} constructor(
public translate: TranslateService,
private toast: ToastService,
private dialog: MatDialog,
) {}
public removeMfa(type: MultiFactorType | SecondFactorType): void { public removeMfa(type: MultiFactorType | SecondFactorType): void {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {

View File

@ -36,7 +36,11 @@ export class NotificationPolicyComponent implements OnInit {
public isDefault: boolean = false; public isDefault: boolean = false;
private hasNotificationPolicy: boolean = false; private hasNotificationPolicy: boolean = false;
constructor(private toast: ToastService, private injector: Injector, private dialog: MatDialog) {} constructor(
private toast: ToastService,
private injector: Injector,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
switch (this.serviceType) { switch (this.serviceType) {

View File

@ -11,7 +11,10 @@ import {
}) })
export class PasswordDialogComponent { export class PasswordDialogComponent {
public password: string = ''; public password: string = '';
constructor(public dialogRef: MatDialogRef<PasswordDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<PasswordDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
closeDialog(password: string = ''): void { closeDialog(password: string = ''): void {
this.dialogRef.close(password); this.dialogRef.close(password);

View File

@ -27,7 +27,11 @@ export class PasswordComplexityPolicyComponent implements OnInit {
public loading: boolean = false; public loading: boolean = false;
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
constructor(private toast: ToastService, private injector: Injector, private dialog: MatDialog) {} constructor(
private toast: ToastService,
private injector: Injector,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
switch (this.serviceType) { switch (this.serviceType) {

View File

@ -26,7 +26,11 @@ export class PasswordLockoutPolicyComponent implements OnInit {
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
constructor(private toast: ToastService, private injector: Injector, private dialog: MatDialog) {} constructor(
private toast: ToastService,
private injector: Injector,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
switch (this.serviceType) { switch (this.serviceType) {

View File

@ -54,7 +54,9 @@
::ng-deep .chrome-picker { ::ng-deep .chrome-picker {
border-radius: 0.5rem !important; border-radius: 0.5rem !important;
box-shadow: 0 0 1px #00000020, 0 1px 3px #00000020 !important; box-shadow:
0 0 1px #00000020,
0 1px 3px #00000020 !important;
} }
::ng-deep .saturation { ::ng-deep .saturation {

View File

@ -29,7 +29,11 @@ export class SecretGeneratorComponent implements OnInit {
SecretGeneratorType.SECRET_GENERATOR_TYPE_OTP_EMAIL, SecretGeneratorType.SECRET_GENERATOR_TYPE_OTP_EMAIL,
]; ];
constructor(private service: AdminService, private toast: ToastService, private dialog: MatDialog) {} constructor(
private service: AdminService,
private toast: ToastService,
private dialog: MatDialog,
) {}
ngOnInit(): void { ngOnInit(): void {
this.fetchData(); this.fetchData();

View File

@ -20,7 +20,10 @@ export class SecurityPolicyComponent implements OnInit {
@Input() public originsControl: UntypedFormControl = new UntypedFormControl({ value: [], disabled: true }); @Input() public originsControl: UntypedFormControl = new UntypedFormControl({ value: [], disabled: true });
constructor(private service: AdminService, private toast: ToastService) {} constructor(
private service: AdminService,
private toast: ToastService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.fetchData(); this.fetchData();

View File

@ -246,7 +246,8 @@ export class ProviderAppleComponent {
return (e) => { return (e) => {
const keyBase64 = e.target?.result; const keyBase64 = e.target?.result;
if (keyBase64 && typeof keyBase64 === 'string') { if (keyBase64 && typeof keyBase64 === 'string') {
const cropped = keyBase64.replace('data:application/octet-stream;base64,', ''); const contentType = file.type || 'application/octet-stream';
const cropped = keyBase64.replace(`data:${contentType};base64,`, '');
this.privateKey?.setValue(cropped); this.privateKey?.setValue(cropped);
} }
}; };

View File

@ -22,7 +22,10 @@ export class SearchOrgAutocompleteComponent implements OnInit, OnDestroy {
@Output() public selectionChanged: EventEmitter<Org.AsObject> = new EventEmitter(); @Output() public selectionChanged: EventEmitter<Org.AsObject> = new EventEmitter();
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
constructor(public authService: AuthenticationService, private auth: GrpcAuthService) { constructor(
public authService: AuthenticationService,
private auth: GrpcAuthService,
) {
this.myControl.valueChanges this.myControl.valueChanges
.pipe( .pipe(
takeUntil(this.unsubscribed$), takeUntil(this.unsubscribed$),

View File

@ -57,7 +57,11 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec
@Input() public singleOutput: boolean = false; @Input() public singleOutput: boolean = false;
private unsubscribed$: Subject<void> = new Subject(); private unsubscribed$: Subject<void> = new Subject();
constructor(private userService: ManagementService, private toast: ToastService, private cdref: ChangeDetectorRef) {} constructor(
private userService: ManagementService,
private toast: ToastService,
private cdref: ChangeDetectorRef,
) {}
public ngOnInit(): void { public ngOnInit(): void {
if (this.target === UserTarget.EXTERNAL) { if (this.target === UserTarget.EXTERNAL) {

View File

@ -18,7 +18,10 @@
<ng-container *ngIf="currentSetting === 'login'"> <ng-container *ngIf="currentSetting === 'login'">
<cnsl-login-policy [serviceType]="serviceType"></cnsl-login-policy> <cnsl-login-policy [serviceType]="serviceType"></cnsl-login-policy>
</ng-container> </ng-container>
<ng-container *ngIf="currentSetting === 'domain'"> <ng-container *ngIf="currentSetting === 'verified_domains'">
<cnsl-domains></cnsl-domains>
</ng-container>
<ng-container *ngIf="currentSetting === 'domain' && (['iam.policy.write'] | hasRole | async) === true">
<cnsl-domain-policy [serviceType]="serviceType"></cnsl-domain-policy> <cnsl-domain-policy [serviceType]="serviceType"></cnsl-domain-policy>
</ng-container> </ng-container>
<ng-container *ngIf="currentSetting === 'idp'"> <ng-container *ngIf="currentSetting === 'idp'">

View File

@ -5,6 +5,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../card/card.module'; import { CardModule } from '../card/card.module';
import DomainsModule from '../domains/domains.module';
import { DomainPolicyModule } from '../policies/domain-policy/domain-policy.module'; import { DomainPolicyModule } from '../policies/domain-policy/domain-policy.module';
import { GeneralSettingsModule } from '../policies/general-settings/general-settings.module'; import { GeneralSettingsModule } from '../policies/general-settings/general-settings.module';
import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module'; import { IdpSettingsModule } from '../policies/idp-settings/idp-settings.module';
@ -40,6 +41,7 @@ import { SettingsListComponent } from './settings-list.component';
PrivacyPolicyModule, PrivacyPolicyModule,
MessageTextsPolicyModule, MessageTextsPolicyModule,
SecurityPolicyModule, SecurityPolicyModule,
DomainsModule,
LoginTextsPolicyModule, LoginTextsPolicyModule,
DomainPolicyModule, DomainPolicyModule,
TranslateModule, TranslateModule,

View File

@ -43,13 +43,22 @@ export const LOGIN: SidenavSetting = {
}, },
}; };
export const VERIFIED_DOMAINS: SidenavSetting = {
id: 'verified_domains',
i18nKey: 'SETTINGS.LIST.VERIFIED_DOMAINS',
groupI18nKey: 'SETTINGS.GROUPS.DOMAIN',
requiredRoles: {
[PolicyComponentServiceType.MGMT]: ['org.read'],
},
};
export const DOMAIN: SidenavSetting = { export const DOMAIN: SidenavSetting = {
id: 'domain', id: 'domain',
i18nKey: 'SETTINGS.LIST.DOMAIN', i18nKey: 'SETTINGS.LIST.DOMAIN',
groupI18nKey: 'SETTINGS.GROUPS.DOMAIN', groupI18nKey: 'SETTINGS.GROUPS.DOMAIN',
requiredRoles: { requiredRoles: {
[PolicyComponentServiceType.MGMT]: ['policy.read'], [PolicyComponentServiceType.MGMT]: ['iam.policy.write'],
[PolicyComponentServiceType.ADMIN]: ['iam.policy.read'], [PolicyComponentServiceType.ADMIN]: ['iam.policy.write'],
}, },
}; };

View File

@ -240,7 +240,10 @@
box-sizing: border-box; box-sizing: border-box;
border-radius: 1rem; border-radius: 1rem;
border: 1px solid $border-color; border: 1px solid $border-color;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); box-shadow:
0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
} }
.cdk-drag-placeholder { .cdk-drag-placeholder {

View File

@ -16,7 +16,10 @@ export class ShowKeyDialogComponent {
public keyResponse!: AddMachineKeyResponse.AsObject | AddAppKeyResponse.AsObject; public keyResponse!: AddMachineKeyResponse.AsObject | AddAppKeyResponse.AsObject;
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
constructor(public dialogRef: MatDialogRef<ShowKeyDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<ShowKeyDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.keyResponse = data.key; this.keyResponse = data.key;
} }

View File

@ -17,7 +17,10 @@ export class ShowTokenDialogComponent {
public copied: string = ''; public copied: string = '';
InfoSectionType: any = InfoSectionType; InfoSectionType: any = InfoSectionType;
constructor(public dialogRef: MatDialogRef<ShowTokenDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<ShowTokenDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.tokenResponse = data.token; this.tokenResponse = data.token;
} }

View File

@ -28,11 +28,6 @@
<button <button
(click)="value = setting.id" (click)="value = setting.id"
*ngIf="
!setting.requiredRoles ||
(setting.requiredRoles.mgmt && (setting.requiredRoles.mgmt | hasRole | async)) ||
(setting.requiredRoles.admin && (setting.requiredRoles.admin | hasRole | async))
"
class="sidenav-setting-list-element hide-on-mobile" class="sidenav-setting-list-element hide-on-mobile"
[ngClass]="{ active: currentSetting === setting.id, show: currentSetting === undefined }" [ngClass]="{ active: currentSetting === setting.id, show: currentSetting === undefined }"
[attr.data-e2e]="'sidenav-element-' + setting.id" [attr.data-e2e]="'sidenav-element-' + setting.id"

View File

@ -30,7 +30,10 @@ export class SidenavComponent implements ControlValueAccessor, OnInit {
@Input() public queryParam: string = ''; @Input() public queryParam: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
constructor(private router: Router, private route: ActivatedRoute) {} constructor(
private router: Router,
private route: ActivatedRoute,
) {}
ngOnInit(): void { ngOnInit(): void {
if (!this.value) { if (!this.value) {

View File

@ -16,7 +16,10 @@ export class UserGrantRoleDialogComponent {
public selectedRoleKeys: string[] = []; public selectedRoleKeys: string[] = [];
constructor(public dialogRef: MatDialogRef<UserGrantRoleDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<UserGrantRoleDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.projectId = data.projectId; this.projectId = data.projectId;
this.grantId = data.grantId; this.grantId = data.grantId;
this.selectedRoleKeysList = data.selectedRoleKeysList; this.selectedRoleKeysList = data.selectedRoleKeysList;

View File

@ -31,7 +31,10 @@ export class UserGrantsDataSource extends DataSource<UserGrantAsObject> {
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private authService: GrpcAuthService, private userService: ManagementService) { constructor(
private authService: GrpcAuthService,
private userService: ManagementService,
) {
super(); super();
} }

View File

@ -30,7 +30,7 @@
<a <a
actions actions
*ngIf="disableWrite === false && selection.hasValue() === false" *ngIf="disableWrite === false && selection.hasValue() === false && routerLink"
matTooltip="{{ 'GRANTS.ADD' | translate }}" matTooltip="{{ 'GRANTS.ADD' | translate }}"
color="primary" color="primary"
class="cnsl-action-button" class="cnsl-action-button"

View File

@ -56,7 +56,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@ViewChild('input') public filter!: MatInput; @ViewChild('input') public filter!: MatInput;
public projectRoleOptions: Role.AsObject[] = []; public projectRoleOptions: Role.AsObject[] = [];
public routerLink: any = ['']; public routerLink: any = undefined;
public loadedId: string = ''; public loadedId: string = '';
public loadedProjectId: string = ''; public loadedProjectId: string = '';

View File

@ -14,7 +14,10 @@ import { InfoSectionType } from '../info-section/info-section.component';
export class WarnDialogComponent { export class WarnDialogComponent {
public confirm: string = ''; public confirm: string = '';
InfoSectionType: any = InfoSectionType; InfoSectionType: any = InfoSectionType;
constructor(public dialogRef: MatDialogRef<WarnDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<WarnDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);

View File

@ -150,7 +150,10 @@
border-radius: 0.5rem; border-radius: 0.5rem;
padding: 0 0.5rem; padding: 0 0.5rem;
background-color: $primary-color; background-color: $primary-color;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12); box-shadow:
0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
i { i {
margin-right: 0.5rem; margin-right: 0.5rem;

View File

@ -13,7 +13,10 @@ export class AppCreateComponent {
public projectId: string = ''; public projectId: string = '';
public ProjectAutocompleteType: any = ProjectAutocompleteType; public ProjectAutocompleteType: any = ProjectAutocompleteType;
constructor(private router: Router, breadcrumbService: BreadcrumbService) { constructor(
private router: Router,
breadcrumbService: BreadcrumbService,
) {
const bread: Breadcrumb = { const bread: Breadcrumb = {
type: BreadcrumbType.ORG, type: BreadcrumbType.ORG,
routerLink: ['/org'], routerLink: ['/org'],

View File

@ -1,17 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DomainsComponent } from './domains.component';
const routes: Routes = [
{
path: '',
component: DomainsComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class DomainsRoutingModule {}

View File

@ -1,69 +0,0 @@
<div class="max-width-container">
<ng-container *ngIf="['org.write$'] | hasRole as canwrite$">
<div class="domain-top-view">
<div>
<div class="domain-title-row">
<h1>{{ 'ORG.DOMAINS.TITLE' | translate }}</h1>
<a
mat-icon-button
href="https://zitadel.com/docs/guides/manage/console/organizations#how-zitadel-handles-usernames"
rel="noreferrer"
target="_blank"
>
<i class="las la-info-circle"></i>
</a>
</div>
<p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}</p>
</div>
<span class="fill-space"></span>
<button
[disabled]="(canwrite$ | async) === false"
matTooltip="Add domain"
mat-raised-button
color="primary"
class="cnsl-action-button"
(click)="addNewDomain()"
>
<mat-icon>add</mat-icon>
<span>{{ 'ACTIONS.NEW' | translate }}</span>
<cnsl-action-keys (actionTriggered)="addNewDomain()"> </cnsl-action-keys>
</button>
</div>
<cnsl-card *ngFor="let domain of domains" class="domain-card">
<div class="domain">
<span class="title">{{ domain.domainName }}</span>
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
<a
*ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)"
class="primaryset"
(click)="setPrimary(domain)"
>{{ 'ORG.DOMAINS.SETPRIMARY' | translate }}</a
>
<span class="fill-space"></span>
<button
mat-icon-button
[disabled]="(canwrite$ | async) === false || domain.isVerified"
*ngIf="canwrite$ | async"
(click)="verifyDomain(domain)"
>
<i class="las la-pen"></i>
</button>
<button
class="domain-rem-button"
[disabled]="(canwrite$ | async) === false || domain.isPrimary"
matTooltip="Remove domain"
color="warn"
mat-icon-button
(click)="removeDomain(domain.domainName)"
>
<i class="las la-trash"></i>
</button>
</div>
</cnsl-card>
</ng-container>
</div>

View File

@ -21,7 +21,11 @@ export class HomeComponent {
public dark: boolean = true; public dark: boolean = true;
constructor(public authService: GrpcAuthService, breadcrumbService: BreadcrumbService, public themeService: ThemeService) { constructor(
public authService: GrpcAuthService,
breadcrumbService: BreadcrumbService,
public themeService: ThemeService,
) {
const bread: Breadcrumb = { const bread: Breadcrumb = {
type: BreadcrumbType.ORG, type: BreadcrumbType.ORG,
routerLink: ['/org'], routerLink: ['/org'],

View File

@ -23,7 +23,10 @@ export class IamViewsComponent implements AfterViewInit {
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private adminService: AdminService, private breadcrumbService: BreadcrumbService) { constructor(
private adminService: AdminService,
private breadcrumbService: BreadcrumbService,
) {
this.loadViews(); this.loadViews();
const breadcrumbs = [ const breadcrumbs = [

View File

@ -8,9 +8,11 @@
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
</div> </div>
<ng-container *ngIf="settingsList | async as list">
<cnsl-settings-list <cnsl-settings-list
[selectedId]="id" [selectedId]="id"
[serviceType]="PolicyComponentServiceType.ADMIN" [serviceType]="PolicyComponentServiceType.ADMIN"
[settingsList]="settingsList" [settingsList]="list"
></cnsl-settings-list> ></cnsl-settings-list>
</ng-container>
</div> </div>

View File

@ -1,10 +1,11 @@
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { Subject, takeUntil } from 'rxjs'; import { Observable, of, Subject, takeUntil } from 'rxjs';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { import {
BRANDING, BRANDING,
COMPLEXITY, COMPLEXITY,
@ -27,10 +28,10 @@ import {
templateUrl: './instance-settings.component.html', templateUrl: './instance-settings.component.html',
styleUrls: ['./instance-settings.component.scss'], styleUrls: ['./instance-settings.component.scss'],
}) })
export class InstanceSettingsComponent implements OnDestroy { export class InstanceSettingsComponent implements OnInit, OnDestroy {
public id: string = ''; public id: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public settingsList: SidenavSetting[] = [ public defaultSettingsList: SidenavSetting[] = [
GENERAL, GENERAL,
// notifications // notifications
// { showWarn: true, ...NOTIFICATIONS }, // { showWarn: true, ...NOTIFICATIONS },
@ -53,8 +54,14 @@ export class InstanceSettingsComponent implements OnDestroy {
SECURITY, SECURITY,
]; ];
public settingsList: Observable<SidenavSetting[]> = of([]);
private destroy$: Subject<void> = new Subject(); private destroy$: Subject<void> = new Subject();
constructor(breadcrumbService: BreadcrumbService, activatedRoute: ActivatedRoute) { constructor(
breadcrumbService: BreadcrumbService,
activatedRoute: ActivatedRoute,
public authService: GrpcAuthService,
) {
const breadcrumbs = [ const breadcrumbs = [
new Breadcrumb({ new Breadcrumb({
type: BreadcrumbType.INSTANCE, type: BreadcrumbType.INSTANCE,
@ -72,6 +79,10 @@ export class InstanceSettingsComponent implements OnDestroy {
}); });
} }
ngOnInit(): void {
this.settingsList = this.authService.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.admin);
}
ngOnDestroy(): void { ngOnDestroy(): void {
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();

View File

@ -8,9 +8,11 @@
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
</div> </div>
<ng-container *ngIf="settingsList | async as list">
<cnsl-settings-list <cnsl-settings-list
[selectedId]="id" [selectedId]="id"
[serviceType]="PolicyComponentServiceType.MGMT" [serviceType]="PolicyComponentServiceType.MGMT"
[settingsList]="settingsList" [settingsList]="list"
></cnsl-settings-list> ></cnsl-settings-list>
</ng-container>
</div> </div>

View File

@ -1,10 +1,11 @@
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { take } from 'rxjs'; import { Observable, of, take } from 'rxjs';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { import {
BRANDING, BRANDING,
COMPLEXITY, COMPLEXITY,
@ -16,6 +17,7 @@ import {
MESSAGETEXTS, MESSAGETEXTS,
NOTIFICATION_POLICY, NOTIFICATION_POLICY,
PRIVACYPOLICY, PRIVACYPOLICY,
VERIFIED_DOMAINS,
} from '../../modules/settings-list/settings'; } from '../../modules/settings-list/settings';
@Component({ @Component({
@ -23,15 +25,17 @@ import {
templateUrl: './org-settings.component.html', templateUrl: './org-settings.component.html',
styleUrls: ['./org-settings.component.scss'], styleUrls: ['./org-settings.component.scss'],
}) })
export class OrgSettingsComponent { export class OrgSettingsComponent implements OnInit {
public id: string = ''; public id: string = '';
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public settingsList: SidenavSetting[] = [
private defaultSettingsList: SidenavSetting[] = [
LOGIN, LOGIN,
IDP, IDP,
COMPLEXITY, COMPLEXITY,
LOCKOUT, LOCKOUT,
NOTIFICATION_POLICY, NOTIFICATION_POLICY,
VERIFIED_DOMAINS,
DOMAIN, DOMAIN,
BRANDING, BRANDING,
MESSAGETEXTS, MESSAGETEXTS,
@ -39,7 +43,13 @@ export class OrgSettingsComponent {
PRIVACYPOLICY, PRIVACYPOLICY,
]; ];
constructor(breadcrumbService: BreadcrumbService, activatedRoute: ActivatedRoute) { public settingsList: Observable<Array<SidenavSetting>> = of([]);
constructor(
breadcrumbService: BreadcrumbService,
activatedRoute: ActivatedRoute,
public authService: GrpcAuthService,
) {
const breadcrumbs = [ const breadcrumbs = [
new Breadcrumb({ new Breadcrumb({
type: BreadcrumbType.ORG, type: BreadcrumbType.ORG,
@ -55,4 +65,10 @@ export class OrgSettingsComponent {
} }
}); });
} }
ngOnInit(): void {
this.settingsList = this.authService
.isAllowedMapper(this.defaultSettingsList, (setting) => setting.requiredRoles.mgmt)
.pipe(take(1));
}
} }

View File

@ -11,7 +11,10 @@ import {
}) })
export class AuthMethodDialogComponent { export class AuthMethodDialogComponent {
public authmethod: string = ''; public authmethod: string = '';
constructor(public dialogRef: MatDialogRef<AuthMethodDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<AuthMethodDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
this.authmethod = data.initialAuthMethod; this.authmethod = data.initialAuthMethod;
} }

View File

@ -11,7 +11,10 @@ import {
}) })
export class AppSecretDialogComponent { export class AppSecretDialogComponent {
public copied: string = ''; public copied: string = '';
constructor(public dialogRef: MatDialogRef<AppSecretDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<AppSecretDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);

View File

@ -17,7 +17,10 @@ export class ProjectGrantsDataSource extends DataSource<GrantedProject.AsObject>
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private mgmtService: ManagementService, private toast: ToastService) { constructor(
private mgmtService: ManagementService,
private toast: ToastService,
) {
super(); super();
} }

View File

@ -39,7 +39,11 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
public AuthFactorState: any = AuthFactorState; public AuthFactorState: any = AuthFactorState;
public error: string = ''; public error: string = '';
constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) {} constructor(
private service: GrpcAuthService,
private toast: ToastService,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
this.getPasswordless(); this.getPasswordless();

View File

@ -42,7 +42,11 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
public otpSmsDisabled$ = new BehaviorSubject<boolean>(true); public otpSmsDisabled$ = new BehaviorSubject<boolean>(true);
public otpEmailDisabled$ = new BehaviorSubject<boolean>(true); public otpEmailDisabled$ = new BehaviorSubject<boolean>(true);
constructor(private service: GrpcAuthService, private toast: ToastService, private dialog: MatDialog) {} constructor(
private service: GrpcAuthService,
private toast: ToastService,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
this.getMFAs(); this.getMFAs();

View File

@ -11,7 +11,10 @@ import {
}) })
export class CodeDialogComponent { export class CodeDialogComponent {
public code: string = ''; public code: string = '';
constructor(public dialogRef: MatDialogRef<CodeDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<CodeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
closeDialog(code: string = ''): void { closeDialog(code: string = ''): void {
this.dialogRef.close(code); this.dialogRef.close(code);

View File

@ -11,7 +11,10 @@ import {
}) })
export class ResendEmailDialogComponent { export class ResendEmailDialogComponent {
public email: string = ''; public email: string = '';
constructor(public dialogRef: MatDialogRef<ResendEmailDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) { constructor(
public dialogRef: MatDialogRef<ResendEmailDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {
if (data.email) { if (data.email) {
this.email = data.email; this.email = data.email;
} }

View File

@ -26,7 +26,10 @@ export class ContactComponent {
public UserState: any = UserState; public UserState: any = UserState;
public EditDialogType: any = EditDialogType; public EditDialogType: any = EditDialogType;
constructor(private dialog: MatDialog, private authService: GrpcAuthService) {} constructor(
private dialog: MatDialog,
private authService: GrpcAuthService,
) {}
async emitDeletePhone(): Promise<void> { async emitDeletePhone(): Promise<void> {
const { resultList } = await this.authService.listMyMultiFactors(); const { resultList } = await this.authService.listMyMultiFactors();

View File

@ -29,7 +29,10 @@ export class DetailFormComponent implements OnDestroy, OnChanges {
private sub: Subscription = new Subscription(); private sub: Subscription = new Subscription();
constructor(private fb: UntypedFormBuilder, private dialog: MatDialog) { constructor(
private fb: UntypedFormBuilder,
private dialog: MatDialog,
) {
this.profileForm = this.fb.group({ this.profileForm = this.fb.group({
userName: [{ value: '', disabled: true }, [requiredValidator]], userName: [{ value: '', disabled: true }, [requiredValidator]],
firstName: [{ value: '', disabled: this.disabled }, requiredValidator], firstName: [{ value: '', disabled: this.disabled }, requiredValidator],

View File

@ -35,7 +35,10 @@ export class ExternalIdpsComponent implements OnInit, OnDestroy {
'actions', 'actions',
]; ];
constructor(private toast: ToastService, private dialog: MatDialog) {} constructor(
private toast: ToastService,
private dialog: MatDialog,
) {}
ngOnInit(): void { ngOnInit(): void {
this.getData(10, 0); this.getData(10, 0);

View File

@ -11,7 +11,10 @@ import {
}) })
export class MachineSecretDialogComponent { export class MachineSecretDialogComponent {
public copied: string = ''; public copied: string = '';
constructor(public dialogRef: MatDialogRef<MachineSecretDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any) {} constructor(
public dialogRef: MatDialogRef<MachineSecretDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) {}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);

View File

@ -37,7 +37,11 @@ export class PasswordlessComponent implements OnInit, OnDestroy {
public AuthFactorState: any = AuthFactorState; public AuthFactorState: any = AuthFactorState;
public error: string = ''; public error: string = '';
constructor(private service: ManagementService, private toast: ToastService, private dialog: MatDialog) {} constructor(
private service: ManagementService,
private toast: ToastService,
private dialog: MatDialog,
) {}
public ngOnInit(): void { public ngOnInit(): void {
this.getPasswordless(); this.getPasswordless();

View File

@ -78,7 +78,7 @@
<ng-container *ngIf="currentSetting === 'general'"> <ng-container *ngIf="currentSetting === 'general'">
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:' + user.id]"> <ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:' + user.id]">
<cnsl-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}"> <cnsl-card *ngIf="user.human" title="{{ user.preferredLoginName }} - {{ 'USER.PROFILE.TITLE' | translate }}">
<cnsl-detail-form <cnsl-detail-form
[preferredLoginName]="user.preferredLoginName" [preferredLoginName]="user.preferredLoginName"
[disabled]="(canWrite$ | async) === false" [disabled]="(canWrite$ | async) === false"

View File

@ -32,7 +32,11 @@ export class UserMfaComponent implements OnInit, OnDestroy {
public AuthFactorState: any = AuthFactorState; public AuthFactorState: any = AuthFactorState;
public error: string = ''; public error: string = '';
constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) {} constructor(
private mgmtUserService: ManagementService,
private dialog: MatDialog,
private toast: ToastService,
) {}
public ngOnInit(): void { public ngOnInit(): void {
this.getMFAs(); this.getMFAs();

View File

@ -14,7 +14,11 @@ export class UserListComponent {
public Type: any = Type; public Type: any = Type;
public type: Type = Type.TYPE_HUMAN; public type: Type = Type.TYPE_HUMAN;
constructor(public translate: TranslateService, activatedRoute: ActivatedRoute, breadcrumbService: BreadcrumbService) { constructor(
public translate: TranslateService,
activatedRoute: ActivatedRoute,
breadcrumbService: BreadcrumbService,
) {
activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => { activatedRoute.queryParams.pipe(take(1)).subscribe((params: Params) => {
const { type } = params; const { type } = params;
if (type && type === 'human') { if (type && type === 'human') {

View File

@ -124,6 +124,20 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="preferredLoginName">
<th
mat-header-cell
*matHeaderCellDef
mat-sort-header
[ngClass]="{ 'search-active': this.userSearchKey === UserListSearchKey.DISPLAY_NAME }"
>
{{ 'USER.PROFILE.PREFERREDLOGINNAME' | translate }}
</th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id] : null">
<span *ngIf="user.human">{{ user.preferredLoginName }}</span>
</td>
</ng-container>
<ng-container matColumnDef="username"> <ng-container matColumnDef="username">
<th <th
mat-header-cell mat-header-cell

View File

@ -51,7 +51,7 @@ export class UserTableComponent implements OnInit {
@Input() public displayedColumnsHuman: string[] = [ @Input() public displayedColumnsHuman: string[] = [
'select', 'select',
'displayName', 'displayName',
'username', 'preferredLoginName',
'email', 'email',
'state', 'state',
'creationDate', 'creationDate',
@ -194,6 +194,10 @@ export class UserTableComponent implements OnInit {
case 'username': case 'username':
sortingField = UserFieldName.USER_FIELD_NAME_USER_NAME; sortingField = UserFieldName.USER_FIELD_NAME_USER_NAME;
break; break;
case 'preferredLoginName':
// TODO: replace with preferred username sorting once implemented
sortingField = UserFieldName.USER_FIELD_NAME_USER_NAME;
break;
case 'email': case 'email':
sortingField = UserFieldName.USER_FIELD_NAME_EMAIL; sortingField = UserFieldName.USER_FIELD_NAME_EMAIL;
break; break;

View File

@ -390,7 +390,10 @@ export class AdminService {
public progressTotal: BehaviorSubject<number> = new BehaviorSubject(0); public progressTotal: BehaviorSubject<number> = new BehaviorSubject(0);
public progressAllDone: BehaviorSubject<boolean> = new BehaviorSubject(true); public progressAllDone: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor(private readonly grpcService: GrpcService, private storageService: StorageService) { constructor(
private readonly grpcService: GrpcService,
private storageService: StorageService,
) {
this.progressEvents$.subscribe(this.progressEvents); this.progressEvents$.subscribe(this.progressEvents);
this.hideOnboarding = this.hideOnboarding =

View File

@ -71,7 +71,11 @@ export const ENDPOINT = {
}) })
export class AssetService { export class AssetService {
private accessToken: string = ''; private accessToken: string = '';
constructor(private envService: EnvironmentService, private http: HttpClient, private storageService: StorageService) { constructor(
private envService: EnvironmentService,
private http: HttpClient,
private storageService: StorageService,
) {
const aT = this.storageService.getItem(accessTokenStorageKey); const aT = this.storageService.getItem(accessTokenStorageKey);
if (aT) { if (aT) {
this.accessToken = aT; this.accessToken = aT;

View File

@ -12,7 +12,10 @@ export class AuthenticationService {
private _authenticated: boolean = false; private _authenticated: boolean = false;
private readonly _authenticationChanged: BehaviorSubject<boolean> = new BehaviorSubject(this.authenticated); private readonly _authenticationChanged: BehaviorSubject<boolean> = new BehaviorSubject(this.authenticated);
constructor(private oauthService: OAuthService, private statehandler: StatehandlerService) {} constructor(
private oauthService: OAuthService,
private statehandler: StatehandlerService,
) {}
public initConfig(data: AuthConfig): void { public initConfig(data: AuthConfig): void {
this.authConfig = data; this.authConfig = data;

View File

@ -36,7 +36,10 @@ export class EnvironmentService {
private environment$: Observable<Environment>; private environment$: Observable<Environment>;
private wellKnown$: Observable<WellKnown>; private wellKnown$: Observable<WellKnown>;
constructor(private http: HttpClient, private exhaustedSvc: ExhaustedService) { constructor(
private http: HttpClient,
private exhaustedSvc: ExhaustedService,
) {
this.environment$ = this.createEnvironment(); this.environment$ = this.createEnvironment();
this.wellKnown$ = this.createWellKnown(this.environment$); this.wellKnown$ = this.createWellKnown(this.environment$);
} }

Some files were not shown because too many files have changed in this diff Show More