diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts index 86f2b06c1..2c5d54f60 100644 --- a/console/src/app/app.module.ts +++ b/console/src/app/app.module.ts @@ -36,6 +36,7 @@ import { AvatarModule } from './modules/avatar/avatar.module'; import { InputModule } from './modules/input/input.module'; import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module'; import { SignedoutComponent } from './pages/signedout/signedout.component'; +import { HasFeaturePipeModule } from './pipes/has-feature-pipe/has-feature-pipe.module'; import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module'; import { GrpcAuthService } from './services/grpc-auth.service'; import { GrpcService } from './services/grpc.service'; @@ -112,6 +113,7 @@ const authConfig: AuthConfig = { OutsideClickModule, InputModule, HasRolePipeModule, + HasFeaturePipeModule, MatProgressBarModule, MatProgressSpinnerModule, MatToolbarModule, diff --git a/console/src/app/modules/changes/changes.component.ts b/console/src/app/modules/changes/changes.component.ts index 1dfa1542a..23f094889 100644 --- a/console/src/app/modules/changes/changes.component.ts +++ b/console/src/app/modules/changes/changes.component.ts @@ -75,7 +75,6 @@ export class ChangesComponent implements OnInit, OnDestroy { } public scrollHandler(e: any): void { - console.log('bottom'); if (e === 'bottom') { this.more(); } @@ -107,7 +106,6 @@ export class ChangesComponent implements OnInit, OnDestroy { private more(): void { const cursor = this.getCursor(); - console.log('cursor' + cursor); let more: Promise; @@ -152,7 +150,6 @@ export class ChangesComponent implements OnInit, OnDestroy { take(1), tap((res: ListChanges) => { const values = res.resultList; - console.log(values); const mapped = this.mapChanges(values); // update source with new values, done loading // this._data.next(values); @@ -226,7 +223,6 @@ export class ChangesComponent implements OnInit, OnDestroy { return parseFloat(b.key) - parseFloat(a.key); }); - // console.log(arr); return arr; } @@ -237,7 +233,6 @@ export class ChangesComponent implements OnInit, OnDestroy { getTimestampIndex(date: any): number { const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000); - console.log(ts); return ts.getTime(); } diff --git a/console/src/app/modules/client-keys/client-keys.component.html b/console/src/app/modules/client-keys/client-keys.component.html index 57c4b09ff..edf7bd95b 100644 --- a/console/src/app/modules/client-keys/client-keys.component.html +++ b/console/src/app/modules/client-keys/client-keys.component.html @@ -36,14 +36,14 @@ {{ 'USER.MACHINE.CREATIONDATE' | translate }} - {{key.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{key.details.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}} {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} - {{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}} diff --git a/console/src/app/modules/client-keys/client-keys.component.ts b/console/src/app/modules/client-keys/client-keys.component.ts index b0d66d224..d658b9f4c 100644 --- a/console/src/app/modules/client-keys/client-keys.component.ts +++ b/console/src/app/modules/client-keys/client-keys.component.ts @@ -86,7 +86,6 @@ export class ClientKeysComponent implements OnInit { if (resp.date as Moment) { const ts = new Timestamp(); - console.log(resp.date.toDate()); const milliseconds = resp.date.toDate().getTime(); const seconds = Math.abs(milliseconds / 1000); const nanos = (milliseconds - seconds * 1000) * 1000 * 1000; diff --git a/console/src/app/modules/features/features-routing.module.ts b/console/src/app/modules/features/features-routing.module.ts new file mode 100644 index 000000000..8974762fe --- /dev/null +++ b/console/src/app/modules/features/features-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { FeaturesComponent } from './features.component'; + +const routes: Routes = [ + { + path: '', + component: FeaturesComponent, + data: { + animation: 'DetailPage', + }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class FeaturesRoutingModule { } diff --git a/console/src/app/modules/features/features.component.html b/console/src/app/modules/features/features.component.html new file mode 100644 index 000000000..1557775c4 --- /dev/null +++ b/console/src/app/modules/features/features.component.html @@ -0,0 +1,86 @@ + +

{{'POLICY.DEFAULTLABEL' | translate}}

+ + + + + +
+ +
+ {{'FEATURES.DATA.AUDITLOGRETENTION' | translate}} + + {{features.auditLogRetention | json }} +
+
+ {{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LOGINPOLICYIDP' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}} + + + +
+ +
+ {{'FEATURES.DATA.LABELPOLICY' | translate}} + + + +
+
+ +
+ +
+
\ No newline at end of file diff --git a/console/src/app/modules/features/features.component.scss b/console/src/app/modules/features/features.component.scss new file mode 100644 index 000000000..c90c5d0d0 --- /dev/null +++ b/console/src/app/modules/features/features.component.scss @@ -0,0 +1,41 @@ +.default { + color: var(--color-main); + margin-top: 0; +} + +.content { + padding-top: 1rem; + display: flex; + flex-direction: column; + width: 100%; + + .row { + display: flex; + align-items: center; + padding: .3rem 0; + + .left-desc { + font-size: .9rem; + } + + .fill-space { + flex: 1; + } + + .length-wrapper { + display: flex; + align-items: center; + } + } +} + +.btn-container { + display: flex; + justify-content: flex-end; + width: 100%; + + button { + margin-top: 3rem; + display: block; + } +} diff --git a/console/src/app/modules/features/features.component.spec.ts b/console/src/app/modules/features/features.component.spec.ts new file mode 100644 index 000000000..57da30615 --- /dev/null +++ b/console/src/app/modules/features/features.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { FeaturesComponent } from './features.component'; + +describe('FeaturesComponent', () => { + let component: FeaturesComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [FeaturesComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FeaturesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/features/features.component.ts b/console/src/app/modules/features/features.component.ts new file mode 100644 index 000000000..3684e032d --- /dev/null +++ b/console/src/app/modules/features/features.component.ts @@ -0,0 +1,144 @@ +import { Component, Injector, OnDestroy, Type } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Subscription } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { + GetOrgFeaturesResponse, + SetDefaultFeaturesRequest, + SetOrgFeaturesRequest, +} from 'src/app/proto/generated/zitadel/admin_pb'; +import { Features } from 'src/app/proto/generated/zitadel/features_pb'; +import { GetFeaturesResponse } from 'src/app/proto/generated/zitadel/management_pb'; +import { Org } from 'src/app/proto/generated/zitadel/org_pb'; +import { AdminService } from 'src/app/services/admin.service'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { StorageService } from 'src/app/services/storage.service'; +import { ToastService } from 'src/app/services/toast.service'; + +export enum FeatureServiceType { + MGMT, + ADMIN, +} + +@Component({ + selector: 'app-features', + templateUrl: './features.component.html', + styleUrls: ['./features.component.scss'], +}) +export class FeaturesComponent implements OnDestroy { + private managementService!: ManagementService; + public serviceType!: FeatureServiceType; + + public features!: Features.AsObject; + + private sub: Subscription = new Subscription(); + private org!: Org.AsObject; + + public FeatureServiceType: any = FeatureServiceType; + constructor( + private route: ActivatedRoute, + private toast: ToastService, + private sessionStorage: StorageService, + private injector: Injector, + private adminService: AdminService, + ) { + const temporg = this.sessionStorage.getItem('organization') as Org.AsObject; + if (temporg) { + this.org = temporg; + } + this.sub = this.route.data.pipe(switchMap(data => { + this.serviceType = data.serviceType; + if (this.serviceType === FeatureServiceType.MGMT) { + this.managementService = this.injector.get(ManagementService as Type); + } + return this.route.params; + })).subscribe(_ => { + this.fetchData(); + }); + } + + public ngOnDestroy(): void { + this.sub.unsubscribe(); + } + + public fetchData(): void { + this.getData().then(resp => { + if (resp?.features) { + this.features = resp.features; + } + }); + } + + private async getData(): Promise { + switch (this.serviceType) { + case FeatureServiceType.MGMT: + return this.managementService.getFeatures(); + case FeatureServiceType.ADMIN: + if (this.org?.id) { + return this.adminService.getDefaultFeatures(); + } + break; + } + } + + public savePolicy(): void { + switch (this.serviceType) { + case FeatureServiceType.MGMT: + const req = new SetOrgFeaturesRequest(); + req.setOrgId(this.org.id); + + req.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin); + req.setLoginPolicyRegistration(this.features.loginPolicyRegistration); + req.setLoginPolicyIdp(this.features.loginPolicyIdp); + req.setLoginPolicyFactors(this.features.loginPolicyFactors); + req.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless); + req.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy); + req.setLabelPolicy(this.features.labelPolicy); + + this.adminService.setOrgFeatures(req).then(() => { + this.toast.showInfo('POLICY.TOAST.SET', true); + }).catch(error => { + this.toast.showError(error); + }); + break; + case FeatureServiceType.ADMIN: + // update Default org iam policy? + const dreq = new SetDefaultFeaturesRequest(); + dreq.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin); + dreq.setLoginPolicyRegistration(this.features.loginPolicyRegistration); + dreq.setLoginPolicyIdp(this.features.loginPolicyIdp); + dreq.setLoginPolicyFactors(this.features.loginPolicyFactors); + dreq.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless); + dreq.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy); + dreq.setLabelPolicy(this.features.labelPolicy); + + this.adminService.setDefaultFeatures(dreq).then(() => { + this.toast.showInfo('POLICY.TOAST.SET', true); + }).catch(error => { + this.toast.showError(error); + }); + break; + } + } + + public resetFeatures(): void { + if (this.serviceType === FeatureServiceType.MGMT) { + this.adminService.resetOrgFeatures(this.org.id).then(() => { + this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true); + setTimeout(() => { + this.fetchData(); + }, 1000); + }).catch(error => { + this.toast.showError(error); + }); + } + } + + public get isDefault(): boolean { + if (this.features && this.serviceType === FeatureServiceType.MGMT) { + return this.features.isDefault; + } else { + return false; + } + } +} diff --git a/console/src/app/modules/features/features.module.ts b/console/src/app/modules/features/features.module.ts new file mode 100644 index 000000000..94ee7a465 --- /dev/null +++ b/console/src/app/modules/features/features.module.ts @@ -0,0 +1,42 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslateModule } from '@ngx-translate/core'; +import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module'; +import { InputModule } from 'src/app/modules/input/input.module'; +import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; + +import { InfoSectionModule } from '../info-section/info-section.module'; +import { FeaturesRoutingModule } from './features-routing.module'; +import { FeaturesComponent } from './features.component'; + +@NgModule({ + declarations: [ + FeaturesComponent + ], + imports: [ + FeaturesRoutingModule, + CommonModule, + FormsModule, + InputModule, + MatButtonModule, + HasRoleModule, + MatSlideToggleModule, + MatIconModule, + HasRoleModule, + HasRolePipeModule, + MatTooltipModule, + InfoSectionModule, + TranslateModule, + DetailLayoutModule, + ], + exports: [ + FeaturesComponent, + ] +}) +export class FeaturesModule { } diff --git a/console/src/app/modules/form-field/form-field.component.ts b/console/src/app/modules/form-field/form-field.component.ts index 55fecceff..4089c87fc 100644 --- a/console/src/app/modules/form-field/form-field.component.ts +++ b/console/src/app/modules/form-field/form-field.component.ts @@ -80,7 +80,6 @@ export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestr @HostListener('blur', ['false']) _focusChanged(isFocused: boolean): void { - console.log('blur1'); if (isFocused !== this.focused && (!isFocused)) { this.focused = isFocused; this.stateChanges.next(); diff --git a/console/src/app/modules/idp-table/idp-table.component.ts b/console/src/app/modules/idp-table/idp-table.component.ts index eb4aaf546..fa1da39af 100644 --- a/console/src/app/modules/idp-table/idp-table.component.ts +++ b/console/src/app/modules/idp-table/idp-table.component.ts @@ -153,7 +153,6 @@ export class IdpTableComponent implements OnInit { (this.service as ManagementService).listOrgIDPs(limit, offset).then(resp => { this.idpResult = resp; this.dataSource.data = resp.resultList; - console.log(resp.resultList); this.loadingSubject.next(false); }).catch(error => { this.toast.showError(error); @@ -163,7 +162,6 @@ export class IdpTableComponent implements OnInit { (this.service as AdminService).listIDPs(limit, offset).then(resp => { this.idpResult = resp; this.dataSource.data = resp.resultList; - console.log(resp.resultList); this.loadingSubject.next(false); }).catch(error => { diff --git a/console/src/app/modules/info-section/info-section.component.html b/console/src/app/modules/info-section/info-section.component.html index 0ca31edc2..4de828cb3 100644 --- a/console/src/app/modules/info-section/info-section.component.html +++ b/console/src/app/modules/info-section/info-section.component.html @@ -1,6 +1,6 @@
- +
diff --git a/console/src/app/modules/info-section/info-section.component.scss b/console/src/app/modules/info-section/info-section.component.scss index 0bc8c21f3..bd384f6a0 100644 --- a/console/src/app/modules/info-section/info-section.component.scss +++ b/console/src/app/modules/info-section/info-section.component.scss @@ -20,7 +20,6 @@ margin-left: .5rem; } - .info-section-content { flex: 1; } @@ -37,6 +36,10 @@ &.warn { background-color: if($is-dark-theme, #4f566b, #ffc1c1); color: if($is-dark-theme, #ffc1c1, #620e0e); + + .icon { + color: if($is-dark-theme, #ffc1c1, #620e0e); + } } } } diff --git a/console/src/app/modules/machine-keys/machine-keys.component.html b/console/src/app/modules/machine-keys/machine-keys.component.html index 6396bd4c5..bd6b25097 100644 --- a/console/src/app/modules/machine-keys/machine-keys.component.html +++ b/console/src/app/modules/machine-keys/machine-keys.component.html @@ -36,14 +36,14 @@ {{ 'USER.MACHINE.CREATIONDATE' | translate }} - {{key.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{key.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}} {{ 'USER.MACHINE.EXPIRATIONDATE' | translate }} - {{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{key.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}} diff --git a/console/src/app/modules/machine-keys/machine-keys.component.ts b/console/src/app/modules/machine-keys/machine-keys.component.ts index 84f574a20..ce3cc5d0c 100644 --- a/console/src/app/modules/machine-keys/machine-keys.component.ts +++ b/console/src/app/modules/machine-keys/machine-keys.component.ts @@ -85,7 +85,6 @@ export class MachineKeysComponent implements OnInit { if (resp.date as Moment) { const ts = new Timestamp(); - console.log(resp.date.toDate()); const milliseconds = resp.date.toDate().getTime(); const seconds = Math.abs(milliseconds / 1000); const nanos = (milliseconds - seconds * 1000) * 1000 * 1000; @@ -123,7 +122,6 @@ export class MachineKeysComponent implements OnInit { if (this.userId) { this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => { this.keyResult = resp; - console.log(resp); if (resp.resultList) { this.dataSource.data = resp.resultList; } diff --git a/console/src/app/modules/mfa-table/mfa-table.component.html b/console/src/app/modules/mfa-table/mfa-table.component.html index 563b785e1..f8ef5e1a3 100644 --- a/console/src/app/modules/mfa-table/mfa-table.component.html +++ b/console/src/app/modules/mfa-table/mfa-table.component.html @@ -10,7 +10,7 @@ {{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.': LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
-
+
add
\ No newline at end of file diff --git a/console/src/app/modules/mfa-table/mfa-table.component.scss b/console/src/app/modules/mfa-table/mfa-table.component.scss index fe0619902..3329d39ed 100644 --- a/console/src/app/modules/mfa-table/mfa-table.component.scss +++ b/console/src/app/modules/mfa-table/mfa-table.component.scss @@ -49,5 +49,10 @@ } } } + + &.disabled { + opacity: .5; + cursor: not-allowed; + } } } diff --git a/console/src/app/modules/mfa-table/mfa-table.component.ts b/console/src/app/modules/mfa-table/mfa-table.component.ts index 8e53c76ef..2b877b597 100644 --- a/console/src/app/modules/mfa-table/mfa-table.component.ts +++ b/console/src/app/modules/mfa-table/mfa-table.component.ts @@ -214,8 +214,6 @@ export class MfaTableComponent implements OnInit { }); } } - - console.log(this.mfas); } public refreshPageAfterTimout(to: number): void { diff --git a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts b/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts index e8f889426..bc7516289 100644 --- a/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts +++ b/console/src/app/modules/policies/login-policy/add-idp-dialog/add-idp-dialog.component.ts @@ -63,7 +63,6 @@ export class AddIdpDialogComponent { case IDPOwnerType.IDP_OWNER_TYPE_SYSTEM: this.adminService.listIDPs().then(resp => { this.availableIdps = resp.resultList; - console.log(resp); }); break; diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index 074251c7d..fd7969579 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -37,9 +37,17 @@ [(ngModel)]="loginData.allowRegister"> {{'POLICY.DATA.ALLOWREGISTER' | translate}} - - {{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}} - + + + {{'FEATURES.NOTAVAILABLE' | translate: ({value: + 'login_policy.registration'})}} + + + + + {{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}} + +
+ {{'LOGINPOLICY.PASSWORDLESS' | translate}} - + {{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}} + + {{'FEATURES.NOTAVAILABLE' | translate: ({value: + 'login_policy.passwordless'})}} + +
@@ -79,24 +94,40 @@

{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}

{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}

+ + {{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}} + + + [disabled]="(([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false) || ((['login_policy.factors'] | hasFeature | async) == false)">

{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}

{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}

+ + {{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}} + + + [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false || ((['login_policy.factors'] | hasFeature | async) == false)">

{{'LOGINPOLICY.IDPS' | translate}}

+ + {{'FEATURES.NOTAVAILABLE' | translate: ({value: + 'login_policy.idp'})}} + + +
-
- @@ -105,13 +136,15 @@ *ngIf="idp.stylingType == IDPStylingType.STYLING_TYPE_GOOGLE" alt="google" />
{{idp.name}} - {{ 'IDP.TYPE' | translate }}: {{ 'IDP.OWNERTYPES.'+idp.idpType | translate + {{ 'IDP.TYPE' | translate }}: {{ 'IDP.OWNERTYPES.'+idp.idpType | + translate }} {{ 'IDP.ID' | translate }}: {{idp.idpId}}
-
+
add
@@ -127,7 +160,7 @@ + [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false || (['login_policy.idp'] | hasFeature | async) == false"> diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.scss b/console/src/app/modules/policies/login-policy/login-policy.component.scss index ec8290ef7..71cafac5a 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.scss +++ b/console/src/app/modules/policies/login-policy/login-policy.component.scss @@ -11,12 +11,13 @@ padding-top: 1rem; .row { + padding-bottom: 1.5rem; + .toggle { margin: .3rem 0; } .info { - margin-bottom: 1rem; display: block; } } diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.ts b/console/src/app/modules/policies/login-policy/login-policy.component.ts index 3b49f0e80..dd697da0e 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.component.ts @@ -101,7 +101,6 @@ export class LoginPolicyComponent implements OnDestroy { }); this.getIdps().then(resp => { this.idps = resp; - console.log(resp); }); } @@ -144,7 +143,6 @@ export class LoginPolicyComponent implements OnDestroy { mgmtreq.setAllowUsernamePassword(this.loginData.allowUsernamePassword); mgmtreq.setForceMfa(this.loginData.forceMfa); mgmtreq.setPasswordlessType(this.loginData.passwordlessType); - // console.log(mgmtreq.toObject()); if ((this.loginData as LoginPolicy.AsObject).isDefault) { return (this.service as ManagementService).addCustomLoginPolicy(mgmtreq); } else { @@ -158,8 +156,6 @@ export class LoginPolicyComponent implements OnDestroy { adminreq.setForceMfa(this.loginData.forceMfa); adminreq.setPasswordlessType(this.loginData.passwordlessType); - // console.log(adminreq.toObject()); - return (this.service as AdminService).updateLoginPolicy(adminreq); } } diff --git a/console/src/app/modules/policies/login-policy/login-policy.module.ts b/console/src/app/modules/policies/login-policy/login-policy.module.ts index f4186d21c..1630ba7ac 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.module.ts +++ b/console/src/app/modules/policies/login-policy/login-policy.module.ts @@ -15,10 +15,11 @@ import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout. import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module'; import { InputModule } from 'src/app/modules/input/input.module'; import { MfaTableModule } from 'src/app/modules/mfa-table/mfa-table.module'; +import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; + import { InfoSectionModule } from '../../info-section/info-section.module'; import { LinksModule } from '../../links/links.module'; - import { AddIdpDialogModule } from './add-idp-dialog/add-idp-dialog.module'; import { LoginPolicyRoutingModule } from './login-policy-routing.module'; import { LoginPolicyComponent } from './login-policy.component'; @@ -33,6 +34,7 @@ import { LoginPolicyComponent } from './login-policy.component'; CardModule, InputModule, MatButtonModule, + HasFeaturePipeModule, MatSlideToggleModule, MatIconModule, HasRoleModule, diff --git a/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts b/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts index 116b78ae7..a9b11d5a4 100644 --- a/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts +++ b/console/src/app/modules/policies/org-iam-policy/org-iam-policy.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, Input, OnDestroy, Type } from '@angular/core'; +import { Component, Injector, OnDestroy, Type } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @@ -10,6 +10,7 @@ import { AdminService } from 'src/app/services/admin.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { StorageService } from 'src/app/services/storage.service'; import { ToastService } from 'src/app/services/toast.service'; + import { CnslLinks } from '../../links/links.component'; import { IAM_COMPLEXITY_LINK, @@ -26,7 +27,6 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum'; styleUrls: ['./org-iam-policy.component.scss'], }) export class OrgIamPolicyComponent implements OnDestroy { - @Input() service!: AdminService; private managementService!: ManagementService; public serviceType!: PolicyComponentServiceType; diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts index 9c9f66f95..0fedeb489 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts @@ -77,7 +77,6 @@ export class PasswordComplexityPolicyComponent implements OnDestroy { this.getData().then(data => { if (data.policy) { this.complexityData = data.policy; - console.log(data.policy); this.loading = false; } }); diff --git a/console/src/app/modules/policy-grid/policy-grid.component.html b/console/src/app/modules/policy-grid/policy-grid.component.html index a1ea023a2..f0ad7b8be 100644 --- a/console/src/app/modules/policy-grid/policy-grid.component.html +++ b/console/src/app/modules/policy-grid/policy-grid.component.html @@ -57,10 +57,16 @@

{{'POLICY.IAM_POLICY.DESCRIPTION' | translate}}

+ + {{'FEATURES.NOTAVAILABLE' | translate: ({value: + 'password_complexity_policy'})}} + +
- @@ -111,12 +117,18 @@

{{'POLICY.LABEL.DESCRIPTION' | translate}}

+ + {{'FEATURES.NOTAVAILABLE' | translate: ({value: + 'label_policy'})}} + +
+ mat-stroked-button [disabled]="(['label_policy'] | hasFeature | async) == false"> + {{'POLICY.BTN_EDIT' | translate}}
diff --git a/console/src/app/modules/policy-grid/policy-grid.component.scss b/console/src/app/modules/policy-grid/policy-grid.component.scss index a8cc65d20..8f2383b0a 100644 --- a/console/src/app/modules/policy-grid/policy-grid.component.scss +++ b/console/src/app/modules/policy-grid/policy-grid.component.scss @@ -6,6 +6,7 @@ h2 { .top-desc { color: var(--grey); + font-size: 14px; } .row-lyt { @@ -65,6 +66,10 @@ h2 { color: var(--grey); } + .warn { + margin-bottom: .5rem; + } + .icons { margin-bottom: 1rem; .icon { diff --git a/console/src/app/modules/policy-grid/policy-grid.module.ts b/console/src/app/modules/policy-grid/policy-grid.module.ts index 6dcd601fd..1adc8df62 100644 --- a/console/src/app/modules/policy-grid/policy-grid.module.ts +++ b/console/src/app/modules/policy-grid/policy-grid.module.ts @@ -6,8 +6,10 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; +import { InfoSectionModule } from '../info-section/info-section.module'; import { PolicyGridComponent } from './policy-grid.component'; @NgModule({ @@ -21,6 +23,8 @@ import { PolicyGridComponent } from './policy-grid.component'; MatButtonModule, MatIconModule, MatTooltipModule, + InfoSectionModule, + HasFeaturePipeModule, ], exports: [ PolicyGridComponent, diff --git a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts index 8a04ccae1..2a01b1eb3 100644 --- a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts +++ b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts @@ -62,7 +62,6 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec } else if (this.target === UserTarget.SELF) { this.getFilteredResults(); // new subscription } - console.log(this.users); } public ngAfterContentChecked(): void { @@ -90,7 +89,6 @@ export class SearchUserAutocompleteComponent implements OnInit, AfterContentChec }), ).subscribe((userresp: ListUsersResponse.AsObject | unknown) => { this.isLoading = false; - console.log(userresp); if (this.target === UserTarget.SELF && userresp) { this.filteredUsers = (userresp as ListUsersResponse.AsObject).resultList; } diff --git a/console/src/app/modules/user-grants/user-grants-datasource.ts b/console/src/app/modules/user-grants/user-grants-datasource.ts index 61b1f63d5..ddb4a3ad6 100644 --- a/console/src/app/modules/user-grants/user-grants-datasource.ts +++ b/console/src/app/modules/user-grants/user-grants-datasource.ts @@ -128,7 +128,6 @@ export class UserGrantsDataSource extends DataSource { catchError(() => of([])), finalize(() => this.loadingSubject.next(false)), ).subscribe(grants => { - console.log(grants); this.grantsSubject.next(grants); }); } diff --git a/console/src/app/modules/user-grants/user-grants.component.ts b/console/src/app/modules/user-grants/user-grants.component.ts index da29197cb..42b446bc7 100644 --- a/console/src/app/modules/user-grants/user-grants.component.ts +++ b/console/src/app/modules/user-grants/user-grants.component.ts @@ -182,7 +182,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { } private getGrantRoleOptions(id: string, projectId: string): void { - console.log(projectId, id); this.mgmtService.getGrantedProjectByID(projectId, id).then(resp => { if (resp.grantedProject) { this.loadedId = id; diff --git a/console/src/app/pages/iam/iam-routing.module.ts b/console/src/app/pages/iam/iam-routing.module.ts index 08d609bbd..e8a011320 100644 --- a/console/src/app/pages/iam/iam-routing.module.ts +++ b/console/src/app/pages/iam/iam-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuard } from 'src/app/guards/auth.guard'; import { RoleGuard } from 'src/app/guards/role.guard'; +import { FeatureServiceType } from 'src/app/modules/features/features.component'; import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum'; import { EventstoreComponent } from './eventstore/eventstore.component'; @@ -32,6 +33,15 @@ const routes: Routes = [ roles: ['iam.member.read'], }, }, + { + path: 'features', + loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule), + // canActivate: [RoleGuard], + data: { + roles: ['iam.features.read'], + serviceType: FeatureServiceType.ADMIN + }, + }, { path: 'idp', children: [ diff --git a/console/src/app/pages/iam/iam.component.html b/console/src/app/pages/iam/iam.component.html index fdd125fe3..d67b39c91 100644 --- a/console/src/app/pages/iam/iam.component.html +++ b/console/src/app/pages/iam/iam.component.html @@ -3,6 +3,22 @@

{{'IAM.POLICIES.TITLE' | translate}}

{{'IAM.POLICIES.DESCRIPTION' | translate}}

+

{{'FEATURES.TITLE' | translate}}

+

{{'FEATURES.DESCRIPTION' | translate}}

+
+ stars +
+

ZITADEL {{features.tier.name}}

+

{{features.tier?.description}}

+

{{'FEATURES.TIERSTATES.'+features.tier.state | translate}}

+

{{features.tier?.statusInfo}}

+
+ + + +
+ diff --git a/console/src/app/pages/iam/iam.component.scss b/console/src/app/pages/iam/iam.component.scss index 3f7ee0cbd..36859cd54 100644 --- a/console/src/app/pages/iam/iam.component.scss +++ b/console/src/app/pages/iam/iam.component.scss @@ -6,3 +6,48 @@ color: var(--grey); margin-bottom: 2rem; } + +.top-desc { + color: var(--grey); + font-size: 14px; +} + +.tier { + display: flex; + flex-direction: row; + padding: 1rem; + border-radius: .5rem; + color: white; + background-color: rgb(245, 203, 99); + margin-bottom: .5rem; + + .ext { + margin-right: .5rem; + align-self: center; + color: inherit; + } + + mat-icon { + margin-right: 1rem; + font-size: 2rem; + } + + .text { + display: flex; + flex-direction: column; + p { + margin: 0; + font-size: 14px; + } + .title { + font-size: 16px; + margin-bottom: .5rem; + } + + + } + + .fill-space { + flex: 1; + } +} \ No newline at end of file diff --git a/console/src/app/pages/iam/iam.component.ts b/console/src/app/pages/iam/iam.component.ts index ad4a4eedf..557cb560c 100644 --- a/console/src/app/pages/iam/iam.component.ts +++ b/console/src/app/pages/iam/iam.component.ts @@ -6,6 +6,7 @@ import { catchError, finalize, map } from 'rxjs/operators'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component'; +import { Features } from 'src/app/proto/generated/zitadel/features_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb'; import { User } from 'src/app/proto/generated/zitadel/user_pb'; import { AdminService } from 'src/app/services/admin.service'; @@ -25,10 +26,13 @@ export class IamComponent { = new BehaviorSubject([]); public PolicyGridType: any = PolicyGridType; + public features!: Features.AsObject; constructor(public adminService: AdminService, private dialog: MatDialog, private toast: ToastService, private router: Router) { this.loadMembers(); + this.loadFeatures(); + this.adminService.getDefaultFeatures(); } public loadMembers(): void { @@ -79,4 +83,13 @@ export class IamComponent { public showDetail(): void { this.router.navigate(['iam/members']); } + + public loadFeatures(): void { + this.loadingSubject.next(true); + this.adminService.getDefaultFeatures().then(resp => { + if (resp.features) { + this.features = resp.features; + } + }); + } } diff --git a/console/src/app/pages/orgs/org-create/org-create.component.ts b/console/src/app/pages/orgs/org-create/org-create.component.ts index 19597c3da..a0e7f4d5e 100644 --- a/console/src/app/pages/orgs/org-create/org-create.component.ts +++ b/console/src/app/pages/orgs/org-create/org-create.component.ts @@ -196,7 +196,6 @@ export class OrgCreateComponent { name: ['', [Validators.required]], }); - console.log(this.orgForm); } else { this.createSteps = 2; diff --git a/console/src/app/pages/orgs/org-detail/org-detail.component.html b/console/src/app/pages/orgs/org-detail/org-detail.component.html index 481e5aca5..31c0217b4 100644 --- a/console/src/app/pages/orgs/org-detail/org-detail.component.html +++ b/console/src/app/pages/orgs/org-detail/org-detail.component.html @@ -28,6 +28,23 @@ +

{{'FEATURES.TITLE' | translate}}

+

{{'FEATURES.DESCRIPTION' | translate}}

+
+ stars +
+

ZITADEL {{features.tier.name}}

+

{{features.tier?.description}}

+

{{'FEATURES.TIERSTATES.'+features.tier.state | translate}}

+

{{features.tier?.statusInfo}}

+
+ + + + +
+ diff --git a/console/src/app/pages/orgs/org-detail/org-detail.component.scss b/console/src/app/pages/orgs/org-detail/org-detail.component.scss index 63c399b7a..cccb7800d 100644 --- a/console/src/app/pages/orgs/org-detail/org-detail.component.scss +++ b/console/src/app/pages/orgs/org-detail/org-detail.component.scss @@ -2,11 +2,62 @@ margin-top: 0; } +h2 { + font-size: 1.2rem; + letter-spacing: .05em; + text-transform: uppercase; +} + +.top-desc { + color: var(--grey); + font-size: 14px; +} + .sub { color: var(--grey); margin-bottom: 2rem; } +.tier { + display: flex; + flex-direction: row; + padding: 1rem; + border-radius: .5rem; + color: white; + background-color: rgb(245, 203, 99); + margin-bottom: .5rem; + + .ext { + margin-right: .5rem; + align-self: center; + color: inherit; + } + + mat-icon { + margin-right: 1rem; + font-size: 2rem; + } + + .text { + display: flex; + flex-direction: column; + p { + margin: 0; + font-size: 14px; + } + .title { + font-size: 16px; + margin-bottom: .5rem; + } + + + } + + .fill-space { + flex: 1; + } +} + .domain { display: flex; align-items: center; diff --git a/console/src/app/pages/orgs/org-detail/org-detail.component.ts b/console/src/app/pages/orgs/org-detail/org-detail.component.ts index 4235c2212..79fbf067b 100644 --- a/console/src/app/pages/orgs/org-detail/org-detail.component.ts +++ b/console/src/app/pages/orgs/org-detail/org-detail.component.ts @@ -10,6 +10,7 @@ import { ChangeType } from 'src/app/modules/changes/changes.component'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; +import { Features } from 'src/app/proto/generated/zitadel/features_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb'; import { Domain, Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb'; import { User } from 'src/app/proto/generated/zitadel/user_pb'; @@ -43,13 +44,17 @@ export class OrgDetailComponent implements OnInit { = new BehaviorSubject([]); public PolicyGridType: any = PolicyGridType; + public features!: Features.AsObject; + constructor( private dialog: MatDialog, public translate: TranslateService, public mgmtService: ManagementService, private toast: ToastService, private router: Router, - ) { } + ) { + + } public ngOnInit(): void { this.getData(); @@ -65,12 +70,12 @@ export class OrgDetailComponent implements OnInit { }); this.loadMembers(); this.loadDomains(); + this.loadFeatures(); } public loadDomains(): void { this.mgmtService.listOrgDomains().then(result => { this.domains = result.resultList; - console.log(this.domains); this.primaryDomain = this.domains.find(domain => domain.isPrimary)?.domainName ?? ''; }); } @@ -208,4 +213,13 @@ export class OrgDetailComponent implements OnInit { this.membersSubject.next(members); }); } + + public loadFeatures(): void { + this.loadingSubject.next(true); + this.mgmtService.getFeatures().then(resp => { + if (resp.features) { + this.features = resp.features; + } + }); + } } diff --git a/console/src/app/pages/orgs/orgs-routing.module.ts b/console/src/app/pages/orgs/orgs-routing.module.ts index 0484c2676..9c150ab31 100644 --- a/console/src/app/pages/orgs/orgs-routing.module.ts +++ b/console/src/app/pages/orgs/orgs-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { RoleGuard } from 'src/app/guards/role.guard'; +import { FeatureServiceType } from 'src/app/modules/features/features.component'; import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum'; import { OrgCreateComponent } from './org-create/org-create.component'; @@ -39,6 +40,15 @@ const routes: Routes = [ }, ], }, + { + path: 'features', + loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule), + canActivate: [RoleGuard], + data: { + roles: ['features.read'], + serviceType: FeatureServiceType.MGMT, + }, + }, { path: 'policy', children: [ diff --git a/console/src/app/pages/orgs/orgs.module.ts b/console/src/app/pages/orgs/orgs.module.ts index 5bb283c3d..2a295dcc1 100644 --- a/console/src/app/pages/orgs/orgs.module.ts +++ b/console/src/app/pages/orgs/orgs.module.ts @@ -15,6 +15,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module'; import { CardModule } from 'src/app/modules/card/card.module'; import { ContributorsModule } from 'src/app/modules/contributors/contributors.module'; +import { FeaturesModule } from 'src/app/modules/features/features.module'; import { InputModule } from 'src/app/modules/input/input.module'; import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module'; import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module'; @@ -57,6 +58,7 @@ import { OrgsRoutingModule } from './orgs-routing.module'; ContributorsModule, CopyToClipboardModule, PolicyGridModule, + FeaturesModule, ], }) export class OrgsModule { } diff --git a/console/src/app/pages/projects/apps/app-create/app-create.component.ts b/console/src/app/pages/projects/apps/app-create/app-create.component.ts index e932cfd26..ceb1b570f 100644 --- a/console/src/app/pages/projects/apps/app-create/app-create.component.ts +++ b/console/src/app/pages/projects/apps/app-create/app-create.component.ts @@ -273,7 +273,6 @@ export class AppCreateComponent implements OnInit, OnDestroy { private async getData({ projectid }: Params): Promise { this.projectId = projectid; - console.log(this.projectId); this.oidcAppRequest.projectId = projectid; this.apiAppRequest.projectId = projectid; } diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index 41d823afb..de619cada 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -39,13 +39,13 @@
- - {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}} + {{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_INACTIVE | translate}} - {{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}} + {{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_ACTIVE | translate}} diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts index 94b81fc8e..2001818ce 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.ts @@ -211,7 +211,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { this.mgmtService.getAppByID(projectid, id).then(app => { if (app.app) { this.app = app.app; - console.log(this.app); this.appNameForm.patchValue(this.app); if (this.app.oidcConfig) { @@ -301,7 +300,6 @@ export class AppDetailComponent implements OnInit, OnDestroy { const snackRef = this.snackbar.open(message, action, { duration: 5000, verticalPosition: 'top' }); snackRef.onAction().subscribe(() => { - console.log(this.app); if (this.app.oidcConfig) { this.saveOIDCApp(); } else if (this.app.apiConfig) { diff --git a/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html b/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html index 4b579335f..f11451155 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html +++ b/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html @@ -29,7 +29,7 @@
{{'RESOURCEID' | translate}}: - {{ projectId }} + {{ project?.grantId }}
{{'PROJECT.STATE.TITLE' | translate}}: diff --git a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.ts b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.ts index 676bf107c..7cfacf24d 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.ts +++ b/console/src/app/pages/projects/granted-projects/granted-project-list/granted-project-list.component.ts @@ -91,7 +91,6 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy { this.loadingSubject.next(true); this.mgmtService.listGrantedProjects(limit, offset).then(resp => { this.grantedProjectList = resp.resultList; - console.log(this.grantedProjectList); if (resp.details?.totalResult) { this.totalResult = resp.details.totalResult; } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts index 2b2d9205d..d52745e2f 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/application-grid/application-grid.component.ts @@ -27,13 +27,11 @@ export class ApplicationGridComponent implements OnInit { public loadApps(): void { from(this.mgmtService.listApps(this.projectId, 100, 0)).pipe( map(resp => { - console.log(resp.resultList); return resp.resultList; }), // catchError(() => of([])), finalize(() => this.loadingSubject.next(false)), ).subscribe((apps) => { - console.log(apps); this.appsSubject.next(apps as App.AsObject[]); }); } diff --git a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts index a341225ac..a6e38d2ca 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts +++ b/console/src/app/pages/projects/owned-projects/owned-project-list/owned-project-list.component.ts @@ -109,7 +109,6 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy { private async getData(limit?: number, offset?: number): Promise { this.loadingSubject.next(true); this.mgmtService.listProjects(limit, offset).then(resp => { - console.log(resp.resultList); this.ownedProjectList = resp.resultList; if (resp.details?.totalResult) { this.totalResult = resp.details.totalResult; diff --git a/console/src/app/pages/user-grant-create/user-grant-create.component.ts b/console/src/app/pages/user-grant-create/user-grant-create.component.ts index 543246ef4..89f3a5aa4 100644 --- a/console/src/app/pages/user-grant-create/user-grant-create.component.ts +++ b/console/src/app/pages/user-grant-create/user-grant-create.component.ts @@ -95,10 +95,6 @@ export class UserGrantCreateComponent implements OnDestroy { public addGrant(): void { switch (this.context) { case UserGrantContext.OWNED_PROJECT: - console.log('owned', this.userId, - this.rolesList, - this.projectId, - this.grantId); this.userService.addUserGrant( this.userId, this.rolesList, @@ -111,11 +107,6 @@ export class UserGrantCreateComponent implements OnDestroy { }); break; case UserGrantContext.GRANTED_PROJECT: - - console.log('granted', this.userId, - this.rolesList, - this.projectId, - this.grantId); this.userService.addUserGrant( this.userId, this.rolesList, @@ -135,11 +126,6 @@ export class UserGrantCreateComponent implements OnDestroy { grantId = (this.project as GrantedProject.AsObject).grantId; } - console.log(this.userId, - this.rolesList, - this.projectId, - grantId); - this.userService.addUserGrant( this.userId, this.rolesList, diff --git a/console/src/app/pages/users/user-create/user-create.component.ts b/console/src/app/pages/users/user-create/user-create.component.ts index 863546635..4749b4a0b 100644 --- a/console/src/app/pages/users/user-create/user-create.component.ts +++ b/console/src/app/pages/users/user-create/user-create.component.ts @@ -10,24 +10,6 @@ import { Gender } from 'src/app/proto/generated/zitadel/user_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; -function noEmailValidator(c: AbstractControl): any { - const EMAIL_REGEXP: RegExp = /^((?!@).)*$/gm; - if (!c.parent || !c) { - return; - } - const username = c.parent.get('userName'); - - if (!username) { - return; - } - - return EMAIL_REGEXP.test(username.value) ? null : { - noEmailValidator: { - valid: false, - }, - }; -} - @Component({ selector: 'app-user-create', templateUrl: './user-create.component.html', @@ -88,7 +70,6 @@ export class UserCreateComponent implements OnDestroy { [ Validators.required, Validators.minLength(2), - this.userLoginMustBeDomain ? noEmailValidator : Validators.email, ], ], firstName: ['', Validators.required], diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-factor-dialog/auth-factor-dialog.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-factor-dialog/auth-factor-dialog.component.ts index 77d2190e6..527cbcf30 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-factor-dialog/auth-factor-dialog.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-factor-dialog/auth-factor-dialog.component.ts @@ -56,7 +56,6 @@ export class AuthFactorDialogComponent { const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string)); if (credOptions.publicKey?.challenge) { - console.log(credOptions.publicKey); credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any); credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any); if (credOptions.publicKey.excludeCredentials) { @@ -93,13 +92,12 @@ export class AuthFactorDialogComponent { public submitU2F(): void { if (this.u2fname && this.u2fCredentialOptions.publicKey) { // this.data.credOptions.publicKey.rp.id = 'localhost'; - navigator.credentials.create(this.data.credOptions).then((resp) => { + navigator.credentials.create(this.u2fCredentialOptions).then((resp) => { if (resp && (resp as any).response.attestationObject && (resp as any).response.clientDataJSON && (resp as any).rawId) { - console.log(resp); const attestationObject = (resp as any).response.attestationObject; const clientDataJSON = (resp as any).response.clientDataJSON; const rawId = (resp as any).rawId; diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts index 6600345a5..1b2ad01f0 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts @@ -53,7 +53,6 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy { public addPasswordless(): void { this.service.addMyPasswordless().then((resp) => { if (resp.key) { - console.log(resp.key); const credOptions: CredentialCreationOptions = JSON.parse(atob(resp.key.publicKey as string)); if (credOptions.publicKey?.challenge) { diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html index 611f688bc..ccf1d17d4 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html @@ -44,9 +44,8 @@ refresh diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts index 2d7cf3827..562453582 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts @@ -182,7 +182,6 @@ export class AuthUserDetailComponent implements OnDestroy { }); break; case EditDialogType.EMAIL: - console.log('email'); const dialogRefEmail = this.dialog.open(EditDialogComponent, { data: { confirmKey: 'ACTIONS.SAVE', diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts index ae64f58c2..55edf4590 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts @@ -72,7 +72,6 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy { public getMFAs(): void { this.service.listMyMultiFactors().then(mfas => { const list = mfas.resultList; - console.log(list); this.dataSource = new MatTableDataSource(list); this.dataSource.sort = this.sort; diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.ts index 75f298f91..b4fd6b853 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.ts @@ -25,7 +25,6 @@ export class EditDialogComponent { } this.valueControl.valueChanges.subscribe(value => { - console.log(value); if (value && value.length > 1) { this.changeValue(value); } diff --git a/console/src/app/pages/users/user-detail/contact/contact.component.html b/console/src/app/pages/users/user-detail/contact/contact.component.html index 2cc2008eb..e2ccefb5a 100644 --- a/console/src/app/pages/users/user-detail/contact/contact.component.html +++ b/console/src/app/pages/users/user-detail/contact/contact.component.html @@ -25,8 +25,7 @@ {{'USER.NOTVERIFIED' | translate}} - {{'USER.LOGINMETHODS.RESENDCODE' | translate}}
@@ -35,7 +34,8 @@
- diff --git a/console/src/app/pages/users/user-detail/contact/contact.component.ts b/console/src/app/pages/users/user-detail/contact/contact.component.ts index ce7d4a28f..a38d4c6a9 100644 --- a/console/src/app/pages/users/user-detail/contact/contact.component.ts +++ b/console/src/app/pages/users/user-detail/contact/contact.component.ts @@ -21,7 +21,7 @@ export class ContactComponent { @Output() resendPhoneVerification: EventEmitter = new EventEmitter(); @Output() enteredPhoneCode: EventEmitter = new EventEmitter(); @Output() deletedPhone: EventEmitter = new EventEmitter(); - @Input() public userStateEnum: any; + public UserState: any = UserState; public EditDialogType: any = EditDialogType; constructor(private dialog: MatDialog) { } diff --git a/console/src/app/pages/users/user-detail/membership-detail/membership-detail.component.html b/console/src/app/pages/users/user-detail/membership-detail/membership-detail.component.html index 38a610520..5f07611d3 100644 --- a/console/src/app/pages/users/user-detail/membership-detail/membership-detail.component.html +++ b/console/src/app/pages/users/user-detail/membership-detail/membership-detail.component.html @@ -27,7 +27,10 @@ {{ 'USER.MEMBERSHIPS.TYPE' | translate }} - {{'USER.MEMBERSHIPS.TYPES.' + member.memberType | translate }} + {{'USER.MEMBERSHIPS.TYPES.ORG' | translate }} + {{'USER.MEMBERSHIPS.TYPES.PROJECT' | translate }} + {{'USER.MEMBERSHIPS.TYPES.GRANTEDPROJECT' | translate }} + @@ -39,13 +42,13 @@ {{ 'USER.MEMBERSHIPS.CREATIONDATE' | translate }} - {{member.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{member.details?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} {{ 'USER.MEMBERSHIPS.CHANGEDATE' | translate }} - {{member.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} + {{member.details?.changeDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}} diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index 2d68b3e21..8bfd091c4 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -52,7 +52,6 @@ export class UserDetailComponent implements OnInit { this.mgmtUserService.getUserByID(id).then(resp => { if (resp.user) { this.user = resp.user; - console.log(this.user); } }).catch(err => { console.error(err); @@ -83,7 +82,6 @@ export class UserDetailComponent implements OnInit { } public saveProfile(profileData: Profile.AsObject): void { - console.log(profileData); if (this.user.human) { this.user.human.profile = profileData; this.mgmtUserService @@ -159,7 +157,6 @@ export class UserDetailComponent implements OnInit { this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => { this.toast.showInfo('USER.TOAST.EMAILSAVED', true); if (this.user.state == UserState.USER_STATE_INITIAL) { - console.log('init'); this.mgmtUserService.resendHumanInitialization(this.user.id, email ?? '').then(() => { this.toast.showInfo('USER.TOAST.INITEMAILSENT', true); this.refreshChanges$.emit(); diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.ts b/console/src/app/pages/users/user-list/user-table/user-table.component.ts index 3822bdcae..ba4b64f9d 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.ts +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.ts @@ -180,8 +180,6 @@ export class UserTableComponent implements OnInit { } this.userService.listUsers(limit, offset, [query]).then(resp => { - console.log(resp); - if (resp.details?.totalResult) { this.totalResult = resp.details?.totalResult; } diff --git a/console/src/app/pipes/has-feature-pipe/has-feature-pipe.module.ts b/console/src/app/pipes/has-feature-pipe/has-feature-pipe.module.ts new file mode 100644 index 000000000..55cf88399 --- /dev/null +++ b/console/src/app/pipes/has-feature-pipe/has-feature-pipe.module.ts @@ -0,0 +1,18 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { HasFeaturePipe } from './has-feature.pipe'; + + +@NgModule({ + declarations: [ + HasFeaturePipe, + ], + imports: [ + CommonModule, + ], + exports: [ + HasFeaturePipe, + ], +}) +export class HasFeaturePipeModule { } diff --git a/console/src/app/pipes/has-feature-pipe/has-feature.pipe.ts b/console/src/app/pipes/has-feature-pipe/has-feature.pipe.ts new file mode 100644 index 000000000..947426cde --- /dev/null +++ b/console/src/app/pipes/has-feature-pipe/has-feature.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Observable } from 'rxjs'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; + +@Pipe({ + name: 'hasFeature', +}) +export class HasFeaturePipe implements PipeTransform { + constructor(private authService: GrpcAuthService) { } + + public transform(values: string[]): Observable { + return this.authService.canUseFeature(values); + } +} diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts index 7bfbe92cd..6eb4b39f8 100644 --- a/console/src/app/services/admin.service.ts +++ b/console/src/app/services/admin.service.ts @@ -19,12 +19,16 @@ import { DeactivateIDPResponse, GetCustomOrgIAMPolicyRequest, GetCustomOrgIAMPolicyResponse, + GetDefaultFeaturesRequest, + GetDefaultFeaturesResponse, GetIDPByIDRequest, GetIDPByIDResponse, GetLabelPolicyRequest, GetLabelPolicyResponse, GetLoginPolicyRequest, GetLoginPolicyResponse, + GetOrgFeaturesRequest, + GetOrgFeaturesResponse, GetOrgIAMPolicyRequest, GetOrgIAMPolicyResponse, GetPasswordAgePolicyRequest, @@ -66,6 +70,12 @@ import { RemoveSecondFactorFromLoginPolicyResponse, ResetCustomOrgIAMPolicyToDefaultRequest, ResetCustomOrgIAMPolicyToDefaultResponse, + ResetOrgFeaturesRequest, + ResetOrgFeaturesResponse, + SetDefaultFeaturesRequest, + SetDefaultFeaturesResponse, + SetOrgFeaturesRequest, + SetOrgFeaturesResponse, SetUpOrgRequest, SetUpOrgResponse, UpdateCustomOrgIAMPolicyRequest, @@ -167,6 +177,33 @@ export class AdminService { return this.grpcService.admin.removeFailedEvent(req, null).then(resp => resp.toObject());; } + // Features + + public getOrgFeatures(orgId: string): Promise { + const req = new GetOrgFeaturesRequest(); + req.setOrgId(orgId); + return this.grpcService.admin.getOrgFeatures(req, null).then(resp => resp.toObject()); + } + + public setOrgFeatures(req: SetOrgFeaturesRequest): Promise { + return this.grpcService.admin.setOrgFeatures(req, null).then(resp => resp.toObject()); + } + + public resetOrgFeatures(orgId: string): Promise { + const req = new ResetOrgFeaturesRequest(); + req.setOrgId(orgId); + return this.grpcService.admin.resetOrgFeatures(req, null).then(resp => resp.toObject()); + } + + public getDefaultFeatures(): Promise { + const req = new GetDefaultFeaturesRequest(); + return this.grpcService.admin.getDefaultFeatures(req, null).then(resp => resp.toObject()); + } + + public setDefaultFeatures(req: SetDefaultFeaturesRequest): Promise { + return this.grpcService.admin.setDefaultFeatures(req, null).then(resp => resp.toObject()); + } + /* Policies */ /* complexity */ diff --git a/console/src/app/services/grpc-auth.service.ts b/console/src/app/services/grpc-auth.service.ts index acb183f1a..b2c05e116 100644 --- a/console/src/app/services/grpc-auth.service.ts +++ b/console/src/app/services/grpc-auth.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs'; -import { catchError, filter, finalize, first, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators'; +import { catchError, filter, finalize, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators'; import { AddMyAuthFactorOTPRequest, @@ -34,6 +34,8 @@ import { ListMyUserGrantsResponse, ListMyUserSessionsRequest, ListMyUserSessionsResponse, + ListMyZitadelFeaturesRequest, + ListMyZitadelFeaturesResponse, ListMyZitadelPermissionsRequest, ListMyZitadelPermissionsResponse, RemoveMyAuthFactorOTPRequest, @@ -81,7 +83,10 @@ export class GrpcAuthService { private _activeOrgChanged: Subject = new Subject(); public user!: Observable; private zitadelPermissions: BehaviorSubject = new BehaviorSubject(['user.resourceowner']); + private zitadelFeatures: BehaviorSubject = new BehaviorSubject(['']); + public readonly fetchedZitadelPermissions: BehaviorSubject = new BehaviorSubject(false as boolean); + public readonly fetchedZitadelFeatures: BehaviorSubject = new BehaviorSubject(false as boolean); private cachedOrgs: Org.AsObject[] = []; @@ -114,11 +119,13 @@ export class GrpcAuthService { }), finalize(() => { this.loadPermissions(); + this.loadFeatures(); }), ); this.activeOrgChanged.subscribe(() => { this.loadPermissions(); + this.loadFeatures(); }); } @@ -164,12 +171,7 @@ export class GrpcAuthService { } private loadPermissions(): void { - merge([ - // this.authenticationChanged, - this.activeOrgChanged.pipe(map(org => !!org)), - ]).pipe( - first(), - switchMap(() => from(this.listMyZitadelPermissions())), + from(this.listMyZitadelPermissions()).pipe( map(rolesResp => rolesResp.resultList), catchError(_ => { return of([]); @@ -182,6 +184,20 @@ export class GrpcAuthService { }); } + private loadFeatures(): void { + from(this.listMyZitadelFeatures()).pipe( + map(featuresResp => featuresResp.resultList), + catchError(_ => { + return of([]); + }), + finalize(() => { + this.fetchedZitadelFeatures.next(true); + }), + ).subscribe(features => { + this.zitadelFeatures.next(features); + }); + } + /** * returns true if user has one of the provided roles * @param roles roles of the user @@ -207,6 +223,31 @@ export class GrpcAuthService { }) > -1; } + /** + * returns true if user has one of the provided features + * @param features regex of the user + */ + public canUseFeature(features: string[] | RegExp[]): Observable { + if (features && features.length > 0) { + return this.zitadelPermissions.pipe(switchMap(zFeatures => of(this.hasFeature(zFeatures, features)))); + } else { + return of(false); + } + } + + /** + * returns true if user has one of the provided features + * @param userFeature features of the user + * @param requestedFeature required features for accessing the respective component + */ + public hasFeature(userFeatures: string[], requestedFeatures: string[] | RegExp[]): boolean { + return requestedFeatures.findIndex((regexp: any) => { + return userFeatures.findIndex(feature => { + return new RegExp(regexp).test(feature); + }) > -1; + }) > -1; + } + public getMyProfile(): Promise { return this.grpcService.auth.getMyProfile(new GetMyProfileRequest(), null).then(resp => resp.toObject()); } @@ -329,6 +370,12 @@ export class GrpcAuthService { ).then(resp => resp.toObject()); } + public listMyZitadelFeatures(): Promise { + return this.grpcService.auth.listMyZitadelFeatures( + new ListMyZitadelFeaturesRequest(), null + ).then(resp => resp.toObject()); + } + public getMyPhone(): Promise { return this.grpcService.auth.getMyPhone( new GetMyPhoneRequest(), null diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index 31136453d..f39706f43 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -75,6 +75,8 @@ import { GetAppByIDResponse, GetDefaultPasswordComplexityPolicyRequest, GetDefaultPasswordComplexityPolicyResponse, + GetFeaturesRequest, + GetFeaturesResponse, GetGrantedProjectByIDRequest, GetGrantedProjectByIDResponse, GetHumanEmailRequest, @@ -701,6 +703,13 @@ export class ManagementService { return this.grpcService.mgmt.listOrgMemberRoles(req, null).then(resp => resp.toObject()); } + // Features + + public getFeatures(): Promise { + const req = new GetFeaturesRequest(); + return this.grpcService.mgmt.getFeatures(req, null).then(resp => resp.toObject()); + } + // Policy public getOrgIAMPolicy(): Promise { diff --git a/console/src/assets/environment.json b/console/src/assets/environment.json index 9f45116c2..79be3c987 100644 --- a/console/src/assets/environment.json +++ b/console/src/assets/environment.json @@ -3,5 +3,5 @@ "mgmtServiceUrl": "https://api.zitadel.io", "adminServiceUrl":"https://api.zitadel.io", "issuer": "https://issuer.zitadel.io", - "clientid": "100851239960569938@zitadel" + "clientid": "100992085175427532@zitadel" } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 78716231b..9b3fa770c 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -440,10 +440,10 @@ "REMOVE":"Entfernen", "TYPE":"Typ", "TYPES":{ - "0":"Unbekannt", - "1":"Organisation", - "2":"Projekt", - "3":"Berechtigtes Projekt" + "UNKNOWN":"Unbekannt", + "ORG":"Organisation", + "PROJECT":"Projekt", + "GRANTEDPROJECT":"Berechtigtes Projekt" } } }, @@ -568,6 +568,28 @@ "SETPRIMARY":"Primäre Domain gesetzt." } }, + "FEATURES": { + "TITLE":"Features", + "DESCRIPTION":"Hier können Sie Funktionen von ZITADEL auf Basis von Ihrem Tier aktivieren sowie deaktivieren.", + "BTN-EDIT":"Featureset anpassen", + "DATA": { + "AUDITLOGRETENTION":"Audit Log retention", + "LOGINPOLICYUSERNAMELOGIN":"Login Policy - Username as login", + "LOGINPOLICYREGISTRATION":"Login Policy - Registration", + "LOGINPOLICYIDP":"Login Policy - Identity Providers", + "LOGINPOLICYFACTORS":"Login Policy - Factors", + "LOGINPOLICYPASSWORDLESS": "Login Policy - Passwordless Authentication", + "LOGINPOLICYCOMPLEXITYPOLICY":"Password Complexity Settings", + "LABELPOLICY":"Label Policy" + }, + "TIERSTATES": { + "0":"Aktiv", + "1":"Aktion erforderlich", + "2":"Annuliert", + "3":"Besitzstandswahrend" + }, + "NOTAVAILABLE":"Feature {{value}} ist auf Ihrer organisation nicht freigeschaltet!" + }, "POLICY": { "TITLE":"Richtlinen entdecken", "DESCRIPTION":"Vorgefertigte Richtlinien, die Dir Zeit sparen und die Sicherheit erhöhen.", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 04c738f0a..2f9c3abbb 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -438,10 +438,10 @@ "REMOVE":"Remove", "TYPE":"Type", "TYPES":{ - "0":"Unknown", - "1":"Organisation", - "2":"Project", - "3":"Granted Project" + "UNKNOWN":"Unknown", + "ORG":"Organisation", + "PROJECT":"Project", + "GRANTEDPROJECT":"Granted Project" } } }, @@ -566,6 +566,28 @@ "SETPRIMARY":"Primary domain set." } }, + "FEATURES": { + "TITLE":"Features", + "DESCRIPTION":"here you can edit your ZITADEL Features based on your Tier", + "BTN-EDIT":"Edit Featureset", + "DATA": { + "AUDITLOGRETENTION":"Audit Log retention", + "LOGINPOLICYUSERNAMELOGIN":"Login Policy - Benutzername als Loginname", + "LOGINPOLICYREGISTRATION":"Login Policy - Registration", + "LOGINPOLICYIDP":"Login Policy - Identity Provider", + "LOGINPOLICYFACTORS":"Login Policy - Multifaktoren", + "LOGINPOLICYPASSWORDLESS": "Login Policy - Passwortlose Authentifizierung", + "LOGINPOLICYCOMPLEXITYPOLICY":"Password Komplexitätseinstellungen", + "LABELPOLICY":"Labelling Einstellungen" + }, + "TIERSTATES": { + "0":"Active", + "1":"Action required", + "2":"Cancelled", + "3":"Grandfathered" + }, + "NOTAVAILABLE":"Feature {{value}} is missing on your organization." + }, "POLICY": { "TITLE":"Explore Policies", "DESCRIPTION":"Pre-packaged policies that enhance your security.",