feat(console): show available login methods on user pages (#5055)

Shows all possible login methods on the user detail pages

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner
2023-01-17 17:02:22 +01:00
committed by GitHub
parent 0316c2c187
commit 650122070d
12 changed files with 49 additions and 4 deletions

View File

@@ -33,8 +33,8 @@
</div> </div>
<div class="info-wrapper width"> <div class="info-wrapper width">
<p class="info-row-title">{{ 'USER.PAGES.LOGINNAMES' | translate }}</p> <p class="info-row-title">{{ 'USER.PAGES.LOGINMETHODS' | translate }}</p>
<div class="copy-row" *ngFor="let login of user?.loginNamesList"> <div class="copy-row" *ngFor="let login of loginMethods">
<button <button
[disabled]="copied === login" [disabled]="copied === login"
[matTooltip]="(copied !== login ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate" [matTooltip]="(copied !== login ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"

View File

@@ -3,6 +3,7 @@ import { App, AppState } from 'src/app/proto/generated/zitadel/app_pb';
import { IDP, IDPState } from 'src/app/proto/generated/zitadel/idp_pb'; import { IDP, IDPState } from 'src/app/proto/generated/zitadel/idp_pb';
import { InstanceDetail, State } from 'src/app/proto/generated/zitadel/instance_pb'; import { InstanceDetail, State } from 'src/app/proto/generated/zitadel/instance_pb';
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb'; import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
import { LoginPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { GrantedProject, Project, ProjectGrantState, ProjectState } from 'src/app/proto/generated/zitadel/project_pb'; import { GrantedProject, Project, ProjectGrantState, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
import { User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; import { User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
@@ -19,6 +20,7 @@ export class InfoRowComponent {
@Input() public idp!: IDP.AsObject; @Input() public idp!: IDP.AsObject;
@Input() public project!: Project.AsObject; @Input() public project!: Project.AsObject;
@Input() public grantedProject!: GrantedProject.AsObject; @Input() public grantedProject!: GrantedProject.AsObject;
@Input() public loginPolicy?: LoginPolicy.AsObject;
public UserState: any = UserState; public UserState: any = UserState;
public State: any = State; public State: any = State;
@@ -31,4 +33,19 @@ export class InfoRowComponent {
public copied: string = ''; public copied: string = '';
constructor() {} constructor() {}
public get loginMethods(): Set<string> {
const methods = this.user?.loginNamesList;
let email: string = '';
let phone: string = '';
if (this.loginPolicy) {
if (!this.loginPolicy?.disableLoginWithEmail && this.user.human?.email?.email) {
email = this.user.human?.email?.email;
}
if (!this.loginPolicy?.disableLoginWithPhone && this.user.human?.phone?.phone) {
phone = this.user.human?.phone?.phone;
}
}
return new Set([email, phone, ...methods].filter((method) => !!method));
}
} }

View File

@@ -8,7 +8,7 @@
(backRouterLink)="(['/'])" (backRouterLink)="(['/'])"
> >
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span> <span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<cnsl-info-row topContent *ngIf="user" [user]="user"></cnsl-info-row> <cnsl-info-row topContent *ngIf="user" [user]="user" [loginPolicy]="loginPolicy"></cnsl-info-row>
</cnsl-top-view> </cnsl-top-view>
<div *ngIf="loading" class="max-width-container"> <div *ngIf="loading" class="max-width-container">

View File

@@ -20,6 +20,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.component'; import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.component';
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum'; import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
import { LoginPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
@Component({ @Component({
selector: 'cnsl-auth-user-detail', selector: 'cnsl-auth-user-detail',
@@ -58,6 +59,7 @@ export class AuthUserDetailComponent implements OnDestroy {
}, },
]; ];
public currentSetting: string | undefined = this.settingsList[0].id; public currentSetting: string | undefined = this.settingsList[0].id;
public loginPolicy?: LoginPolicy.AsObject;
constructor( constructor(
public translate: TranslateService, public translate: TranslateService,
@@ -93,6 +95,12 @@ export class AuthUserDetailComponent implements OnDestroy {
this.userService.getSupportedLanguages().then((lang) => { this.userService.getSupportedLanguages().then((lang) => {
this.languages = lang.languagesList; this.languages = lang.languagesList;
}); });
this.userService.getMyLoginPolicy().then((policy) => {
if (policy.policy) {
this.loginPolicy = policy.policy;
}
});
} }
private changeSelection(small: boolean): void { private changeSelection(small: boolean): void {

View File

@@ -33,7 +33,7 @@
</button> </button>
</ng-template> </ng-template>
</ng-template> </ng-template>
<cnsl-info-row topContent *ngIf="user" [user]="user"></cnsl-info-row> <cnsl-info-row topContent *ngIf="user" [user]="user" [loginPolicy]="loginPolicy"></cnsl-info-row>
</cnsl-top-view> </cnsl-top-view>
<div *ngIf="loading" class="max-width-container"> <div *ngIf="loading" class="max-width-container">

View File

@@ -20,6 +20,7 @@ import { ToastService } from 'src/app/services/toast.service';
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { EditDialogComponent, EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.component'; import { EditDialogComponent, EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.component';
import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component'; import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dialog/resend-email-dialog.component';
import { LoginPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
const GENERAL: SidenavSetting = { id: 'general', i18nKey: 'USER.SETTINGS.GENERAL' }; const GENERAL: SidenavSetting = { id: 'general', i18nKey: 'USER.SETTINGS.GENERAL' };
const GRANTS: SidenavSetting = { id: 'grants', i18nKey: 'USER.SETTINGS.USERGRANTS' }; const GRANTS: SidenavSetting = { id: 'grants', i18nKey: 'USER.SETTINGS.USERGRANTS' };
@@ -58,6 +59,7 @@ export class UserDetailComponent implements OnInit {
public settingsList: SidenavSetting[] = [GENERAL, GRANTS, MEMBERSHIPS, METADATA]; public settingsList: SidenavSetting[] = [GENERAL, GRANTS, MEMBERSHIPS, METADATA];
public currentSetting: string | undefined = 'general'; public currentSetting: string | undefined = 'general';
public loginPolicy?: LoginPolicy.AsObject;
constructor( constructor(
public translate: TranslateService, public translate: TranslateService,
@@ -137,6 +139,12 @@ export class UserDetailComponent implements OnInit {
public ngOnInit(): void { public ngOnInit(): void {
this.refreshUser(); this.refreshUser();
this.mgmtUserService.getLoginPolicy().then((policy) => {
if (policy.policy) {
this.loginPolicy = policy.policy;
}
});
} }
public changeUsername(): void { public changeUsername(): void {

View File

@@ -30,6 +30,8 @@ import {
GetMyEmailResponse, GetMyEmailResponse,
GetMyLabelPolicyRequest, GetMyLabelPolicyRequest,
GetMyLabelPolicyResponse, GetMyLabelPolicyResponse,
GetMyLoginPolicyRequest,
GetMyLoginPolicyResponse,
GetMyPasswordComplexityPolicyRequest, GetMyPasswordComplexityPolicyRequest,
GetMyPasswordComplexityPolicyResponse, GetMyPasswordComplexityPolicyResponse,
GetMyPhoneRequest, GetMyPhoneRequest,
@@ -487,6 +489,11 @@ export class GrpcAuthService {
return this.grpcService.auth.getSupportedLanguages(req, null).then((resp) => resp.toObject()); return this.grpcService.auth.getSupportedLanguages(req, null).then((resp) => resp.toObject());
} }
public getMyLoginPolicy(): Promise<GetMyLoginPolicyResponse.AsObject> {
const req = new GetMyLoginPolicyRequest();
return this.grpcService.auth.getMyLoginPolicy(req, null).then((resp) => resp.toObject());
}
public removeMyPhone(): Promise<RemoveMyPhoneResponse.AsObject> { public removeMyPhone(): Promise<RemoveMyPhoneResponse.AsObject> {
return this.grpcService.auth.removeMyPhone(new RemoveMyPhoneRequest(), null).then((resp) => resp.toObject()); return this.grpcService.auth.removeMyPhone(new RemoveMyPhoneRequest(), null).then((resp) => resp.toObject());
} }

View File

@@ -231,6 +231,7 @@
"CREATE": "Erstellen", "CREATE": "Erstellen",
"MY": "Meine Informationen", "MY": "Meine Informationen",
"LOGINNAMES": "Login-Namen", "LOGINNAMES": "Login-Namen",
"LOGINMETHODS": "Anmeldemethoden",
"LOGINNAMESDESC": "Mit diesen Namen kannst Du Dich anmelden.", "LOGINNAMESDESC": "Mit diesen Namen kannst Du Dich anmelden.",
"NOUSER": "Kein Benutzer", "NOUSER": "Kein Benutzer",
"REACTIVATE": "Reaktivieren", "REACTIVATE": "Reaktivieren",

View File

@@ -231,6 +231,7 @@
"CREATE": "Create", "CREATE": "Create",
"MY": "My Information", "MY": "My Information",
"LOGINNAMES": "Login names", "LOGINNAMES": "Login names",
"LOGINMETHODS": "Login methods",
"LOGINNAMESDESC": "These are your login names:", "LOGINNAMESDESC": "These are your login names:",
"NOUSER": "No associated user.", "NOUSER": "No associated user.",
"REACTIVATE": "Reactivate", "REACTIVATE": "Reactivate",

View File

@@ -231,6 +231,7 @@
"CREATE": "Créer", "CREATE": "Créer",
"MY": "Mes informations", "MY": "Mes informations",
"LOGINNAMES": "Noms de connexion", "LOGINNAMES": "Noms de connexion",
"LOGINMETHODS": "Méthodes de connexion",
"LOGINNAMESDESC": "Ce sont vos noms de connexion", "LOGINNAMESDESC": "Ce sont vos noms de connexion",
"NOUSER": "Aucun utilisateur associé.", "NOUSER": "Aucun utilisateur associé.",
"REACTIVATE": "Réactiver", "REACTIVATE": "Réactiver",

View File

@@ -231,6 +231,7 @@
"CREATE": "Crea", "CREATE": "Crea",
"MY": "Le mie informazioni", "MY": "Le mie informazioni",
"LOGINNAMES": "Loginnames", "LOGINNAMES": "Loginnames",
"LOGINMETHODS": "Metodi di accesso",
"LOGINNAMESDESC": "Questi sono i nomi di accesso:", "LOGINNAMESDESC": "Questi sono i nomi di accesso:",
"NOUSER": "Nessun utente associato.", "NOUSER": "Nessun utente associato.",
"REACTIVATE": "Riattiva", "REACTIVATE": "Riattiva",

View File

@@ -231,6 +231,7 @@
"CREATE": "创建", "CREATE": "创建",
"MY": "我的信息", "MY": "我的信息",
"LOGINNAMES": "登录名称", "LOGINNAMES": "登录名称",
"LOGINMETHODS": "登录方式",
"LOGINNAMESDESC": "这些是您的登录名:", "LOGINNAMESDESC": "这些是您的登录名:",
"NOUSER": "没有关联的用户。", "NOUSER": "没有关联的用户。",
"REACTIVATE": "重新启用", "REACTIVATE": "重新启用",