mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:57:33 +00:00
feat: label policy (#1708)
* feat: label policy proto extension * feat: label policy and activate event * feat: label policy asset events * feat: label policy asset commands * feat: add storage key * feat: storage key validation * feat: label policy asset tests * feat: label policy query side * feat: avatar * feat: avatar event * feat: human avatar * feat: avatar read side * feat: font on iam label policy * feat: label policy font * feat: possiblity to create bucket on put file * uplaoder * login policy logo * set bucket prefix * feat: avatar upload * feat: avatar upload * feat: use assets on command side * feat: fix human avatar removed event * feat: remove human avatar * feat: mock asset storage * feat: remove human avatar * fix(operator): add configuration of asset storage to zitadel operator * feat(console): private labeling policy (#1697) * private labeling component, routing, preview * font, colors, upload, i18n * show logo * fix: uniqueness (#1710) * fix: uniqueconstraint to lower * feat: change org * feat: org change test * feat: change org * fix: tests * fix: handle domain claims correctly * feat: update org Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> * fix: handle domain claimed event correctly for service users (#1711) * fix: handle domain claimed event correctly on user view * fix: ignore domain claimed events for email notifications * fix: change org * handle org changed in read models correctly * fix: change org in user grant handler Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> * fix: correct value (#1695) * docs(api): correct link (#1712) * upload service Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> Co-authored-by: Florian Forster <florian@caos.ch> * feat: fix tests, * feat: remove assets from label policy * fix npm, set environment * lint ts * remove stylelinting * fix(operator): add mapping for console with changed unit tests * fix(operator): add secrets as env variables to pod * feat: remove human avatar * fix(operator): add secrets as env variables to pod * feat: map label policy * feat: labelpolicy, admin, mgmt, adv settings (#1715) * fetch label policy, mgmt, admin service * feat: advanced beh, links, add, update * lint ts * feat: watermark * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: custom css * css * css * css * css * css * getobject * feat: dynamic handler * feat: varibale css * content info * css overwrite * feat: variablen css * feat: generate css file * feat: dark mode * feat: dark mode * fix logo css * feat: upload logos * dark mode with cookie * feat: handle images in login * avatar css and begin font * feat: avatar * feat: user avatar * caching of static assets in login * add avatar.js to main.html * feat: header dont show logo if no url * feat: label policy colors * feat: mock asset storage * feat: mock asset storage * feat: fix tests * feat: user avatar * feat: header logo * avatar * avatar * make it compatible with go 1.15 * feat: remove unused logos * fix handler * fix: styling error handling * fonts * fix: download func * switch to mux * fix: change upload api to assets * fix build * fix: download avatar * fix: download logos * fix: my avatar * font * fix: remove error msg popup possibility * fix: docs * fix: svalidate colors * rem msg popup from frontend * fix: email with private labeling * fix: tests * fix: email templates * fix: change migration version * fix: fix duplicate imports * fix(console): assets, service url, upload, policy current and preview (#1781) * upload endpoint, layout * fetch current, preview, fix upload * cleanup private labeling * fix linting * begin generated asset handler * generate asset api in dockerfile * features for label policy * features for label policy * features * flag for asset generator * change asset generator flag * fix label policy view in grpc * fix: layout, activate policy (#1786) * theme switcher up on top * change layout * activate policy * feat(console): label policy back color, layout (#1788) * theme switcher up on top * change layout * activate policy * fix overwrite value fc * reset policy, reset service * autosave policy, preview desc, layout impv * layout, i18n * background colors, inject material styles * load images * clean, lint * fix layout * set custom hex * fix content size conversion * remove font format in generated css * fix features for assets * fix(console): label policy colors, image downloads, preview (#1804) * load images * colors, images binding * lint * refresh emitter * lint * propagate font colors * upload error handling * label policy feature check * add blob in csp for console * log * fix: feature edits for label policy, refresh state on upload (#1807) * show error on load image, stop spinner * fix merge * fix migration versions * fix assets * fix csp * fix background color * scss * fix build * lint scss * fix statik for console * fix features check for label policy * cleanup * lint * public links * fix notifications * public links * feat: merge main * feat: fix translation files * fix migration * set api domain * fix logo in email * font face in email * font face in email * validate assets on upload * cleanup * add missing translations * add missing translations Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Stefan Benz <stefan@caos.ch> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
@@ -26,6 +26,7 @@ import { from, Observable } from 'rxjs';
|
||||
import { OnboardingModule } from 'src/app/modules/onboarding/onboarding.module';
|
||||
import { RegExpPipeModule } from 'src/app/pipes/regexp-pipe/regexp-pipe.module';
|
||||
import { SubscriptionService } from 'src/app/services/subscription.service';
|
||||
import { UploadService } from 'src/app/services/upload.service';
|
||||
|
||||
import { environment } from '../environments/environment';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
@@ -180,6 +181,7 @@ const authConfig: AuthConfig = {
|
||||
AuthenticationService,
|
||||
GrpcAuthService,
|
||||
SubscriptionService,
|
||||
UploadService,
|
||||
{ provide: 'windowObject', useValue: window },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
|
28
console/src/app/directives/dropzone/dropzone.directive.ts
Normal file
28
console/src/app/directives/dropzone/dropzone.directive.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[cnslDropzone]',
|
||||
})
|
||||
export class DropzoneDirective {
|
||||
@Output() dropped: EventEmitter<FileList> = new EventEmitter<FileList>();
|
||||
@Output() hovered: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop($event: DragEvent): void {
|
||||
$event.preventDefault();
|
||||
this.dropped.emit($event.dataTransfer?.files);
|
||||
this.hovered.emit(false);
|
||||
}
|
||||
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver($event: any): void {
|
||||
$event.preventDefault();
|
||||
this.hovered.emit(true);
|
||||
}
|
||||
|
||||
@HostListener('dragleave', ['$event'])
|
||||
onDragLeave($event: any): void {
|
||||
$event.preventDefault();
|
||||
this.hovered.emit(false);
|
||||
}
|
||||
}
|
19
console/src/app/directives/dropzone/dropzone.module.ts
Normal file
19
console/src/app/directives/dropzone/dropzone.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { DropzoneDirective } from './dropzone.directive';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DropzoneDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
DropzoneDirective,
|
||||
],
|
||||
})
|
||||
export class DropzoneModule { }
|
@@ -120,9 +120,17 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICY' | translate}}</span>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicy"
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyPrivateLabel"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyWatermark"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
@@ -158,7 +158,8 @@ export class FeaturesComponent implements OnDestroy {
|
||||
req.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
req.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
req.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
req.setLabelPolicy(this.features.labelPolicy);
|
||||
req.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
req.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
req.setCustomDomain(this.features.customDomain);
|
||||
|
||||
this.adminService.setOrgFeatures(req).then(() => {
|
||||
@@ -177,7 +178,8 @@ export class FeaturesComponent implements OnDestroy {
|
||||
dreq.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
dreq.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
dreq.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
dreq.setLabelPolicy(this.features.labelPolicy);
|
||||
dreq.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
dreq.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
dreq.setCustomDomain(this.features.customDomain);
|
||||
|
||||
this.adminService.setDefaultFeatures(dreq).then(() => {
|
||||
|
@@ -4,6 +4,7 @@
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.ng-untouched {
|
||||
.cnsl-error {
|
||||
@@ -29,6 +30,10 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input {
|
||||
color: mat.get-color-from-palette($foreground, text);
|
||||
}
|
||||
|
||||
// Wrapper for the hints and error messages.
|
||||
.cnsl-form-field-subscript-wrapper {
|
||||
position: absolute;
|
||||
|
@@ -8,17 +8,18 @@
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: .5rem;
|
||||
margin: 0 -.5rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.step {
|
||||
min-width: 220px;
|
||||
max-width: 280px;
|
||||
padding: 1rem;
|
||||
margin: 0 .5rem;
|
||||
border: 1px solid var(--grey);
|
||||
margin: 1rem .5rem;
|
||||
border: 1px solid #8795a150;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -26,6 +27,10 @@
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
|
||||
@media only screen and (min-width: 899px) {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
@@ -46,14 +51,6 @@
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LabelPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class LabelPolicyRoutingModule { }
|
@@ -1,19 +0,0 @@
|
||||
<app-detail-layout [backRouterLink]="['/iam/policies']" [title]="'POLICY.LABEL.TITLE' | translate"
|
||||
[description]="'POLICY.LABEL.DESCRIPTION' | translate">
|
||||
|
||||
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||
|
||||
<div class="content" *ngIf="labelData">
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl
|
||||
[(ngModel)]="labelData.hideLoginNameSuffix">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
|
||||
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
|
||||
</app-detail-layout>
|
@@ -1,23 +0,0 @@
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.default {
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
padding: .5rem 4rem;
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
describe('LabelPolicyComponent', () => {
|
||||
let component: LabelPolicyComponent;
|
||||
let fixture: ComponentFixture<LabelPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LabelPolicyComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LabelPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,71 +0,0 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { GetLabelPolicyResponse, UpdateLabelPolicyRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import { IAM_COMPLEXITY_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-label-policy',
|
||||
templateUrl: './label-policy.component.html',
|
||||
styleUrls: ['./label-policy.component.scss'],
|
||||
})
|
||||
export class LabelPolicyComponent implements OnDestroy {
|
||||
public labelData!: LabelPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: CnslLinks[] = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private adminService: AdminService,
|
||||
) {
|
||||
this.route.params.subscribe(() => {
|
||||
this.getData().then(data => {
|
||||
if (data?.policy) {
|
||||
this.labelData = data.policy;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetLabelPolicyResponse.AsObject> {
|
||||
return this.adminService.getLabelPolicy();
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
req.setPrimaryColor(this.labelData.primaryColor);
|
||||
req.setSecondaryColor(this.labelData.secondaryColor);
|
||||
req.setHideLoginNameSuffix(this.labelData.hideLoginNameSuffix);
|
||||
this.adminService.updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.labelData) {
|
||||
return (this.labelData as LabelPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
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 { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { LinksModule } from '../../links/links.module';
|
||||
import { LabelPolicyRoutingModule } from './label-policy-routing.module';
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [LabelPolicyComponent],
|
||||
imports: [
|
||||
LabelPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
LinksModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
})
|
||||
export class LabelPolicyModule { }
|
@@ -22,10 +22,11 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
|
||||
@@ -69,6 +70,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
@@ -80,7 +82,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
@@ -96,7 +98,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
if (resp.policy) {
|
||||
this.loginData = resp.policy;
|
||||
this.loading = false;
|
||||
this.disabled = this.isDefault ?? false;
|
||||
this.disabled = ((this.loginData as LoginPolicy.AsObject)?.isDefault) ?? false;
|
||||
}
|
||||
});
|
||||
this.getIdps().then(resp => {
|
||||
|
@@ -13,141 +13,143 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
selector: 'app-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
})
|
||||
export class OrgIamPolicyComponent implements OnDestroy {
|
||||
private managementService!: ManagementService;
|
||||
public serviceType!: PolicyComponentServiceType;
|
||||
private managementService!: ManagementService;
|
||||
public serviceType!: PolicyComponentServiceType;
|
||||
|
||||
public iamData!: OrgIAMPolicy.AsObject;
|
||||
public iamData!: OrgIAMPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
private org!: Org.AsObject;
|
||||
private sub: Subscription = new Subscription();
|
||||
private org!: Org.AsObject;
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: Array<CnslLinks> = [];
|
||||
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;
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: Array<CnslLinks> = [];
|
||||
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 === PolicyComponentServiceType.MGMT) {
|
||||
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
} else {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
}
|
||||
return this.route.params;
|
||||
})).subscribe(_ => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp?.policy) {
|
||||
this.iamData = resp.policy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
];
|
||||
} else {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
];
|
||||
}
|
||||
return this.route.params;
|
||||
})).subscribe(_ => {
|
||||
this.fetchData();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp?.policy) {
|
||||
this.iamData = resp.policy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
this.adminService.addCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
this.adminService.updateCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
this.adminService.updateOrgIAMPolicy(
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.adminService.resetCustomOrgIAMPolicyToDefault(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.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
this.adminService.addCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
this.adminService.updateCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
this.adminService.updateOrgIAMPolicy(
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.adminService.resetCustomOrgIAMPolicyToDefault(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.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,10 +3,10 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse,
|
||||
GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
|
||||
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
@@ -15,163 +15,165 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_LABEL_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-policy',
|
||||
templateUrl: './password-complexity-policy.component.html',
|
||||
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||
selector: 'app-password-policy',
|
||||
templateUrl: './password-complexity-policy.component.html',
|
||||
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||
})
|
||||
export class PasswordComplexityPolicyComponent implements OnDestroy {
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public complexityData!: PasswordComplexityPolicy.AsObject;
|
||||
public complexityData!: PasswordComplexityPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
this.nextLinks = [
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
this.nextLinks = [
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData().then(data => {
|
||||
if (data.policy) {
|
||||
this.complexityData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordComplexityPolicyResponse.AsObject | AdminGetPasswordComplexityPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordComplexityPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordComplexityPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData().then(data => {
|
||||
if (data.policy) {
|
||||
this.complexityData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
public incrementLength(): void {
|
||||
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
||||
this.complexityData.minLength++;
|
||||
}
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordComplexityPolicyResponse.AsObject | AdminGetPasswordComplexityPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordComplexityPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordComplexityPolicy();
|
||||
}
|
||||
public decrementLength(): void {
|
||||
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
||||
this.complexityData.minLength--;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordComplexityPolicy(
|
||||
|
||||
public incrementLength(): void {
|
||||
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
||||
this.complexityData.minLength++;
|
||||
}
|
||||
}
|
||||
|
||||
public decrementLength(): void {
|
||||
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
||||
this.complexityData.minLength--;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordComplexityPolicy(
|
||||
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
(this.service as ManagementService).updateCustomPasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.complexityData as PasswordComplexityPolicy.AsObject).isDefault;
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
(this.service as ManagementService).updateCustomPasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.complexityData as PasswordComplexityPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
export enum PolicyComponentType {
|
||||
LOCKOUT = 'lockout',
|
||||
AGE = 'age',
|
||||
COMPLEXITY = 'complexity',
|
||||
IAM = 'iam',
|
||||
LOGIN = 'login',
|
||||
LABEL = 'label',
|
||||
LOCKOUT = 'lockout',
|
||||
AGE = 'age',
|
||||
COMPLEXITY = 'complexity',
|
||||
IAM = 'iam',
|
||||
LOGIN = 'login',
|
||||
PRIVATELABEL = 'privatelabel',
|
||||
}
|
||||
export enum PolicyComponentServiceType {
|
||||
MGMT = 'mgmt',
|
||||
ADMIN = 'admin',
|
||||
MGMT = 'mgmt',
|
||||
ADMIN = 'admin',
|
||||
}
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<div class="form-row">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{name}} (current {{color}})</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="previewColor" (keydown.enter)="name ? emitPreview(previewColor) : null" />
|
||||
</cnsl-form-field>
|
||||
<button matTooltip="{{'ACTIONS.SET' | translate}}" (click)="emitPreview(previewColor)" mat-icon-button><mat-icon>check</mat-icon></button>
|
||||
</div>
|
||||
<div class="color-selector">
|
||||
<div *ngFor="let c of colors" class="circle mat-elevation-z3"
|
||||
[ngClass]="{'active': color == c.color || previewColor == c.color}"
|
||||
(click)="emitPreview(c.color)" [ngStyle]="{'background-color': c.color}">
|
||||
<span *ngIf="previewColor == c.color">P</span>
|
||||
<span *ngIf="color == c.color">C</span>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,52 @@
|
||||
.title {
|
||||
// font-size: 1rem;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
|
||||
&.border {
|
||||
border-top: 1px solid var(--grey);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.25rem;
|
||||
width: 100%;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.circle {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background-color: #cd5c5c;
|
||||
border-radius: 50%;
|
||||
margin: .25rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
span {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 3px solid #ffffff90;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ColorComponent } from './color.component';
|
||||
|
||||
describe('ColorComponent', () => {
|
||||
let component: ColorComponent;
|
||||
let fixture: ComponentFixture<ColorComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ColorComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ColorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,118 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
import { ColorType } from '../private-labeling-policy.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-color',
|
||||
templateUrl: './color.component.html',
|
||||
styleUrls: ['./color.component.scss'],
|
||||
})
|
||||
export class ColorComponent implements OnInit {
|
||||
public PRIMARY: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'red', color: '#f44336' },
|
||||
{ name: 'pink', color: '#e91e63' },
|
||||
{ name: 'purple', color: '#9c27b0' },
|
||||
{ name: 'deeppurple', color: '#673ab7' },
|
||||
{ name: 'indigo', color: '#3f51b5' },
|
||||
{ name: 'blue', color: '#2196f3' },
|
||||
{ name: 'lightblue', color: '#03a9f4' },
|
||||
{ name: 'cyan', color: '#00bcd4' },
|
||||
{ name: 'teal', color: '#009688' },
|
||||
{ name: 'green', color: '#4caf50' },
|
||||
{ name: 'lightgreen', color: '#8bc34a' },
|
||||
{ name: 'lime', color: '#cddc39' },
|
||||
{ name: 'yellow', color: '#ffeb3b' },
|
||||
{ name: 'amber', color: '#ffc107' },
|
||||
{ name: 'orange', color: '#fb8c00' },
|
||||
{ name: 'deeporange', color: '#ff5722' },
|
||||
{ name: 'brown', color: '#795548' },
|
||||
{ name: 'grey', color: '#9e9e9e' },
|
||||
{ name: 'bluegrey', color: '#607d8b' },
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
];
|
||||
|
||||
public WARN: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'red', color: '#f44336' },
|
||||
{ name: 'pink', color: '#e91e63' },
|
||||
{ name: 'purple', color: '#9c27b0' },
|
||||
{ name: 'deeppurple', color: '#673ab7' },
|
||||
];
|
||||
|
||||
public FONTLIGHT: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
{ name: 'gray-600', color: '#4b5563' },
|
||||
{ name: 'gray-700', color: '#374151' },
|
||||
{ name: 'gray-800', color: '#1f2937' },
|
||||
{ name: 'gray-900', color: '#111827' },
|
||||
{ name: 'black', color: '#000000' },
|
||||
];
|
||||
|
||||
public FONTDARK: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
{ name: 'gray-50', color: '#f9fafb' },
|
||||
{ name: 'gray-100', color: '#f3f4f6' },
|
||||
{ name: 'gray-200', color: '#e5e7eb' },
|
||||
{ name: 'gray-300', color: '#d1d5db' },
|
||||
{ name: 'gray-400', color: '#9ca3af' },
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
];
|
||||
|
||||
public BACKGROUNDLIGHT: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
{ name: 'gray-50', color: '#f9fafb' },
|
||||
{ name: 'gray-100', color: '#f3f4f6' },
|
||||
{ name: 'gray-200', color: '#e5e7eb' },
|
||||
{ name: 'gray-300', color: '#d1d5db' },
|
||||
{ name: 'gray-400', color: '#9ca3af' },
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
];
|
||||
|
||||
public BACKGROUNDDARK: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
{ name: 'gray-600', color: '#4b5563' },
|
||||
{ name: 'gray-700', color: '#374151' },
|
||||
{ name: 'gray-800', color: '#1f2937' },
|
||||
{ name: 'gray-900', color: '#111827' },
|
||||
{ name: 'black', color: '#000000' },
|
||||
];
|
||||
|
||||
public colors: Array<{ name: string; color: string; }> = this.PRIMARY;
|
||||
|
||||
@Input() colorType: ColorType = ColorType.PRIMARY;
|
||||
@Input() color: string = '';
|
||||
@Input() previewColor: string = '';
|
||||
@Input() name: string = '';
|
||||
@Output() previewChanged: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
public emitPreview(color: string): void {
|
||||
this.previewColor = color;
|
||||
this.previewChanged.emit(this.previewColor);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
switch (this.colorType) {
|
||||
case ColorType.PRIMARY:
|
||||
this.colors = this.PRIMARY;
|
||||
break;
|
||||
case ColorType.WARN:
|
||||
this.colors = this.WARN;
|
||||
break;
|
||||
case ColorType.FONTDARK:
|
||||
this.colors = this.FONTDARK;
|
||||
break;
|
||||
case ColorType.FONTLIGHT:
|
||||
this.colors = this.FONTLIGHT;
|
||||
break;
|
||||
case ColorType.BACKGROUNDDARK:
|
||||
this.colors = this.BACKGROUNDDARK;
|
||||
break;
|
||||
case ColorType.BACKGROUNDLIGHT:
|
||||
this.colors = this.BACKGROUNDLIGHT;
|
||||
break;
|
||||
default:
|
||||
this.colors = this.PRIMARY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<div class="preview" *ngIf="policy">
|
||||
<span class="label">{{label}}</span>
|
||||
<div class="dashed" [ngClass]="{'dark': theme === Theme.DARK, 'light': theme === Theme.LIGHT}" [style.background]="theme == Theme.DARK ? policy.backgroundColorDark : policy.backgroundColor">
|
||||
<div class="login-wrapper" [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor">
|
||||
<img *ngIf="images['previewLogo'] && theme == Theme.LIGHT" [src]="images['previewLogo']" alt="logo-mock" />
|
||||
<img *ngIf="!images['previewLogo'] && theme == Theme.LIGHT" src="../../../../assets/images/zitadel-logo-dark.svg" alt="logo-mock" />
|
||||
|
||||
<img *ngIf="images['previewDarkLogo'] && theme == Theme.DARK" [src]="images['previewDarkLogo']" alt="logo-mock" />
|
||||
<img *ngIf="!images['previewDarkLogo'] && theme == Theme.DARK" src="../../../../assets/images/zitadel-logo-light.svg" alt="logo-mock" />
|
||||
|
||||
<h1>{{'POLICY.PRIVATELABELING.PREVIEW.TITLE' | translate}}</h1>
|
||||
<p class="desc-text">{{'POLICY.PRIVATELABELING.PREVIEW.SECOND' | translate}}</p>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>Loginname</cnsl-label>
|
||||
<input cnslInput value="road.runner"/>
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="error-msg" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">
|
||||
<i class="las la-exclamation-circle" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor"></i>
|
||||
<span [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">{{'POLICY.PRIVATELABELING.PREVIEW.ERROR' | translate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button mat-stroked-button>{{'POLICY.PRIVATELABELING.PREVIEW.SECONDARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.DARK" mat-raised-button [style.background]="policy.primaryColorDark" [style.color]="policy.primaryColorDark == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.LIGHT" mat-raised-button [style.background]="policy.primaryColor" [style.color]="policy.primaryColor == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,92 @@
|
||||
.preview {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--grey);
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border: 2px dashed rgba(#697386, .5);
|
||||
border-radius: 1rem;
|
||||
padding: 100px 20px;
|
||||
|
||||
.login-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: auto;
|
||||
|
||||
@media only screen and (min-width: 1000px) {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 160px;
|
||||
max-height: 150px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
justify-content: flex-start;
|
||||
|
||||
i {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
background: #fff;
|
||||
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: #212224;
|
||||
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PreviewComponent } from './preview.component';
|
||||
|
||||
describe('PreviewComponent', () => {
|
||||
let component: PreviewComponent;
|
||||
let fixture: ComponentFixture<PreviewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PreviewComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PreviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,35 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
|
||||
import { Preview, Theme } from '../private-labeling-policy.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-preview',
|
||||
templateUrl: './preview.component.html',
|
||||
styleUrls: ['./preview.component.scss'],
|
||||
})
|
||||
export class PreviewComponent implements OnInit, OnDestroy {
|
||||
@Input() preview: Preview = Preview.PREVIEW;
|
||||
@Input() policy!: LabelPolicy.AsObject;
|
||||
@Input() label: string = 'PREVIEW';
|
||||
@Input() images: { [imagekey: string]: any; } = {};
|
||||
@Input() theme: Theme = Theme.DARK;
|
||||
@Input() refresh: Observable<void> = of();
|
||||
private destroyed$: Subject<void> = new Subject();
|
||||
public Theme: any = Theme;
|
||||
public Preview: any = Preview;
|
||||
constructor(private chd: ChangeDetectorRef) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.refresh.pipe(takeUntil(this.destroyed$)).subscribe(() => {
|
||||
this.chd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroyed$.next();
|
||||
this.destroyed$.complete();
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PrivateLabelingPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PrivateLabelingPolicyRoutingModule { }
|
@@ -0,0 +1,254 @@
|
||||
<div class="policy enlarged-container">
|
||||
<div class="header">
|
||||
<a [routerLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<div class="col">
|
||||
<h1>{{'POLICY.PRIVATELABELING.TITLE' | translate}}</h1>
|
||||
<p>{{'POLICY.PRIVATELABELING.DESCRIPTION' | translate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="desc">{{'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate}}</p>
|
||||
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="top-row">
|
||||
<div>
|
||||
<p>{{'POLICY.PRIVATELABELING.THEME' | translate}}</p>
|
||||
<div class="theme-changer">
|
||||
<button (click)="theme = Theme.LIGHT" matTooltip="{{'POLICY.PRIVATELABELING.LIGHT' | translate}}" class="light" [ngClass]="{'active': theme === Theme.LIGHT}" >
|
||||
<i class="icon las la-edit"></i>
|
||||
</button>
|
||||
<button (click)="theme = Theme.DARK" matTooltip="{{'POLICY.PRIVATELABELING.DARK' | translate}}" class="dark" [ngClass]="{'active': theme === Theme.DARK}" >
|
||||
<i class="icon las la-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="activate-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button color="primary" (click)="activatePolicy()">
|
||||
{{'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="previewData && data" class="lab-policy-content">
|
||||
|
||||
<mat-accordion class="settings">
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-image"></i>
|
||||
Logo</div>
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<p class="description">Your Logo will be used in the Login itself, while the icon is used for smaller UI elements like in the organisation switcher in console</p>
|
||||
|
||||
<!-- <span class="title">{{ theme === Theme.DARK ? ('POLICY.PRIVATELABELING.DARK' | translate) : ('POLICY.PRIVATELABELING.LIGHT' | translate)}}</span> -->
|
||||
<div class="logo-setup-wrapper">
|
||||
<div class="part">
|
||||
<span class="label">Logo</span>
|
||||
<mat-spinner class="spinner" color="primary" diameter="25" *ngIf="loadingImages"></mat-spinner>
|
||||
<container [ngSwitch]="theme">
|
||||
<div class="logo-view" *ngSwitchCase="Theme.DARK">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewDarkLogo']" [src]="images['previewDarkLogo']" alt="dark logo preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['darkLogo']" [src]="images['darkLogo']" alt="dark logo"/>
|
||||
</div>
|
||||
<div class="logo-view" *ngSwitchCase="Theme.LIGHT">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewLogo']" [src]="images['previewLogo']" alt="logo preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['logo']" [src]="images['logo']" alt="logo"/>
|
||||
</div>
|
||||
</container>
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverLogo(theme, $event)"
|
||||
(dropped)="onDropLogo(theme, $event)"
|
||||
[class.hovering]="isHoveringOverDarkLogo">
|
||||
<label class="file-label">
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDropLogo(theme, $event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFile.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span>{{isHoveringOverDarkLogo ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="part">
|
||||
<span class="label">Icon</span>
|
||||
<mat-spinner class="spinner" color="primary" diameter="25" *ngIf="loadingImages"></mat-spinner>
|
||||
<container [ngSwitch]="theme">
|
||||
<div class="logo-view" *ngSwitchCase="Theme.DARK">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewDarkIcon']" [src]="images['previewDarkIcon']" alt="dark icon preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['darkIcon']" [src]="images['darkIcon']" alt="dark icon"/>
|
||||
</div>
|
||||
<div class="logo-view" *ngSwitchCase="Theme.LIGHT">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewIcon']" [src]="images['previewIcon']" alt="icon preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['icon']" [src]="images['icon']" alt="icon"/>
|
||||
</div>
|
||||
</container>
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverIcon(theme, $event)"
|
||||
(dropped)="onDropIcon(theme, $event)"
|
||||
[class.hovering]="isHoveringOverDarkIcon">
|
||||
<label class="file-label">
|
||||
<input #selectedFileIcon style="display: none;" class="file-input" type="file" (change)="onDropIcon(theme, $event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFileIcon.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span>{{isHoveringOverDarkIcon ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion" [expanded]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-palette"></i>
|
||||
{{'POLICY.PRIVATELABELING.COLORS' | translate}}</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<!-- <span class="title">{{ theme === Theme.DARK ? ('POLICY.PRIVATELABELING.DARK' | translate) : ('POLICY.PRIVATELABELING.LIGHT' | translate)}}</span> -->
|
||||
|
||||
<ng-container *ngIf="theme==Theme.DARK">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDDARK"(previewChanged)="previewData.backgroundColorDark = $event; savePolicy()" name="Background Color Dark" [color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.PRIMARY"(previewChanged)="previewData.primaryColorDark = $event; savePolicy()" name="Preview Primary Color Dark" [color]="data.primaryColorDark" [previewColor]="previewData.primaryColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.WARN" (previewChanged)="previewData.warnColorDark = $event; savePolicy()" name="Preview Warn Color Dark" [color]="data.warnColorDark" [previewColor]="previewData.warnColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.FONTDARK"(previewChanged)="previewData.fontColorDark = $event; savePolicy()" name="Font Color Dark" [color]="data.fontColorDark" [previewColor]="previewData.fontColorDark"></cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="theme==Theme.LIGHT">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDLIGHT" (previewChanged)="previewData.backgroundColor = $event; savePolicy()" name="Background Color Light" [color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.PRIMARY" (previewChanged)="previewData.primaryColor = $event; savePolicy()" name="Preview Primary Color Light" [color]="data.primaryColor" [previewColor]="previewData.primaryColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.WARN" name="Preview Warn Color Light" (previewChanged)="previewData.warnColor= $event; savePolicy()" [color]="data.warnColor" [previewColor]="previewData.warnColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.FONTLIGHT" (previewChanged)="previewData.fontColor = $event; savePolicy()" name="Font Color" [color]="data.fontColor" [previewColor]="previewData.fontColor"></cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header class="header">
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-font"></i>
|
||||
{{'POLICY.PRIVATELABELING.FONT' | translate}}</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="fonts">
|
||||
<div class="font-preview">
|
||||
<mat-icon>text_fields</mat-icon>
|
||||
<span>ABC • abc • 123</span>
|
||||
<span>_</span>
|
||||
<span>Upload your favorite font for the UI</span>
|
||||
</div>
|
||||
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverFont($event)"
|
||||
(dropped)="onDropFont($event)"
|
||||
[class.hovering]="isHoveringOverFont">
|
||||
<label class="file-label">
|
||||
<input #selectedFontFile style="display: none;" class="file-input" type="file" (change)="onDropFont($event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFontFile.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span >{{isHoveringOverFont ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-universal-access"></i>
|
||||
{{'POLICY.PRIVATELABELING.ADVANCEDBEHAVIOR' | translate}}
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="adv-container" *ngIf="previewData">
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false">
|
||||
<cnsl-info-section class="info" type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy.private_label'})}}
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl
|
||||
[(ngModel)]="previewData.hideLoginNameSuffix" (change)="savePolicy()">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false">
|
||||
<cnsl-info-section class="info" type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy.watermark'})}}
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false"
|
||||
[(ngModel)]="previewData.disableWatermark" (change)="saveWatermark()">
|
||||
{{'POLICY.DATA.DISABLEWATERMARK' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="preview-wrapper">
|
||||
<!-- <cnsl-preview class="prev" label="CURRENT CONFIG" [policy]="data"></cnsl-preview> -->
|
||||
<div class="col">
|
||||
<button color="primary" mat-raised-button class="preview-changer" (click)="preview = preview == Preview.PREVIEW ? Preview.CURRENT : Preview.PREVIEW" matTooltip="{{'POLICY.PRIVATELABELING.CHANGEVIEW' | translate}}" [ngClass]="{'active': preview === Preview.PREVIEW}" >
|
||||
<span><span [ngClass]="{'strong': preview == Preview.PREVIEW}">P</span> / <span [ngClass]="{'strong': preview == Preview.CURRENT}">C</span></span>
|
||||
</button>
|
||||
|
||||
<cnsl-preview *ngIf="preview === Preview.CURRENT" [refresh]="refreshPreview" [images]="images" [preview]="preview" [theme]="theme" class="prev" label="CURRENT" [policy]="data"></cnsl-preview>
|
||||
<cnsl-preview *ngIf="preview === Preview.PREVIEW" [refresh]="refreshPreview" [images]="images" [preview]="preview" [theme]="theme" class="prev" label="PREVIEW" [policy]="previewData"></cnsl-preview>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
|
||||
</div>
|
@@ -0,0 +1,398 @@
|
||||
@use '~@angular/material' as mat;
|
||||
|
||||
@import './preview/preview.component.scss';
|
||||
|
||||
@mixin private-label-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
|
||||
.policy {
|
||||
.header {
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 1rem;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.default {
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
max-width: 800px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.spinner-wr {
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
||||
.theme-changer {
|
||||
display: flex;
|
||||
margin: 1rem -.5rem;
|
||||
|
||||
button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #81868a50;
|
||||
margin: 0 .5rem;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: black;
|
||||
|
||||
&.active {
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: white;
|
||||
|
||||
&.active {
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.activate-button {
|
||||
border-radius: 50%;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.theme-changer {
|
||||
display: flex;
|
||||
margin: 0 -.25rem;
|
||||
|
||||
button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #81868a50;
|
||||
margin: 0 .25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: black;
|
||||
|
||||
&.active {
|
||||
transform: scale(1.1);
|
||||
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: white;
|
||||
|
||||
&.active {
|
||||
transform: scale(1.1);
|
||||
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lab-policy-content {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -1rem;
|
||||
|
||||
.settings {
|
||||
flex: 1;
|
||||
margin: 0 1rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.expansion {
|
||||
background: if($is-dark-theme, #2d2e30, #fff) !important;
|
||||
|
||||
.header {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: .5rem;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
outline: none;
|
||||
height: 150px;
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
background: if($is-dark-theme, #2d2e30, #fff);
|
||||
border: 1px solid if($is-dark-theme, #4a4b4b, #ddd);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transition: all .2s ease-in-out;
|
||||
|
||||
.file-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
padding: .5rem 1rem;
|
||||
background-color: inherit;
|
||||
border: 1px solid if($is-dark-theme, #ffffff20, #000);
|
||||
color: if($is-dark-theme, white, #000);
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 1.5rem;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
&.hovering {
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
if(
|
||||
$is-dark-theme,
|
||||
(inset 26px 26px 52px #252628, inset -26px -26px 52px #353638),
|
||||
(inset 26px 26px 52px #d4d4d4, inset -26px -26px 52px #fff)
|
||||
);
|
||||
|
||||
.desc {
|
||||
color: if($is-dark-theme, white, black);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: if($is-dark-theme, white, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo-setup-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.part {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.spinner {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.logo-view {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.prev,
|
||||
.curr {
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.colors {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.color {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fonts {
|
||||
.title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.font-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30px 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.font-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.25rem;
|
||||
width: 100%;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.font {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: .5rem;
|
||||
margin: .25rem;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid if($is-dark-theme, #ffffff30, #00000030);
|
||||
font-size: 1.5rem;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border: 2px solid if($is-dark-theme, #fff, #000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adv-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 50px;
|
||||
|
||||
.info {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-wrapper {
|
||||
margin: 0 1rem;
|
||||
flex: 1;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 2rem;
|
||||
position: relative;
|
||||
min-height: 600px;
|
||||
|
||||
.preview-changer {
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
left: .5rem;
|
||||
border-radius: 10px !important;
|
||||
z-index: 1;
|
||||
|
||||
span {
|
||||
color: if($is-dark-theme, #ffffff50, #00000050);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: if($is-dark-theme, #fff, #000);
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1000px) {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.preview-wrapper {
|
||||
.col {
|
||||
min-width: 400px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
describe('PrivateLabelingPolicyComponent', () => {
|
||||
let component: PrivateLabelingPolicyComponent;
|
||||
let fixture: ComponentFixture<PrivateLabelingPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PrivateLabelingPolicyComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PrivateLabelingPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,511 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, EventEmitter, Injector, OnDestroy, Type } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { DownloadEndpoint, UploadEndpoint, UploadService } from 'src/app/services/upload.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import { IAM_COMPLEXITY_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
export enum Theme {
|
||||
DARK,
|
||||
LIGHT,
|
||||
}
|
||||
|
||||
export enum Preview {
|
||||
CURRENT,
|
||||
PREVIEW,
|
||||
}
|
||||
|
||||
export enum ColorType {
|
||||
BACKGROUND,
|
||||
PRIMARY,
|
||||
WARN,
|
||||
FONTDARK,
|
||||
FONTLIGHT,
|
||||
BACKGROUNDDARK,
|
||||
BACKGROUNDLIGHT,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-private-labeling-policy',
|
||||
templateUrl: './private-labeling-policy.component.html',
|
||||
styleUrls: ['./private-labeling-policy.component.scss'],
|
||||
})
|
||||
export class PrivateLabelingPolicyComponent implements OnDestroy {
|
||||
public theme: Theme = Theme.LIGHT;
|
||||
public preview: Preview = Preview.PREVIEW;
|
||||
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public previewData!: LabelPolicy.AsObject;
|
||||
public data!: LabelPolicy.AsObject;
|
||||
|
||||
public images: { [key: string]: any; } = {};
|
||||
|
||||
public panelOpenState: boolean = false;
|
||||
public isHoveringOverDarkLogo: boolean = false;
|
||||
public isHoveringOverDarkIcon: boolean = false;
|
||||
public isHoveringOverLightLogo: boolean = false;
|
||||
public isHoveringOverLightIcon: boolean = false;
|
||||
public isHoveringOverFont: boolean = false;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
];
|
||||
|
||||
public Theme: any = Theme;
|
||||
public Preview: any = Preview;
|
||||
public ColorType: any = ColorType;
|
||||
|
||||
public refreshPreview: EventEmitter<void> = new EventEmitter();
|
||||
public loadingImages: boolean = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
private uploadService: UploadService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public toggleHoverLogo(theme: Theme, isHovering: boolean): void {
|
||||
if (theme === Theme.DARK) {
|
||||
this.isHoveringOverDarkLogo = isHovering;
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
this.isHoveringOverLightLogo = isHovering;
|
||||
}
|
||||
}
|
||||
|
||||
public toggleHoverFont(isHovering: boolean): void {
|
||||
this.isHoveringOverFont = isHovering;
|
||||
}
|
||||
|
||||
public onDropLogo(theme: Theme, filelist: FileList): Promise<any> | void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (theme === Theme.DARK) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTDARKLOGO, formData));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMDARKLOGO, formData));
|
||||
}
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTLIGHTLOGO, formData));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMLIGHTLOGO, formData));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public onDropFont(filelist: FileList): Promise<any> | void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.uploadService.upload(UploadEndpoint.MGMTFONT, formData);
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.uploadService.upload(UploadEndpoint.IAMFONT, formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public toggleHoverIcon(theme: Theme, isHovering: boolean): void {
|
||||
if (theme === Theme.DARK) {
|
||||
this.isHoveringOverDarkIcon = isHovering;
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
this.isHoveringOverLightIcon = isHovering;
|
||||
}
|
||||
}
|
||||
|
||||
public onDropIcon(theme: Theme, filelist: FileList): void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (theme === Theme.DARK) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTDARKICON, formData));
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMDARKICON, formData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTLIGHTICON, formData));
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMLIGHTICON, formData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleUploadPromise(task: Promise<any>): Promise<any> {
|
||||
return task.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.UPLOADSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getPreviewData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loadPreviewImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => this.toast.showError(error));
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getPreviewData().then(data => {
|
||||
console.log('preview', data);
|
||||
this.loadingImages = true;
|
||||
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loading = false;
|
||||
|
||||
this.loadPreviewImages();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.getData().then(data => {
|
||||
console.log('data', data);
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loading = false;
|
||||
|
||||
this.loadImages();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private loadImages(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
if (this.data.logoUrlDark) {
|
||||
this.loadAsset('darkLogo', DownloadEndpoint.IAMDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrlDark) {
|
||||
this.loadAsset('darkIcon', DownloadEndpoint.IAMDARKICONPREVIEW);
|
||||
}
|
||||
if (this.data.logoUrl) {
|
||||
this.loadAsset('logo', DownloadEndpoint.IAMLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrl) {
|
||||
this.loadAsset('icon', DownloadEndpoint.IAMICONPREVIEW);
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
if (this.data.logoUrlDark) {
|
||||
this.loadAsset('darkLogo', DownloadEndpoint.MGMTDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrlDark) {
|
||||
this.loadAsset('darkIcon', DownloadEndpoint.MGMTDARKICONPREVIEW);
|
||||
}
|
||||
if (this.data.logoUrl) {
|
||||
this.loadAsset('logo', DownloadEndpoint.MGMTLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrl) {
|
||||
this.loadAsset('icon', DownloadEndpoint.MGMTICONPREVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadPreviewImages(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
if (this.previewData.logoUrlDark) {
|
||||
this.loadAsset('previewDarkLogo', DownloadEndpoint.IAMDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrlDark) {
|
||||
this.loadAsset('previewDarkIcon', DownloadEndpoint.IAMDARKICONPREVIEW);
|
||||
}
|
||||
if (this.previewData.logoUrl) {
|
||||
this.loadAsset('previewLogo', DownloadEndpoint.IAMLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrl) {
|
||||
this.loadAsset('previewIcon', DownloadEndpoint.IAMICONPREVIEW);
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
if (this.previewData.logoUrlDark) {
|
||||
this.loadAsset('previewDarkLogo', DownloadEndpoint.MGMTDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrlDark) {
|
||||
this.loadAsset('previewDarkIcon', DownloadEndpoint.MGMTDARKICONPREVIEW);
|
||||
}
|
||||
if (this.previewData.logoUrl) {
|
||||
this.loadAsset('previewLogo', DownloadEndpoint.MGMTLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrl) {
|
||||
this.loadAsset('previewIcon', DownloadEndpoint.MGMTICONPREVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getPreviewData():
|
||||
Promise<MgmtGetPreviewLabelPolicyResponse.AsObject |
|
||||
AdminGetPreviewLabelPolicyResponse.AsObject |
|
||||
MgmtGetLabelPolicyResponse.AsObject |
|
||||
AdminGetLabelPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPreviewLabelPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPreviewLabelPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPreviewLabelPolicyResponse.AsObject |
|
||||
AdminGetPreviewLabelPolicyResponse.AsObject |
|
||||
MgmtGetLabelPolicyResponse.AsObject |
|
||||
AdminGetLabelPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getLabelPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getLabelPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private loadAsset(imagekey: string, url: string): Promise<any> {
|
||||
return this.uploadService.load(`${url}`).then(data => {
|
||||
const objectURL = URL.createObjectURL(data);
|
||||
this.images[imagekey] = this.sanitizer.bypassSecurityTrustUrl(objectURL);
|
||||
this.refreshPreview.emit();
|
||||
this.loadingImages = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.loadingImages = false;
|
||||
});
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.previewData as LabelPolicy.AsObject).isDefault) {
|
||||
const req0 = new AddCustomLabelPolicyRequest();
|
||||
this.overwriteValues(req0);
|
||||
|
||||
(this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch((error: HttpErrorResponse) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req1 = new UpdateCustomLabelPolicyRequest();
|
||||
this.overwriteValues(req1);
|
||||
|
||||
(this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
this.overwriteValues(req);
|
||||
(this.service as AdminService).updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public saveWatermark(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.previewData as LabelPolicy.AsObject).isDefault) {
|
||||
const req0 = new AddCustomLabelPolicyRequest();
|
||||
req0.setDisableWatermark(this.previewData.disableWatermark);
|
||||
|
||||
(this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch((error: HttpErrorResponse) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req1 = new UpdateCustomLabelPolicyRequest();
|
||||
req1.setDisableWatermark(this.previewData.disableWatermark);
|
||||
|
||||
(this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
req.setDisableWatermark(this.data.disableWatermark);
|
||||
|
||||
(this.service as AdminService).updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.previewData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.previewData as LabelPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public overwriteValues(req: AddCustomLabelPolicyRequest | UpdateCustomLabelPolicyRequest): void {
|
||||
req.setBackgroundColorDark(this.previewData.backgroundColorDark);
|
||||
req.setBackgroundColor(this.previewData.backgroundColor);
|
||||
|
||||
req.setFontColorDark(this.previewData.fontColorDark);
|
||||
req.setFontColor(this.previewData.fontColor);
|
||||
|
||||
req.setPrimaryColorDark(this.previewData.primaryColorDark);
|
||||
req.setPrimaryColor(this.previewData.primaryColor);
|
||||
|
||||
req.setWarnColorDark(this.previewData.warnColorDark);
|
||||
req.setWarnColor(this.previewData.warnColor);
|
||||
|
||||
req.setDisableWatermark(this.previewData.disableWatermark);
|
||||
req.setHideLoginNameSuffix(this.previewData.hideLoginNameSuffix);
|
||||
}
|
||||
|
||||
public activatePolicy(): Promise<any> {
|
||||
// dialog warning
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).activateCustomLabelPolicy().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.ACTIVATED', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loadImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).activateLabelPolicy().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.ACTIVATED', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loadImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resetPolicy(): Promise<any> {
|
||||
return (this.service as ManagementService).resetLabelPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.RESET', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
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 { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
|
||||
import { DropzoneModule } from '../../../directives/dropzone/dropzone.module';
|
||||
import { DetailLayoutModule } from '../../detail-layout/detail-layout.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { InputModule } from '../../input/input.module';
|
||||
import { LinksModule } from '../../links/links.module';
|
||||
import { ColorComponent } from './color/color.component';
|
||||
import { PreviewComponent } from './preview/preview.component';
|
||||
import { PrivateLabelingPolicyRoutingModule } from './private-labeling-policy-routing.module';
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
PrivateLabelingPolicyComponent,
|
||||
PreviewComponent,
|
||||
ColorComponent,
|
||||
],
|
||||
imports: [
|
||||
PrivateLabelingPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
DropzoneModule,
|
||||
MatProgressSpinnerModule,
|
||||
LinksModule,
|
||||
MatExpansionModule,
|
||||
InfoSectionModule,
|
||||
HasFeaturePipeModule,
|
||||
],
|
||||
})
|
||||
export class PrivateLabelingPolicyModule { }
|
@@ -75,6 +75,38 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<div class="p-item card">
|
||||
<div class="avatar">
|
||||
<i class="icon las la-gem"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{'POLICY.PRIVATELABELING_POLICY.TITLE' | translate}}</span>
|
||||
<button mat-icon-button disabled>
|
||||
<i class="icon las la-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="desc">
|
||||
{{'POLICY.PRIVATELABELING_POLICY.DESCRIPTION' | translate}}</p>
|
||||
<!-- <cnsl-info-section class="warn"
|
||||
*ngIf="type == PolicyGridType.ORG && (['password_complexity_policy'] | hasFeature | async) == false"
|
||||
type="WARN">
|
||||
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'password_complexity_policy'})}}
|
||||
</cnsl-info-section> -->
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="btn-wrapper">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||
<button
|
||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.PRIVATELABEL ]"
|
||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
|
||||
<div class="p-item card">
|
||||
@@ -101,40 +133,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-container *ngIf="type === PolicyGridType.IAM">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<div class="p-item card">
|
||||
<div class="avatar">
|
||||
<i class="icon las la-envelope"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{'POLICY.LABEL.TITLE' | translate}}</span>
|
||||
<button mat-icon-button disabled>
|
||||
<i class="icon las la-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="desc">
|
||||
{{'POLICY.LABEL.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<cnsl-info-section class="warn"
|
||||
*ngIf="type == PolicyGridType.ORG && (['label_policy'] | hasFeature | async) == false" type="WARN">
|
||||
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy'})}}
|
||||
</cnsl-info-section>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="btn-wrapper">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||
<button
|
||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LABEL ]"
|
||||
mat-stroked-button
|
||||
[disabled]="type == PolicyGridType.ORG && (['label_policy'] | hasFeature | async) == false">
|
||||
{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
@@ -1,51 +1,58 @@
|
||||
import { PolicyComponentType } from '../policies/policy-component-types.enum';
|
||||
|
||||
export const IAM_COMPLEXITY_LINK = {
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_LOGIN_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_LABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LABEL],
|
||||
withRole: ['iam.policy.read'],
|
||||
export const IAM_PRIVATELABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.PRIVATELABEL],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
|
||||
export const ORG_COMPLEXITY_LINK = {
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['policy.read'],
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
||||
export const ORG_IAM_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const ORG_LOGIN_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['policy.read'],
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
||||
|
||||
export const ORG_PRIVATELABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.PRIVATELABEL],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
@@ -9,119 +9,119 @@ import { EventstoreComponent } from './eventstore/eventstore.component';
|
||||
import { IamComponent } from './iam.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'policies',
|
||||
component: IamComponent,
|
||||
{
|
||||
path: 'policies',
|
||||
component: IamComponent,
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'eventstore',
|
||||
component: EventstoreComponent,
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./iam-members/iam-members.module').then(m => m.IamMembersModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
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: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
roles: ['iam.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'eventstore',
|
||||
component: EventstoreComponent,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./iam-members/iam-members.module').then(m => m.IamMembersModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
roles: ['iam.member.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
// canActivate: [RoleGuard],
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
roles: ['iam.features.read'],
|
||||
serviceType: FeatureServiceType.ADMIN,
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/label-policy/label-policy.module')
|
||||
.then(m => m.LabelPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.PRIVATELABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/private-labeling-policy/private-labeling-policy.module')
|
||||
.then(m => m.PrivateLabelingPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class IamRoutingModule { }
|
||||
|
@@ -73,7 +73,7 @@ h2 {
|
||||
|
||||
.new-desc {
|
||||
font-size: 14px;
|
||||
color: #818a8a;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.custom-domain-deactivated {
|
||||
|
@@ -8,108 +8,116 @@ import { OrgCreateComponent } from './org-create/org-create.component';
|
||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
{
|
||||
path: 'create',
|
||||
component: OrgCreateComponent,
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['(org.create)?(iam.write)?'],
|
||||
},
|
||||
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
component: OrgCreateComponent,
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['(org.create)?(iam.write)?'],
|
||||
roles: ['org.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['org.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['features.read'],
|
||||
serviceType: FeatureServiceType.MGMT,
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./org-members/org-members.module').then(m => m.OrgMembersModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: OrgDetailComponent,
|
||||
},
|
||||
{
|
||||
path: 'overview',
|
||||
loadChildren: () => import('./org-list/org-list.module').then(m => m.OrgListModule),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.PRIVATELABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/private-labeling-policy/private-labeling-policy.module')
|
||||
.then(m => m.PrivateLabelingPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./org-members/org-members.module').then(m => m.OrgMembersModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: OrgDetailComponent,
|
||||
},
|
||||
{
|
||||
path: 'overview',
|
||||
loadChildren: () => import('./org-list/org-list.module').then(m => m.OrgListModule),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrgsRoutingModule { }
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
106
console/src/app/services/upload.service.ts
Normal file
106
console/src/app/services/upload.service.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Org } from '../proto/generated/zitadel/org_pb';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
const ORG_STORAGE_KEY = 'organization';
|
||||
const authorizationKey = 'Authorization';
|
||||
const orgKey = 'x-zitadel-orgid';
|
||||
|
||||
const bearerPrefix = 'Bearer';
|
||||
const accessTokenStorageKey = 'access_token';
|
||||
|
||||
export enum UploadEndpoint {
|
||||
IAMFONT = 'iam/policy/label/font',
|
||||
MGMTFONT = 'org/policy/label/font',
|
||||
|
||||
IAMDARKLOGO = 'iam/policy/label/logo/dark',
|
||||
IAMLIGHTLOGO = 'iam/policy/label/logo',
|
||||
IAMDARKICON = 'iam/policy/label/icon/dark',
|
||||
IAMLIGHTICON = 'iam/policy/label/icon',
|
||||
|
||||
MGMTDARKLOGO = 'org/policy/label/logo/dark',
|
||||
MGMTLIGHTLOGO = 'org/policy/label/logo',
|
||||
MGMTDARKICON = 'org/policy/label/icon/dark',
|
||||
MGMTLIGHTICON = 'org/policy/label/icon',
|
||||
}
|
||||
|
||||
export enum DownloadEndpoint {
|
||||
IAMFONT = 'iam/policy/label/font',
|
||||
MGMTFONT = 'org/policy/label/font',
|
||||
|
||||
IAMDARKLOGO = 'iam/policy/label/logo/dark',
|
||||
IAMLOGO = 'iam/policy/label/logo',
|
||||
IAMDARKICON = 'iam/policy/label/icon/dark',
|
||||
IAMICON = 'iam/policy/label/icon',
|
||||
|
||||
MGMTDARKLOGO = 'org/policy/label/logo/dark',
|
||||
MGMTLOGO = 'org/policy/label/logo',
|
||||
MGMTDARKICON = 'org/policy/label/icon/dark',
|
||||
MGMTICON = 'org/policy/label/icon',
|
||||
|
||||
IAMDARKLOGOPREVIEW = 'iam/policy/label/logo/dark/_preview',
|
||||
IAMLOGOPREVIEW = 'iam/policy/label/logo/_preview',
|
||||
IAMDARKICONPREVIEW = 'iam/policy/label/icon/dark/_preview',
|
||||
IAMICONPREVIEW = 'iam/policy/label/icon/_preview',
|
||||
|
||||
MGMTDARKLOGOPREVIEW = 'org/policy/label/logo/dark/_preview',
|
||||
MGMTLOGOPREVIEW = 'org/policy/label/logo/_preview',
|
||||
MGMTDARKICONPREVIEW = 'org/policy/label/icon/dark/_preview',
|
||||
MGMTICONPREVIEW = 'org/policy/label/icon/_preview',
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UploadService {
|
||||
private serviceUrl: string = '';
|
||||
private accessToken: string = '';
|
||||
private org!: Org.AsObject;
|
||||
constructor(private http: HttpClient, private storageService: StorageService) {
|
||||
|
||||
http.get('./assets/environment.json')
|
||||
.toPromise().then((data: any) => {
|
||||
if (data && data.uploadServiceUrl) {
|
||||
this.serviceUrl = data.uploadServiceUrl;
|
||||
const aT = this.storageService.getItem(accessTokenStorageKey);
|
||||
|
||||
if (aT) {
|
||||
this.accessToken = aT;
|
||||
}
|
||||
|
||||
const org: Org.AsObject | null = (this.storageService.getItem(ORG_STORAGE_KEY));
|
||||
|
||||
if (org) {
|
||||
this.org = org;
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
public upload(endpoint: UploadEndpoint, body: any): Promise<any> {
|
||||
return this.http.post(`${this.serviceUrl}/assets/v1/${endpoint}`,
|
||||
body,
|
||||
{
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${this.accessToken}`,
|
||||
[orgKey]: `${this.org.id}`,
|
||||
},
|
||||
}).toPromise();
|
||||
}
|
||||
|
||||
public load(endpoint: string): Promise<any> {
|
||||
return this.http.get(`${this.serviceUrl}/assets/v1/${endpoint}`,
|
||||
|
||||
{
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${this.accessToken}`,
|
||||
[orgKey]: `${this.org.id}`,
|
||||
},
|
||||
}).toPromise();
|
||||
}
|
||||
}
|
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"authServiceUrl": "https://api.zitadel.dev",
|
||||
"mgmtServiceUrl": "https://api.zitadel.dev",
|
||||
"adminServiceUrl":"https://api.zitadel.dev",
|
||||
"subscriptionServiceUrl":"https://sub.zitadel.dev",
|
||||
"issuer": "https://issuer.zitadel.dev",
|
||||
"clientid": "70669160379706195@zitadel"
|
||||
"authServiceUrl": "https://api.zitadel.io",
|
||||
"mgmtServiceUrl": "https://api.zitadel.io",
|
||||
"adminServiceUrl":"https://api.zitadel.io",
|
||||
"subscriptionServiceUrl":"https://sub.zitadel.io",
|
||||
"uploadServiceUrl":"https://api.zitadel.io",
|
||||
"issuer": "https://issuer.zitadel.io",
|
||||
"clientid": "69234247558357051@zitadel"
|
||||
}
|
||||
|
@@ -104,8 +104,9 @@
|
||||
}
|
||||
},
|
||||
"ACTIONS": {
|
||||
"SHOW":"Aufklappen",
|
||||
"HIDE":"Zuklappen",
|
||||
"SET":"Übernehmen",
|
||||
"SHOW":"Aufklappen",
|
||||
"HIDE":"Zuklappen",
|
||||
"SAVE": "Speichern",
|
||||
"SAVENOW": "Speichern",
|
||||
"NEW": "Neu",
|
||||
@@ -613,7 +614,8 @@
|
||||
"LOGINPOLICYFACTORS": "Login Richtlinie: Mltifaktoren - benutzerdefiniert",
|
||||
"LOGINPOLICYPASSWORDLESS": "Login Richtlinie: Passwortlose Authentifizierung - benutzerdefiniert",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Passwortkomplexitäts Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICY": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen",
|
||||
"CUSTOMDOMAIN": "Domänen Verifikation - verfügbar"
|
||||
},
|
||||
"TIERSTATES": {
|
||||
@@ -636,6 +638,28 @@
|
||||
"NUMBERERROR": "Das Password muss eine Ziffer beinhalten.",
|
||||
"PATTERNERROR": "Das Passwort erfüllt nicht die vorgeschriebene Richtlinie."
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE":"Private Labeling",
|
||||
"DESCRIPTION":"Verleihe dem Login deinen benutzerdefinierten Style und passe das Verhalten an.",
|
||||
"PREVIEW_DESCRIPTION":"Änderungen dieser Richtlinie werden automatisch in der Preview Umgebung verfügbar. Um die Preview zu Testen muss dem login flow der scope 'x-preview' mitgegeben werden.",
|
||||
"BTN":"Datei auswählen",
|
||||
"ACTIVATEPREVIEW":"Konfiguration übernehmen",
|
||||
"DARK":"Dunkler Modus",
|
||||
"LIGHT":"Heller Modus",
|
||||
"CHANGEVIEW":"Ansicht wechseln",
|
||||
"ACTIVATED":"Richtlinie wurde LIVE geschaltet",
|
||||
"THEME":"Modus",
|
||||
"COLORS":"Farben",
|
||||
"FONT":"Schrift",
|
||||
"ADVANCEDBEHAVIOR":"Erweitertes Verhalten",
|
||||
"PREVIEW": {
|
||||
"TITLE":"Anmeldung",
|
||||
"SECOND":"mit ZITADEL-Konto anmelden.",
|
||||
"ERROR":"Benutzer konnte nicht gefunden werden!",
|
||||
"PRIMARYBUTTON":"weiter",
|
||||
"SECONDARYBUTTON":"registrieren"
|
||||
}
|
||||
},
|
||||
"PWD_AGE": {
|
||||
"TITLE": "Gültigkeitsdauer für Passwörter",
|
||||
"DESCRIPTION": "Du kannst eine Richtlinie für die maximale Gültigkeitsdauer von Passwörtern festlegen. Diese Richtlinie löst eine Warnung nach Ablauf einer festgelegten Gültigkeitsdauer aus."
|
||||
@@ -648,6 +672,10 @@
|
||||
"TITLE": "Zugangseinstellungen IAM",
|
||||
"DESCRIPTION": "Definiere die Zugangseistellungen für Benutzer."
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Private Labeling",
|
||||
"DESCRIPTION": "Definiere das Erscheinungsbild des Logins."
|
||||
},
|
||||
"LOGIN_POLICY": {
|
||||
"TITLE": "Login Richtlinien",
|
||||
"DESCRIPTION": "Definiere die Loginmethoden für Benutzer",
|
||||
@@ -655,13 +683,6 @@
|
||||
"DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"LABEL": {
|
||||
"TITLE": "Email Labelling Einstellungen",
|
||||
"DESCRIPTION": "Definieren Sie das Erscheinungsbild Ihrer Benachrichtigungs-Mails",
|
||||
"PRIMARYCOLOR": "Hintergrundfarbe",
|
||||
"SECONDARYCOLOR": "Schriftfarbe",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"DEFAULTLABEL": "Die aktuelle Richtlinie entspricht der IAM-Standard Einstellung.",
|
||||
"BTN_INSTALL": "Installieren",
|
||||
"BTN_EDIT": "Modifizieren",
|
||||
@@ -685,14 +706,19 @@
|
||||
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
|
||||
"FORCEMFA": "Mfa erzwingen",
|
||||
"FORCEMFA_DESC": "Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden.",
|
||||
"HIDEPASSWORDRESET": "Passwort vergessen, nicht anzeigen",
|
||||
"FORCEMFA_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link."
|
||||
"HIDEPASSWORDRESET": "Passwort vergessen ausblenden",
|
||||
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
|
||||
"HIDELOGINNAMESUFFIX":"Loginname Suffix ausblenden",
|
||||
"ERRORMSGPOPUP":"Fehler als Dialog Fenster",
|
||||
"DISABLEWATERMARK":"Wasserzeichen ausblenden"
|
||||
},
|
||||
"RESET": "Richtlinie zurücksetzen",
|
||||
"CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen",
|
||||
"TOAST": {
|
||||
"SET": "Richtline erfolgreich gesetzt!",
|
||||
"RESETSUCCESS": "Richtline zurückgesetzt!"
|
||||
"RESETSUCCESS": "Richtline zurückgesetzt!",
|
||||
"UPLOADSUCCESS": "Upload erfolgreich",
|
||||
"UPLOADFAILED":"Upload fehlgeschlagen!"
|
||||
}
|
||||
},
|
||||
"ORG_DETAIL": {
|
||||
|
@@ -104,6 +104,7 @@
|
||||
}
|
||||
},
|
||||
"ACTIONS": {
|
||||
"SET":"Set",
|
||||
"SHOW":"Show",
|
||||
"HIDE":"Hide",
|
||||
"SAVE": "Save",
|
||||
@@ -613,7 +614,8 @@
|
||||
"LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom",
|
||||
"LOGINPOLICYPASSWORDLESS": "Login Policy: Passwordless Authentication - custom",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Password Complexity Policy - custom",
|
||||
"LABELPOLICY": "Labeling Policy - custom",
|
||||
"LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen",
|
||||
"CUSTOMDOMAIN": "Domain Verification - available"
|
||||
},
|
||||
"TIERSTATES": {
|
||||
@@ -636,6 +638,28 @@
|
||||
"NUMBERERROR": "The password must include a digit.",
|
||||
"PATTERNERROR": "The password does not meet the required pattern."
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE":"Private Labeling",
|
||||
"DESCRIPTION":"Give the login your personalized style and modify its behavior.",
|
||||
"PREVIEW_DESCRIPTION":"Changes of the policy will automatically deployed to preview environment. To view those changes, a 'x-preview' scope will have to be added to your login scopes.",
|
||||
"BTN":"Select File",
|
||||
"ACTIVATEPREVIEW":"Set preview as current configuration",
|
||||
"DARK":"Dark Mode",
|
||||
"LIGHT":"Lighg Mode",
|
||||
"CHANGEVIEW":"Change View",
|
||||
"ACTIVATED":"Policy changes are now LIVE",
|
||||
"THEME":"Theme",
|
||||
"COLORS":"Colors",
|
||||
"FONT":"Font",
|
||||
"ADVANCEDBEHAVIOR":"Advanced Behavior",
|
||||
"PREVIEW": {
|
||||
"TITLE":"Login",
|
||||
"SECOND":"login with your ZITADEL-Account.",
|
||||
"ERROR":"User could not be found!",
|
||||
"PRIMARYBUTTON":"next",
|
||||
"SECONDARYBUTTON":"register"
|
||||
}
|
||||
},
|
||||
"PWD_AGE": {
|
||||
"TITLE": "Password Aging",
|
||||
"DESCRIPTION": "You can set a policy for the aging of passwords. This policy emits a warning after the specific aging time has elapsed."
|
||||
@@ -648,6 +672,12 @@
|
||||
"TITLE": "IAM Access Preferences",
|
||||
"DESCRIPTION": "Define access properties of your users."
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Private Labeling",
|
||||
"BTN":"Select File",
|
||||
"DESCRIPTION": "Customize the appearance of the Login",
|
||||
"ACTIVATEPREVIEW":"Activate Configuration"
|
||||
},
|
||||
"LOGIN_POLICY": {
|
||||
"TITLE": "Login Policy",
|
||||
"DESCRIPTION": "Define how Users can be authenticated and configure Identity Providers",
|
||||
@@ -655,13 +685,6 @@
|
||||
"DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organisation only.",
|
||||
"SAVED": "Saved successfully!"
|
||||
},
|
||||
"LABEL": {
|
||||
"TITLE": "Email Labelling Settings",
|
||||
"DESCRIPTION": "Change the look of your emails.",
|
||||
"PRIMARYCOLOR": "Background color",
|
||||
"SECONDARYCOLOR": "Font color",
|
||||
"SAVED": "Saved successfully"
|
||||
},
|
||||
"DEFAULTLABEL": "The currently set guideline corresponds to the standard setting set by the IAM Administrator.",
|
||||
"BTN_INSTALL": "Setup",
|
||||
"BTN_EDIT": "Modify",
|
||||
@@ -686,13 +709,18 @@
|
||||
"FORCEMFA": "Force MFA",
|
||||
"FORCEMFA_DESC": "If the option is selected, users have to configure a second factor for login.",
|
||||
"HIDEPASSWORDRESET": "Hide Password reset",
|
||||
"FORCEMFA_DESC": "If the option is selected, the user can't reset his password in the login process."
|
||||
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
||||
"HIDELOGINNAMESUFFIX":"Hide Loginname suffix",
|
||||
"ERRORMSGPOPUP":"Show Error in Dialog",
|
||||
"DISABLEWATERMARK":"Disable Watermark"
|
||||
},
|
||||
"RESET": "Reset Policy",
|
||||
"CREATECUSTOM": "Create Custom Policy",
|
||||
"TOAST": {
|
||||
"SET": "Policy set successfully!",
|
||||
"RESETSUCCESS": "Policy reset successfully!"
|
||||
"RESETSUCCESS": "Policy reset successfully!",
|
||||
"UPLOADSUCCESS": "Uploaded successfully!",
|
||||
"UPLOADFAILED":"Upload failed!"
|
||||
}
|
||||
},
|
||||
"ORG_DETAIL": {
|
||||
|
@@ -19,6 +19,7 @@
|
||||
@import 'src/app/modules/meta-layout/meta.scss';
|
||||
@import 'src/app/modules/zitadel-tier/zitadel-tier.component.scss';
|
||||
@import 'src/app/modules/onboarding/onboarding.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss';
|
||||
|
||||
@mixin component-themes($theme) {
|
||||
@include avatar-theme($theme);
|
||||
@@ -42,4 +43,5 @@
|
||||
@include info-section-theme($theme);
|
||||
@include onboarding-theme($theme);
|
||||
@include tier-theme($theme);
|
||||
@include private-label-theme($theme);
|
||||
}
|
||||
|
@@ -146,7 +146,6 @@ $dark-accent: mat.define-palette(mat.$pink-palette);
|
||||
$dark-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
$light-theme: mat.define-light-theme($light-primary, $light-accent, $light-warn);
|
||||
|
||||
$dark-theme: mat.define-dark-theme($dark-primary, $dark-accent, $dark-warn);
|
||||
|
||||
$custom-typography: mat.define-typography-config($font-family: 'Lato');
|
||||
|
Reference in New Issue
Block a user