fix(console): login policy, idp type filter (#875)

* cleanup login policy

* detail layout

* disable if no custom

* fix login policy filter

* add styletype to idp

* stylelint

* lint
This commit is contained in:
Max Peintner 2020-10-20 11:37:17 +02:00 committed by GitHub
parent 46bc987b28
commit f8616265be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 184 additions and 131 deletions

View File

@ -168,7 +168,7 @@
.router { .router {
height: 100%; height: 100%;
overflow: auto; overflow-y: auto;
} }
} }

View File

@ -1,14 +1,16 @@
<div class="max-width-container detail-container"> <div class="max-width-container">
<div class="detail-left"> <div class="detail-container">
<a *ngIf="backRouterLink" [routerLink]="backRouterLink" mat-icon-button> <div class="detail-left">
<mat-icon class="icon">arrow_back</mat-icon> <a *ngIf="backRouterLink" [routerLink]="backRouterLink" mat-icon-button>
</a> <mat-icon class="icon">arrow_back</mat-icon>
</div> </a>
<div class="detail-right"> </div>
<div class="head"> <div class="detail-right">
<h1>{{ title }}</h1> <div class="head">
<p class="desc">{{ description }}</p> <h1>{{ title }}</h1>
<ng-content></ng-content> <p class="desc">{{ description }}</p>
<ng-content></ng-content>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,6 +10,7 @@
$lighter-color: rgba(mat-color($primary, 300), .5); $lighter-color: rgba(mat-color($primary, 300), .5);
.detail-container { .detail-container {
width: 100%;
display: flex; display: flex;
padding-bottom: 3rem; padding-bottom: 3rem;
padding-top: 3rem; padding-top: 3rem;
@ -29,7 +30,6 @@
.detail-right { .detail-right {
flex: 1; flex: 1;
position: relative;
padding-left: 1rem; padding-left: 1rem;
@media only screen and (max-width: 500px) { @media only screen and (max-width: 500px) {

View File

@ -1,11 +1,10 @@
.table-wrapper { .table-wrapper {
overflow: auto; overflow: auto;
width: 100%;
.table, .table,
.paginator { .paginator {
width: 100%;
td, td,
th { th {
padding: 0 1rem; padding: 0 1rem;

View File

@ -63,7 +63,7 @@
<mat-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</mat-label> <mat-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</mat-label>
<mat-select formControlName="idpDisplayNameMapping"> <mat-select formControlName="idpDisplayNameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field"> <mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINTFIELD.'+field | translate }} {{ 'IDP.MAPPINGFIELD.'+field | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -71,7 +71,15 @@
<mat-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</mat-label> <mat-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</mat-label>
<mat-select formControlName="usernameMapping"> <mat-select formControlName="usernameMapping">
<mat-option *ngFor="let field of mappingFields" [value]="field"> <mat-option *ngFor="let field of mappingFields" [value]="field">
{{ 'IDP.MAPPINTFIELD.'+field | translate }} {{ 'IDP.MAPPINGFIELD.'+field | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'IDP.STYLE' | translate }}</mat-label>
<mat-select formControlName="usernameMapping">
<mat-option *ngFor="let field of styleFields" [value]="field">
{{ 'IDP.STYLEFIELD.'+field | translate }}
</mat-option> </mat-option>
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
@ -88,4 +96,4 @@
</ng-container> </ng-container>
</div> </div>
</app-detail-layout> </app-detail-layout>

View File

@ -1,20 +1,22 @@
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes'; import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import {Component, Injector, Input, OnDestroy, OnInit, Type} from '@angular/core'; import { Component, Injector, OnDestroy, OnInit, Type } from '@angular/core';
import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { switchMap, take } from 'rxjs/operators'; import { switchMap, take } from 'rxjs/operators';
import { import {
OIDCMappingField as authMappingFields, IdpStylingType as adminIdpStylingType,
OidcIdpConfigUpdate as AdminOidcIdpConfigUpdate, IdpUpdate as AdminIdpConfigUpdate,
IdpUpdate as AdminIdpConfigUpdate, OidcIdpConfigUpdate as AdminOidcIdpConfigUpdate,
OIDCMappingField as adminMappingFields,
} from 'src/app/proto/generated/admin_pb'; } from 'src/app/proto/generated/admin_pb';
import { import {
OIDCMappingField as mgmtMappingFields, IdpStylingType as mgmtIdpStylingType,
OidcIdpConfigUpdate as MgmtOidcIdpConfigUpdate, IdpUpdate as MgmtIdpConfigUpdate,
IdpUpdate as MgmtIdpConfigUpdate, OidcIdpConfigUpdate as MgmtOidcIdpConfigUpdate,
OIDCMappingField as mgmtMappingFields,
} from 'src/app/proto/generated/management_pb'; } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@ -28,7 +30,9 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
styleUrls: ['./idp.component.scss'], styleUrls: ['./idp.component.scss'],
}) })
export class IdpComponent implements OnInit, OnDestroy { export class IdpComponent implements OnInit, OnDestroy {
public mappingFields: mgmtMappingFields[] | authMappingFields[] = []; public mappingFields: mgmtMappingFields[] | adminMappingFields[] = [];
public styleFields: mgmtIdpStylingType[] | adminIdpStylingType[] = [];
public showIdSecretSection: boolean = false; public showIdSecretSection: boolean = false;
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
private service!: ManagementService | AdminService; private service!: ManagementService | AdminService;
@ -41,7 +45,6 @@ export class IdpComponent implements OnInit, OnDestroy {
public oidcConfigForm!: FormGroup; public oidcConfigForm!: FormGroup;
constructor( constructor(
// private router: Router,
private toast: ToastService, private toast: ToastService,
private injector: Injector, private injector: Injector,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -50,15 +53,16 @@ export class IdpComponent implements OnInit, OnDestroy {
this.idpForm = new FormGroup({ this.idpForm = new FormGroup({
id: new FormControl({ disabled: true, value: '' }, [Validators.required]), id: new FormControl({ disabled: true, value: '' }, [Validators.required]),
name: new FormControl('', [Validators.required]), name: new FormControl('', [Validators.required]),
stylingType: new FormControl('', [Validators.required]),
}); });
this.oidcConfigForm = new FormGroup({ this.oidcConfigForm = new FormGroup({
clientId: new FormControl('', [Validators.required]), clientId: new FormControl('', [Validators.required]),
clientSecret: new FormControl(''), clientSecret: new FormControl(''),
issuer: new FormControl('', [Validators.required]), issuer: new FormControl('', [Validators.required]),
scopesList: new FormControl([], []), scopesList: new FormControl([], []),
idpDisplayNameMapping: new FormControl(0), idpDisplayNameMapping: new FormControl(0),
usernameMapping: new FormControl(0), usernameMapping: new FormControl(0),
}); });
this.route.data.pipe(switchMap(data => { this.route.data.pipe(switchMap(data => {
@ -68,14 +72,20 @@ export class IdpComponent implements OnInit, OnDestroy {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
this.service = this.injector.get(ManagementService as Type<ManagementService>); this.service = this.injector.get(ManagementService as Type<ManagementService>);
this.mappingFields = [ this.mappingFields = [
mgmtMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME, mgmtMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME,
mgmtMappingFields.OIDCMAPPINGFIELD_EMAIL]; mgmtMappingFields.OIDCMAPPINGFIELD_EMAIL];
this.styleFields = [
mgmtIdpStylingType.IDPSTYLINGTYPE_UNSPECIFIED,
mgmtIdpStylingType.IDPSTYLINGTYPE_GOOGLE];
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>); this.service = this.injector.get(AdminService as Type<AdminService>);
this.mappingFields = [ this.mappingFields = [
authMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME, adminMappingFields.OIDCMAPPINGFIELD_PREFERRED_USERNAME,
authMappingFields.OIDCMAPPINGFIELD_EMAIL]; adminMappingFields.OIDCMAPPINGFIELD_EMAIL];
this.styleFields = [
adminIdpStylingType.IDPSTYLINGTYPE_UNSPECIFIED,
adminIdpStylingType.IDPSTYLINGTYPE_GOOGLE];
break; break;
} }
@ -84,11 +94,11 @@ export class IdpComponent implements OnInit, OnDestroy {
const { id } = params; const { id } = params;
if (id) { if (id) {
this.service.IdpByID(id).then(idp => { this.service.IdpByID(id).then(idp => {
const idpObject = idp.toObject(); const idpObject = idp.toObject();
this.idpForm.patchValue(idpObject); this.idpForm.patchValue(idpObject);
if (idpObject.oidcConfig) { if (idpObject.oidcConfig) {
this.oidcConfigForm.patchValue(idpObject.oidcConfig); this.oidcConfigForm.patchValue(idpObject.oidcConfig);
} }
}); });
} }
}); });
@ -120,9 +130,10 @@ export class IdpComponent implements OnInit, OnDestroy {
req.setId(this.id?.value); req.setId(this.id?.value);
req.setName(this.name?.value); req.setName(this.name?.value);
req.setStylingType(this.stylingType?.value);
this.service.UpdateIdp(req).then((idp) => { this.service.UpdateIdp(req).then((idp) => {
this.toast.showInfo('IDP.TOAST.SAVED', true); this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]); // this.router.navigate(['idp', ]);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
@ -130,31 +141,31 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
public updateOidcConfig(): void { public updateOidcConfig(): void {
let req: AdminOidcIdpConfigUpdate | MgmtOidcIdpConfigUpdate; let req: AdminOidcIdpConfigUpdate | MgmtOidcIdpConfigUpdate;
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
req = new MgmtOidcIdpConfigUpdate(); req = new MgmtOidcIdpConfigUpdate();
break; break;
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
req = new AdminOidcIdpConfigUpdate(); req = new AdminOidcIdpConfigUpdate();
break; break;
} }
req.setIdpId(this.id?.value); req.setIdpId(this.id?.value);
req.setClientId(this.clientId?.value); req.setClientId(this.clientId?.value);
req.setClientSecret(this.clientSecret?.value); req.setClientSecret(this.clientSecret?.value);
req.setIssuer(this.issuer?.value); req.setIssuer(this.issuer?.value);
req.setScopesList(this.scopesList?.value); req.setScopesList(this.scopesList?.value);
req.setUsernameMapping(this.usernameMapping?.value); req.setUsernameMapping(this.usernameMapping?.value);
req.setIdpDisplayNameMapping(this.idpDisplayNameMapping?.value); req.setIdpDisplayNameMapping(this.idpDisplayNameMapping?.value);
this.service.UpdateOidcIdpConfig(req).then((oidcConfig) => { this.service.UpdateOidcIdpConfig(req).then((oidcConfig) => {
this.toast.showInfo('IDP.TOAST.SAVED', true); this.toast.showInfo('IDP.TOAST.SAVED', true);
// this.router.navigate(['idp', ]); // this.router.navigate(['idp', ]);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
public close(): void { public close(): void {
@ -188,12 +199,10 @@ export class IdpComponent implements OnInit, OnDestroy {
public get backroutes(): string[] { public get backroutes(): string[] {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return ['/org', 'policy', 'login']; return ['/org', 'policy', 'login'];
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return ['/iam', 'policy', 'login']; return ['/iam', 'policy', 'login'];
break;
} }
return [];
} }
public get id(): AbstractControl | null { public get id(): AbstractControl | null {
@ -204,6 +213,10 @@ export class IdpComponent implements OnInit, OnDestroy {
return this.idpForm.get('name'); return this.idpForm.get('name');
} }
public get stylingType(): AbstractControl | null {
return this.idpForm.get('stylingType');
}
public get clientId(): AbstractControl | null { public get clientId(): AbstractControl | null {
return this.oidcConfigForm.get('clientId'); return this.oidcConfigForm.get('clientId');
} }
@ -221,10 +234,10 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
public get idpDisplayNameMapping(): AbstractControl | null { public get idpDisplayNameMapping(): AbstractControl | null {
return this.oidcConfigForm.get('idpDisplayNameMapping'); return this.oidcConfigForm.get('idpDisplayNameMapping');
} }
public get usernameMapping(): AbstractControl | null { public get usernameMapping(): AbstractControl | null {
return this.oidcConfigForm.get('usernameMapping'); return this.oidcConfigForm.get('usernameMapping');
} }
} }

View File

@ -6,7 +6,7 @@
<div mat-dialog-content> <div mat-dialog-content>
<mat-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline"> <mat-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
<mat-label>{{ 'IDP.TYPE' | translate }}</mat-label> <mat-label>{{ 'IDP.TYPE' | translate }}</mat-label>
<mat-select [(ngModel)]="idpType" (selectionChange)="loadIdps()" (selectionChange)="loadIdps()"> <mat-select [(ngModel)]="idpType" (selectionChange)="loadIdps()">
<mat-option *ngFor="let type of idpTypes" [value]="type"> <mat-option *ngFor="let type of idpTypes" [value]="type">
{{ 'IDP.TYPES.'+type | translate}} {{ 'IDP.TYPES.'+type | translate}}
</mat-option> </mat-option>

View File

@ -61,7 +61,9 @@ export class AddIdpDialogComponent {
query.setKey(IdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE); query.setKey(IdpSearchKey.IDPSEARCHKEY_PROVIDER_TYPE);
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
query.setValue(this.idpType.toString()); query.setValue(this.idpType.toString());
this.mgmtService.SearchIdps().then(idps => { console.log(this.idpType);
console.log(query.toObject());
this.mgmtService.SearchIdps(undefined, undefined, [query]).then(idps => {
this.availableIdps = idps.toObject().resultList; this.availableIdps = idps.toObject().resultList;
}); });
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {

View File

@ -1,69 +1,76 @@
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']" <app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam' : '/org']"
[title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate" [title]="'ORG.POLICY.LOGIN_POLICY.TITLECREATE' | translate"
[description]="(serviceType==PolicyComponentServiceType.MGMT ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate"> [description]="(serviceType==PolicyComponentServiceType.MGMT ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'ORG.POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate">
<p class="default" *ngIf="isDefault"> {{'ORG.POLICY.DEFAULTLABEL' | translate}}</p> <p class="default" *ngIf="isDefault"> {{'ORG.POLICY.DEFAULTLABEL' | translate}}</p>
<div class="spinner-wr"> <div class="spinner-wr">
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner> <mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
</div> </div>
<ng-template appHasRole [appHasRole]="['policy.delete']"> <ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT">
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" <ng-template appHasRole [appHasRole]="['policy.delete']">
matTooltip="{{'ORG.POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button> <button *ngIf="!isDefault" matTooltip="{{'ORG.POLICY.RESET' | translate}}" color="warn"
{{'ORG.POLICY.RESET' | translate}} (click)="removePolicy()" mat-stroked-button>
</button> {{'ORG.POLICY.RESET' | translate}}
</ng-template> </button>
</ng-template>
<ng-template appHasRole [appHasRole]="['policy.write']">
<button *ngIf="isDefault" matTooltip="{{'ORG.POLICY.CREATECUSTOM' | translate}}" (click)="savePolicy()"
mat-raised-button>
{{'ORG.POLICY.CREATECUSTOM' | translate}}
</button>
</ng-template>
</ng-container>
<div class="content" *ngIf="loginData"> <div class="content" *ngIf="loginData">
<div class="row"> <div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}</span> <mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="loginData.allowUsernamePassword"> [(ngModel)]="loginData.allowUsernamePassword">
{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}
</mat-slide-toggle> </mat-slide-toggle>
<p>{{'ORG.POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate}}</p>
</div> </div>
<div class="row"> <div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWREGISTER' | translate}}</span> <mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<span class="fill-space"></span> [(ngModel)]="loginData.allowRegister">
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="loginData.allowRegister"> {{'ORG.POLICY.DATA.ALLOWREGISTER' | translate}}
</mat-slide-toggle> </mat-slide-toggle>
<p> {{'ORG.POLICY.DATA.ALLOWREGISTER_DESC' | translate}} </p>
</div> </div>
<div class="row"> <div class="row">
<span class="left-desc">{{'ORG.POLICY.DATA.ALLOWEXTERNALIDP' | translate}}</span> <mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<span class="fill-space"></span>
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
[(ngModel)]="loginData.allowExternalIdp"> [(ngModel)]="loginData.allowExternalIdp">
{{'ORG.POLICY.DATA.ALLOWEXTERNALIDP' | translate}}
</mat-slide-toggle> </mat-slide-toggle>
<p> {{'ORG.POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} </p>
</div> </div>
</div> </div>
<h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3> <h3 class="subheader">{{'LOGINPOLICY.IDPS' | translate}}</h3>
<div class="idps"> <div class="idps">
<div class="idp" *ngFor="let idp of idps"> <div class="idp" [ngClass]="{'disabled': disabled}" *ngFor="let idp of idps">
<mat-icon (click)="removeIdp(idp)" class="rm"> <button [disabled]="disabled" mat-icon-button (click)="removeIdp(idp)" class="rm">
remove_circle</mat-icon> <mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">
<span>{{idp.name}}</span> remove_circle</mat-icon>
</button>
<span class="name">{{idp.name}}</span>
<span class="meta">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span> <span class="meta">{{ 'IDP.TYPE' | translate }}: {{ 'IDP.TYPES.'+idp.type | translate }}</span>
<span class="meta">{{ 'IDP.ID' | translate }}: {{idp.idpConfigId}}</span> <span class="meta">{{ 'IDP.ID' | translate }}: {{idp.idpConfigId}}</span>
</div> </div>
<div class="new-idp" (click)="openDialog()"> <div *ngIf="!disabled" class="new-idp" (click)="openDialog()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</div> </div>
</div> </div>
<div class="btn-container"> <button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
<button (click)="savePolicy()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
<ng-template appHasRole [appHasRole]="['org.idp.read']"> <ng-template appHasRole [appHasRole]="['org.idp.read']">
<app-card class="idp-table-card" title="{{ 'IDP.LIST.TITLE' | translate }}" <h2>{{ 'IDP.LIST.TITLE' | translate }}</h2>
description="{{ 'IDP.LIST.DESCRIPTION' | translate }}"> <p>{{ 'IDP.LIST.DESCRIPTION' | translate }}</p>
<app-idp-table [service]="service" [serviceType]="serviceType" <app-idp-table [service]="service" [serviceType]="serviceType"
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false"> [disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false">
</app-idp-table> </app-idp-table>
</app-card>
</ng-template> </ng-template>
</app-detail-layout> </app-detail-layout>

View File

@ -9,35 +9,24 @@
.content { .content {
padding-top: 1rem; padding-top: 1rem;
display: flex;
flex-direction: column;
width: 100%;
.row { .row {
display: flex; .toggle {
align-items: center; margin: .3rem 0;
padding: .3rem 0;
.left-desc {
font-size: .9rem;
} }
.fill-space { p {
flex: 1; margin-top: .5rem;
font-size: 14px;
color: var(--grey);
} }
} }
} }
.btn-container { .save-button {
display: flex; margin-top: 3rem;
justify-content: flex-end; display: block;
width: 100%; padding: .5rem 4rem;
button {
margin-top: 3rem;
display: block;
padding: .5rem 4rem;
}
} }
.idp-table-card { .idp-table-card {
@ -63,6 +52,12 @@
border-radius: .5rem; border-radius: .5rem;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
min-height: 70px;
min-width: 150px;
.name {
font-weight: 700;
}
span { span {
padding: 2px; padding: 2px;
@ -70,6 +65,7 @@
.meta { .meta {
font-size: 12px; font-size: 12px;
color: var(--grey);
} }
.rm { .rm {
@ -78,10 +74,16 @@
left: 0; left: 0;
transform: translateX(-50%) translateY(-50%); transform: translateX(-50%) translateY(-50%);
cursor: pointer; cursor: pointer;
&[disabled] {
display: none;
}
} }
&:hover { &:not(.disabled) {
background-color: #ffffff10; &:hover {
background-color: #ffffff10;
}
} }
img { img {

View File

@ -38,6 +38,7 @@ export class LoginPolicyComponent implements OnDestroy {
public idps: MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[] = []; public idps: MgmtIdpProviderView.AsObject[] | AdminIdpProviderView.AsObject[] = [];
public loading: boolean = false; public loading: boolean = false;
public disabled: boolean = true;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private toast: ToastService, private toast: ToastService,
@ -66,6 +67,7 @@ export class LoginPolicyComponent implements OnDestroy {
if (data) { if (data) {
this.loginData = data.toObject(); this.loginData = data.toObject();
this.loading = false; this.loading = false;
this.disabled = ((this.loginData as LoginPolicyView.AsObject)?.pb_default) ?? false;
} }
}); });
this.getIdps().then(idps => { this.getIdps().then(idps => {

View File

@ -469,9 +469,13 @@
"USERLOGINMUSTBEDOMAIN":"Benutzer-Login muss eine Domain sein", "USERLOGINMUSTBEDOMAIN":"Benutzer-Login muss eine Domain sein",
"ALLOWUSERNAMEPASSWORD":"Benutzername Password erlaubt", "ALLOWUSERNAMEPASSWORD":"Benutzername Password erlaubt",
"ALLOWEXTERNALIDP":"Externer IDP erlaubt", "ALLOWEXTERNALIDP":"Externer IDP erlaubt",
"ALLOWREGISTER":"Registrieren erlaubt" "ALLOWREGISTER":"Registrieren erlaubt",
"ALLOWUSERNAMEPASSWORD_DESC":"Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
"ALLOWEXTERNALIDP_DESC":"Der Login wird für die darunter liegenden Identity Provider erlaubt.",
"ALLOWREGISTER_DESC":"Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers."
}, },
"RESET":"Richtlinie zurücksetzen", "RESET":"Richtlinie zurücksetzen",
"CREATECUSTOM":"Benutzerdefinierte Richtlinie erstellen",
"TOAST":{ "TOAST":{
"SET":"Richtline erfolgreich gesetzt!", "SET":"Richtline erfolgreich gesetzt!",
"RESETSUCCESS":"Richtline zurückgesetzt!" "RESETSUCCESS":"Richtline zurückgesetzt!"
@ -709,10 +713,15 @@
"1":"aktiv", "1":"aktiv",
"2":"inaktiv" "2":"inaktiv"
}, },
"MAPPINTFIELD": { "MAPPINGFIELD": {
"1": "Preferred Username", "1": "Preferred Username",
"2": "Email" "2": "Email"
}, },
"STYLE":"Style",
"STYLEFIELD": {
"0": "kein Styling",
"1": "Google"
},
"TYPE":"Typ", "TYPE":"Typ",
"ID": "ID", "ID": "ID",
"NAME":"Name", "NAME":"Name",

View File

@ -469,9 +469,13 @@
"USERLOGINMUSTBEDOMAIN":"User Login must be Domain", "USERLOGINMUSTBEDOMAIN":"User Login must be Domain",
"ALLOWUSERNAMEPASSWORD":"Username Password allowed", "ALLOWUSERNAMEPASSWORD":"Username Password allowed",
"ALLOWEXTERNALIDP":"External IDP allowed", "ALLOWEXTERNALIDP":"External IDP allowed",
"ALLOWREGISTER":"Register allowed" "ALLOWREGISTER":"Register allowed",
"ALLOWUSERNAMEPASSWORD_DESC":"The conventional login with user name and password is allowed.",
"ALLOWEXTERNALIDP_DESC":"The login is allowed for the underlying identity providers",
"ALLOWREGISTER_DESC":"If the option is selected, an additional step for registering a user appears in the login."
}, },
"RESET":"Reset Policy", "RESET":"Reset Policy",
"CREATECUSTOM":"Create Custom Policy",
"TOAST":{ "TOAST":{
"SET":"Policy set successfully!", "SET":"Policy set successfully!",
"RESETSUCCESS":"Policy reset successfully!" "RESETSUCCESS":"Policy reset successfully!"
@ -713,6 +717,11 @@
"1": "Preferred Username", "1": "Preferred Username",
"2": "Email" "2": "Email"
}, },
"STYLE":"Style",
"STYLEFIELD": {
"0": "No Styling",
"1": "Google"
},
"TYPE":"Type", "TYPE":"Type",
"ID": "ID", "ID": "ID",
"NAME":"Name", "NAME":"Name",