mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-13 17:35:12 +00:00
feat: protos refactoring
* start with user * user first try done in all services * user, org, idp for discussion * remove unused stuff * bla * dockerbuild * rename search, get multiple to list... * add annotation * update proto dependencies * update proto dependencies * change proto imports * replace all old imports * fix go out * remove unused lines * correct protoc flags * grpc and openapi flags * go out source path relative * -p * remove dead code * sourcepath relative * ls * is onenapi the problem? * hobla * authoption output * wrong field name * gopf * correct option, add correct flags * small improvments * SIMPLYFY * relative path * gopf bin ich en tubel * correct path * default policies in admin * grpc generation in one file * remove non ascii * metadata on manipulations * correct auth_option import * fixes * larry * idp provider to idp * fix generate * admin and auth nearly done * admin and auth nearly done * gen * healthz * imports * deleted too much imports * fix org * add import * imports * import * naming * auth_opt * gopf * management * imports * _TYPE_UNSPECIFIED * improts * auth opts * management policies * imports * passwordlessType to MFAType * auth_opt * add user grant calls * add missing messages * result * fix option * improvements * ids * fix http * imports * fixes * fields * body * add fields * remove wrong member query * fix request response * fixes * add copy files * variable versions * generate all files * improvements * add dependencies * factors * user session * oidc information, iam * remove unused file * changes * enums * dockerfile * fix build * remove unused folder * update readme for build * move old server impl * add event type to change * some changes * start admin * remove wrong field * admin only list calls missing * fix proto numbers * surprisingly it compiles * service ts changes * admin mgmt * mgmt * auth manipulation and gets done, lists missing * validations and some field changes * validations * enum validations * remove todo * move proto files to proto/zitadel * change proto path in dockerfile * it compiles! * add validate import * remove duplicate import * fix protos * fix import * tests * cleanup * remove unimplemented methods * iam member multiple queries * all auth and admin calls * add initial password on crate human * message names * management user server * machine done * fix: todos (#1346) * fix: pub sub in new eventstore * fix: todos * fix: todos * fix: todos * fix: todos * fix: todos * fix tests * fix: search method domain * admin service, user import type typescript * admin changes * admin changes * fix: search method domain * more user grpc and begin org, fix configs * fix: return object details * org grpc * remove creation date add details * app * fix: return object details * fix: return object details * mgmt service, project members * app * fix: convert policies * project, members, granted projects, searches * fix: convert usergrants * fix: convert usergrants * auth user detail, user detail, mfa, second factor, auth * fix: convert usergrants * mfa, memberships, password, owned proj detail * fix: convert usergrants * project grant * missing details * changes, userview * idp table, keys * org list and user table filter * unify rest paths (#1381) * unify rest paths * post for all searches, mfa to multi_factor, secondfactor to second_factor * remove v1 * fix tests * rename api client key to app key * machine keys, age policy * user list, machine keys, changes * fix: org states * add default flag to policy * second factor to type * idp id * app type * unify ListQuery, ListDetails, ObjectDetails field names * user grants, apps, memberships * fix type params * metadata to detail, linke idps * api create, membership, app detail, create * idp, app, policy * queries, multi -> auth factors and missing fields * update converters * provider to user, remove old mgmt refs * temp remove authfactor dialog, build finish Co-authored-by: Max Peintner <[email protected]> Co-authored-by: Fabi <[email protected]> Co-authored-by: Livio Amstutz <[email protected]> Co-authored-by: Fabiennne <[email protected]>
This commit is contained in:
co-authored by
Max Peintner
Fabi
Livio Amstutz
Fabiennne
parent
9f417f3957
commit
dabd5920dc
@@ -3,7 +3,7 @@ import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { FailedEvent } from 'src/app/proto/generated/admin_pb';
|
||||
import { FailedEvent } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -32,13 +32,9 @@ export class FailedEventsComponent implements AfterViewInit {
|
||||
|
||||
public loadEvents(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.adminService.GetFailedEvents()).pipe(
|
||||
from(this.adminService.listFailedEvents()).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
// if (response.viewTimestamp) {
|
||||
// this.viewTimestamp = response.viewTimestamp;
|
||||
// }
|
||||
return response.failedEventsList;
|
||||
return resp?.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -49,7 +45,7 @@ export class FailedEventsComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
public cancelEvent(viewname: string, db: string, seq: number): void {
|
||||
this.adminService.RemoveFailedEvent(viewname, db, seq).then(() => {
|
||||
this.adminService.removeFailedEvent(viewname, db, seq).then(() => {
|
||||
this.toast.showInfo('IAM.FAILEDEVENTS.DELETESUCCESS', true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { IamMemberView } from 'src/app/proto/generated/admin_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
|
||||
/**
|
||||
@@ -10,10 +10,10 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
|
||||
export class IamMembersDataSource extends DataSource<Member.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public membersSubject: BehaviorSubject<IamMemberView.AsObject[]> = new BehaviorSubject<IamMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -27,14 +27,13 @@ export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
from(this.adminService.SearchIamMembers(pageSize, offset)).pipe(
|
||||
from(this.adminService.listIAMMembers(pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
return response.resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -49,7 +48,7 @@ export class IamMembersDataSource extends DataSource<IamMemberView.AsObject> {
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<IamMemberView.AsObject[]> {
|
||||
public connect(): Observable<Member.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { IamMember, IamMemberView } from 'src/app/proto/generated/admin_pb';
|
||||
import { ProjectMember, ProjectType, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -23,7 +24,7 @@ export class IamMembersComponent {
|
||||
public memberRoleOptions: string[] = [];
|
||||
public changePageFactory!: Function;
|
||||
public changePage: EventEmitter<void> = new EventEmitter();
|
||||
public selection: Array<IamMemberView.AsObject> = [];
|
||||
public selection: Array<Member.AsObject> = [];
|
||||
|
||||
constructor(private adminService: AdminService,
|
||||
private dialog: MatDialog,
|
||||
@@ -42,16 +43,16 @@ export class IamMembersComponent {
|
||||
}
|
||||
|
||||
public getRoleOptions(): void {
|
||||
this.adminService.GetIamMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.toObject().rolesList;
|
||||
this.adminService.listIAMMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.rolesList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
updateRoles(member: IamMemberView.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.adminService.ChangeIamMember(member.userId, selectionChange.value)
|
||||
.then((newmember: IamMember) => {
|
||||
updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.adminService.updateIAMMember(member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -60,7 +61,7 @@ export class IamMembersComponent {
|
||||
|
||||
public removeMemberSelection(): void {
|
||||
Promise.all(this.selection.map(member => {
|
||||
return this.adminService.RemoveIamMember(member.userId).then(() => {
|
||||
return this.adminService.removeIAMMember(member.userId).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
|
||||
this.changePage.emit();
|
||||
}).catch(error => {
|
||||
@@ -69,8 +70,8 @@ export class IamMembersComponent {
|
||||
}));
|
||||
}
|
||||
|
||||
public removeMember(member: ProjectMember.AsObject): void {
|
||||
this.adminService.RemoveIamMember(member.userId).then(() => {
|
||||
public removeMember(member: Member.AsObject): void {
|
||||
this.adminService.removeIAMMember(member.userId).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
@@ -90,12 +91,12 @@ export class IamMembersComponent {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { View } from 'src/app/proto/generated/admin_pb';
|
||||
import { View } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -35,9 +35,9 @@ export class IamViewsComponent implements AfterViewInit {
|
||||
|
||||
public loadViews(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.adminService.GetViews()).pipe(
|
||||
from(this.adminService.listViews()).pipe(
|
||||
map(resp => {
|
||||
return resp.toObject().viewsList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -61,7 +61,7 @@ export class IamViewsComponent implements AfterViewInit {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.adminService.ClearView(viewname, db).then(() => {
|
||||
this.adminService.clearView(viewname, db).then(() => {
|
||||
this.toast.showInfo('IAM.VIEWS.CLEARED', true);
|
||||
this.loadViews();
|
||||
}).catch(error => {
|
||||
|
||||
@@ -6,7 +6,8 @@ import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
||||
import { OrgMemberView, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -20,8 +21,8 @@ export class IamComponent {
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
||||
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]>
|
||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||
|
||||
public PolicyGridType: any = PolicyGridType;
|
||||
|
||||
@@ -32,10 +33,12 @@ export class IamComponent {
|
||||
|
||||
public loadMembers(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.adminService.SearchIamMembers(100, 0)).pipe(
|
||||
from(this.adminService.listIAMMembers(100, 0)).pipe(
|
||||
map(resp => {
|
||||
this.totalMemberResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalMemberResult = resp.details.totalResult;
|
||||
}
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -54,12 +57,12 @@ export class IamComponent {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED');
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -6,8 +6,9 @@ import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||
import { Router } from '@angular/router';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators';
|
||||
import { CreateHumanRequest, CreateOrgRequest, Gender, OrgSetUpResponse } from 'src/app/proto/generated/admin_pb';
|
||||
import { PasswordComplexityPolicy as MgmtPasswordComplexityPolicy } from 'src/app/proto/generated/management_pb';
|
||||
import { SetUpOrgRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@@ -57,7 +58,7 @@ export class OrgCreateComponent {
|
||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
public policy!: MgmtPasswordComplexityPolicy.AsObject;
|
||||
public policy!: PasswordComplexityPolicy.AsObject;
|
||||
public usePassword: boolean = false;
|
||||
|
||||
public forSelf: boolean = true;
|
||||
@@ -89,25 +90,29 @@ export class OrgCreateComponent {
|
||||
public currentCreateStep: number = 1;
|
||||
|
||||
public finish(): void {
|
||||
const createOrgRequest: CreateOrgRequest = new CreateOrgRequest();
|
||||
const createOrgRequest: SetUpOrgRequest.Org = new SetUpOrgRequest.Org();
|
||||
createOrgRequest.setName(this.name?.value);
|
||||
createOrgRequest.setDomain(this.domain?.value);
|
||||
|
||||
const humanRequest: CreateHumanRequest = new CreateHumanRequest();
|
||||
const humanRequest: SetUpOrgRequest.Human = new SetUpOrgRequest.Human();
|
||||
humanRequest.setEmail(this.email?.value);
|
||||
humanRequest.setFirstName(this.firstName?.value);
|
||||
humanRequest.setLastName(this.lastName?.value);
|
||||
humanRequest.setNickName(this.nickName?.value);
|
||||
humanRequest.setGender(this.gender?.value);
|
||||
humanRequest.setPreferredLanguage(this.preferredLanguage?.value);
|
||||
humanRequest.setUserName(this.userName?.value);
|
||||
|
||||
const profile: SetUpOrgRequest.Human.Profile = new SetUpOrgRequest.Human.Profile();
|
||||
profile.setFirstName(this.firstName?.value);
|
||||
profile.setLastName(this.lastName?.value);
|
||||
profile.setNickName(this.nickName?.value);
|
||||
profile.setGender(this.gender?.value);
|
||||
profile.setPreferredLanguage(this.preferredLanguage?.value);
|
||||
|
||||
humanRequest.setProfile(this.firstName?.value);
|
||||
if (this.usePassword && this.password) {
|
||||
humanRequest.setPassword(this.password?.value);
|
||||
}
|
||||
|
||||
this.adminService
|
||||
.SetUpOrg(createOrgRequest, humanRequest)
|
||||
.then((org: OrgSetUpResponse) => {
|
||||
.then(() => {
|
||||
this.router.navigate(['/org/overview']);
|
||||
// const orgResp = org.getOrg();
|
||||
// if (orgResp) {
|
||||
@@ -146,31 +151,33 @@ export class OrgCreateComponent {
|
||||
const validators: Validators[] = [Validators.required];
|
||||
|
||||
if (this.usePassword) {
|
||||
this.mgmtService.GetDefaultPasswordComplexityPolicy().then(data => {
|
||||
this.policy = data.toObject();
|
||||
this.mgmtService.getDefaultPasswordComplexityPolicy().then(data => {
|
||||
if (data.policy) {
|
||||
this.policy = data.policy;
|
||||
|
||||
if (this.policy.minLength) {
|
||||
validators.push(Validators.minLength(this.policy.minLength));
|
||||
}
|
||||
if (this.policy.hasLowercase) {
|
||||
validators.push(lowerCaseValidator);
|
||||
}
|
||||
if (this.policy.hasUppercase) {
|
||||
validators.push(upperCaseValidator);
|
||||
}
|
||||
if (this.policy.hasNumber) {
|
||||
validators.push(numberValidator);
|
||||
}
|
||||
if (this.policy.hasSymbol) {
|
||||
validators.push(symbolValidator);
|
||||
}
|
||||
if (this.policy.minLength) {
|
||||
validators.push(Validators.minLength(this.policy.minLength));
|
||||
}
|
||||
if (this.policy.hasLowercase) {
|
||||
validators.push(lowerCaseValidator);
|
||||
}
|
||||
if (this.policy.hasUppercase) {
|
||||
validators.push(upperCaseValidator);
|
||||
}
|
||||
if (this.policy.hasNumber) {
|
||||
validators.push(numberValidator);
|
||||
}
|
||||
if (this.policy.hasSymbol) {
|
||||
validators.push(symbolValidator);
|
||||
}
|
||||
|
||||
const pwdValidators = [...validators] as ValidatorFn[];
|
||||
const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[];
|
||||
this.pwdForm = this.fb.group({
|
||||
password: ['', pwdValidators],
|
||||
confirmPassword: ['', confirmPwdValidators],
|
||||
});
|
||||
const pwdValidators = [...validators] as ValidatorFn[];
|
||||
const confirmPwdValidators = [...validators, passwordConfirmValidator] as ValidatorFn[];
|
||||
this.pwdForm = this.fb.group({
|
||||
password: ['', pwdValidators],
|
||||
confirmPassword: ['', confirmPwdValidators],
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.pwdForm = this.fb.group({
|
||||
@@ -199,7 +206,7 @@ export class OrgCreateComponent {
|
||||
|
||||
public createOrgForSelf(): void {
|
||||
if (this.name && this.name.value) {
|
||||
this.mgmtService.CreateOrg(this.name.value).then((org) => {
|
||||
this.mgmtService.addOrg(this.name.value).then(() => {
|
||||
this.router.navigate(['/org/overview']);
|
||||
// const newOrg = org.toObject();
|
||||
// setTimeout(() => {
|
||||
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
<span class="title" mat-dialog-title>{{'ORG.PAGES.ORGDOMAIN.TITLE' | translate}} {{domain.domain}}</span>
|
||||
<span class="title" mat-dialog-title>{{'ORG.PAGES.ORGDOMAIN.TITLE' | translate}} {{domain.domainName}}</span>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate }}</p>
|
||||
|
||||
<p class="desc warn">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_DESC' | translate }}</p>
|
||||
|
||||
|
||||
<p *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED && !(dns || http)"
|
||||
<p *ngIf="domain.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED && !(dns || http)"
|
||||
class="desc">
|
||||
{{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING' | translate: domain }}
|
||||
{{'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING_TYPE' | translate}}
|
||||
{{'ORG.PAGES.ORGDOMAIN.TYPES.'+ domain.validationType | translate}}</p>
|
||||
<div *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED"
|
||||
<div *ngIf="domain.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED"
|
||||
class="btn-container">
|
||||
<button color="primary" type="submit" mat-raised-button *ngIf="!(dns || http)" (click)="validate()">
|
||||
{{ 'ACTIONS.VERIFY' | translate }}
|
||||
@@ -34,8 +34,8 @@
|
||||
<p class="entry">{{http?.url}}.txt</p>
|
||||
|
||||
<div class="btn-container">
|
||||
<button mat-stroked-button (click)="saveFile()"
|
||||
color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
|
||||
<button mat-stroked-button (click)="saveFile()" color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate
|
||||
}}</button>
|
||||
<button color="primary" class="verify-button" type="submit" mat-raised-button (click)="validate()">
|
||||
<span>{{ 'ACTIONS.VERIFY' | translate }}</span>
|
||||
</button>
|
||||
|
||||
+17
-15
@@ -1,7 +1,8 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { OrgDomainValidationResponse, OrgDomainValidationType, OrgDomainView } from 'src/app/proto/generated/management_pb';
|
||||
import { GenerateOrgDomainValidationResponse } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Domain, DomainValidationType } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -11,12 +12,13 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
styleUrls: ['./domain-verification.component.scss'],
|
||||
})
|
||||
export class DomainVerificationComponent {
|
||||
public domain!: OrgDomainView.AsObject;
|
||||
public domain!: Domain.AsObject;
|
||||
|
||||
public OrgDomainValidationType: any = OrgDomainValidationType;
|
||||
public DomainValidationType: any = DomainValidationType;
|
||||
|
||||
public http!: GenerateOrgDomainValidationResponse.AsObject;
|
||||
public dns!: GenerateOrgDomainValidationResponse.AsObject;
|
||||
|
||||
public http!: OrgDomainValidationResponse.AsObject;
|
||||
public dns!: OrgDomainValidationResponse.AsObject;
|
||||
public copied: string = '';
|
||||
|
||||
public showNew: boolean = false;
|
||||
@@ -29,24 +31,24 @@ export class DomainVerificationComponent {
|
||||
private mgmtService: ManagementService,
|
||||
) {
|
||||
this.domain = data.domain;
|
||||
if (this.domain.validationType === OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED) {
|
||||
if (this.domain.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED) {
|
||||
this.showNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
async loadHttpToken(): Promise<void> {
|
||||
this.mgmtService.GenerateMyOrgDomainValidation(
|
||||
this.domain.domain,
|
||||
OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_HTTP).then((http) => {
|
||||
this.http = http.toObject();
|
||||
this.mgmtService.generateOrgDomainValidation(
|
||||
this.domain.domainName,
|
||||
DomainValidationType.DOMAIN_VALIDATION_TYPE_HTTP).then((http) => {
|
||||
this.http = http;
|
||||
});
|
||||
}
|
||||
|
||||
async loadDnsToken(): Promise<void> {
|
||||
this.mgmtService.GenerateMyOrgDomainValidation(
|
||||
this.domain.domain,
|
||||
OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_DNS).then((dns) => {
|
||||
this.dns = dns.toObject();
|
||||
this.mgmtService.generateOrgDomainValidation(
|
||||
this.domain.domainName,
|
||||
DomainValidationType.DOMAIN_VALIDATION_TYPE_DNS).then((dns) => {
|
||||
this.dns = dns;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ export class DomainVerificationComponent {
|
||||
|
||||
public validate(): void {
|
||||
this.validating = true;
|
||||
this.mgmtService.ValidateMyOrgDomain(this.domain.domain).then(() => {
|
||||
this.mgmtService.validateOrgDomain(this.domain.domainName).then(() => {
|
||||
this.dialogRef.close(true);
|
||||
this.toast.showInfo('ORG.PAGES.ORGDOMAIN.VERIFICATION_SUCCESSFUL', true);
|
||||
this.validating = false;
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
import { PolicyGridType } from 'src/app/modules/policy-grid/policy-grid.component';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import {
|
||||
Org,
|
||||
OrgDomainView,
|
||||
OrgMember,
|
||||
OrgMemberSearchResponse,
|
||||
OrgMemberView,
|
||||
OrgState,
|
||||
UserView,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { Domain, Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -33,28 +25,22 @@ import { DomainVerificationComponent } from './domain-verification/domain-verifi
|
||||
templateUrl: './org-detail.component.html',
|
||||
styleUrls: ['./org-detail.component.scss'],
|
||||
})
|
||||
export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
export class OrgDetailComponent implements OnInit {
|
||||
public org!: Org.AsObject;
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public dataSource: MatTableDataSource<OrgMember.AsObject> = new MatTableDataSource<OrgMember.AsObject>();
|
||||
public memberResult!: OrgMemberSearchResponse.AsObject;
|
||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
public selection: SelectionModel<OrgMember.AsObject> = new SelectionModel<OrgMember.AsObject>(true, []);
|
||||
public OrgState: any = OrgState;
|
||||
public ChangeType: any = ChangeType;
|
||||
|
||||
private subscription: Subscription = new Subscription();
|
||||
|
||||
public domains: OrgDomainView.AsObject[] = [];
|
||||
public domains: Domain.AsObject[] = [];
|
||||
public primaryDomain: string = '';
|
||||
|
||||
// members
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
||||
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]>
|
||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||
public PolicyGridType: any = PolicyGridType;
|
||||
|
||||
constructor(
|
||||
@@ -69,13 +55,11 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData(): Promise<void> {
|
||||
this.mgmtService.GetMyOrg().then((org: Org) => {
|
||||
this.org = org.toObject();
|
||||
this.mgmtService.getMyOrg().then((resp) => {
|
||||
if (resp.org) {
|
||||
this.org = resp.org;
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@@ -84,14 +68,14 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public loadDomains(): void {
|
||||
this.mgmtService.SearchMyOrgDomains().then(result => {
|
||||
this.domains = result.toObject().resultList;
|
||||
this.primaryDomain = this.domains.find(domain => domain.primary)?.domain ?? '';
|
||||
this.mgmtService.listOrgDomains().then(result => {
|
||||
this.domains = result.resultList;
|
||||
this.primaryDomain = this.domains.find(domain => domain.isPrimary)?.domainName ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
public setPrimary(domain: OrgDomainView.AsObject): void {
|
||||
this.mgmtService.setMyPrimaryOrgDomain(domain.domain).then(() => {
|
||||
public setPrimary(domain: Domain.AsObject): void {
|
||||
this.mgmtService.setPrimaryOrgDomain(domain.domainName).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.SETPRIMARY', true);
|
||||
this.loadDomains();
|
||||
}).catch((error) => {
|
||||
@@ -100,14 +84,14 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public changeState(event: MatButtonToggleChange | any): void {
|
||||
if (event.value === OrgState.ORGSTATE_ACTIVE) {
|
||||
this.mgmtService.ReactivateMyOrg().then(() => {
|
||||
if (event.value === OrgState.ORG_STATE_ACTIVE) {
|
||||
this.mgmtService.reactivateOrg().then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.REACTIVATED', true);
|
||||
}).catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (event.value === OrgState.ORGSTATE_INACTIVE) {
|
||||
this.mgmtService.DeactivateMyOrg().then(() => {
|
||||
} else if (event.value === OrgState.ORG_STATE_INACTIVE) {
|
||||
this.mgmtService.deactivateOrg().then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.DEACTIVATED', true);
|
||||
}).catch((error) => {
|
||||
this.toast.showError(error);
|
||||
@@ -123,21 +107,11 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtService.AddMyOrgDomain(resp).then(domain => {
|
||||
const newDomain = domain;
|
||||
this.mgmtService.addOrgDomain(resp).then(resp => {
|
||||
const newDomain = resp;
|
||||
|
||||
const newDomainView = new OrgDomainView();
|
||||
newDomainView.setChangeDate(newDomain.getChangeDate());
|
||||
newDomainView.setCreationDate(newDomain.getCreationDate());
|
||||
newDomainView.setDomain(newDomain.getDomain());
|
||||
newDomainView.setOrgId(newDomain.getOrgId());
|
||||
newDomainView.setPrimary(newDomain.getPrimary());
|
||||
newDomainView.setSequence(newDomain.getSequence());
|
||||
newDomainView.setVerified(newDomain.getVerified());
|
||||
|
||||
this.domains.push(newDomainView.toObject());
|
||||
|
||||
this.verifyDomain(newDomainView.toObject());
|
||||
// TODO send domainname only
|
||||
// this.verifyDomain(newDomainView.toObject());
|
||||
this.toast.showInfo('ORG.TOAST.DOMAINADDED', true);
|
||||
});
|
||||
}
|
||||
@@ -157,9 +131,9 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtService.RemoveMyOrgDomain(domain).then(() => {
|
||||
this.mgmtService.removeOrgDomain(domain).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.DOMAINREMOVED', true);
|
||||
const index = this.domains.findIndex(d => d.domain === domain);
|
||||
const index = this.domains.findIndex(d => d.domainName === domain);
|
||||
if (index > -1) {
|
||||
this.domains.splice(index, 1);
|
||||
}
|
||||
@@ -180,12 +154,12 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -203,7 +177,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
this.router.navigate(['org/members']);
|
||||
}
|
||||
|
||||
public verifyDomain(domain: OrgDomainView.AsObject): void {
|
||||
public verifyDomain(domain: Domain.AsObject): void {
|
||||
const dialogRef = this.dialog.open(DomainVerificationComponent, {
|
||||
data: {
|
||||
domain: domain,
|
||||
@@ -220,10 +194,12 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public loadMembers(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchMyOrgMembers(100, 0)).pipe(
|
||||
from(this.mgmtService.listOrgMembers(100, 0)).pipe(
|
||||
map(resp => {
|
||||
this.totalMemberResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalMemberResult = resp.details?.totalResult;
|
||||
}
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header
|
||||
[ngClass]="{'search-active': this.orgSearchKey == MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME}">
|
||||
[ngClass]="{'search-active': this.orgSearchKey == OrgListSearchKey.NAME}">
|
||||
{{ 'ORG.PAGES.NAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME}"></template>
|
||||
[ngTemplateOutletContext]="{key: OrgListSearchKey.NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let org"> {{org.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
@@ -6,9 +6,14 @@ import { Router } from '@angular/router';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import { MyProjectOrgSearchKey, MyProjectOrgSearchQuery, Org, SearchMethod } from 'src/app/proto/generated/auth_pb';
|
||||
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgNameQuery, OrgQuery } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
enum OrgListSearchKey {
|
||||
NAME = "NAME",
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-list',
|
||||
templateUrl: './org-list.component.html',
|
||||
@@ -18,7 +23,7 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
],
|
||||
})
|
||||
export class OrgListComponent implements AfterViewInit {
|
||||
public orgSearchKey: MyProjectOrgSearchKey | undefined = undefined;
|
||||
public orgSearchKey: OrgListSearchKey | undefined = undefined;
|
||||
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
@@ -29,7 +34,7 @@ export class OrgListComponent implements AfterViewInit {
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
public activeOrg!: Org.AsObject;
|
||||
public MyProjectOrgSearchKey: any = MyProjectOrgSearchKey;
|
||||
public OrgListSearchKey: any = OrgListSearchKey;
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
@@ -37,7 +42,7 @@ export class OrgListComponent implements AfterViewInit {
|
||||
) {
|
||||
this.loadOrgs(10, 0);
|
||||
|
||||
this.authService.GetActiveOrg().then(org => this.activeOrg = org);
|
||||
this.authService.getActiveOrg().then(org => this.activeOrg = org);
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
@@ -48,15 +53,16 @@ export class OrgListComponent implements AfterViewInit {
|
||||
this.loadingSubject.next(true);
|
||||
let query;
|
||||
if (filter) {
|
||||
query = new MyProjectOrgSearchQuery();
|
||||
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE);
|
||||
query.setKey(MyProjectOrgSearchKey.MYPROJECTORGSEARCHKEY_ORG_NAME);
|
||||
query.setValue(filter);
|
||||
const query = new OrgQuery();
|
||||
const orgNameQuery = new OrgNameQuery();
|
||||
orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
orgNameQuery.setName(filter);
|
||||
query.setNameQuery(orgNameQuery);
|
||||
}
|
||||
|
||||
from(this.authService.SearchMyProjectOrgs(limit, offset, query ? [query] : undefined)).pipe(
|
||||
from(this.authService.listMyProjectOrgs(limit, offset, query ? [query] : undefined)).pipe(
|
||||
map(resp => {
|
||||
return resp.toObject().resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -75,7 +81,7 @@ export class OrgListComponent implements AfterViewInit {
|
||||
this.loadOrgs(this.paginator.length, this.paginator.pageSize * this.paginator.pageIndex);
|
||||
}
|
||||
|
||||
public setFilter(key: MyProjectOrgSearchKey): void {
|
||||
public setFilter(key: OrgListSearchKey): void {
|
||||
setTimeout(() => {
|
||||
if (this.filter) {
|
||||
(this.filter as any).nativeElement.focus();
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ export class OrgMemberRolesAutocompleteComponent {
|
||||
@ViewChild('auto') public matAutocomplete!: MatAutocomplete;
|
||||
@Output() public selectionChanged: EventEmitter<string[]> = new EventEmitter();
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService) {
|
||||
this.mgmtService.GetOrgMemberRoles().then(resp => {
|
||||
this.allRoles = resp.toObject().rolesList;
|
||||
this.mgmtService.listOrgMemberRoles().then(resp => {
|
||||
this.allRoles = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
@@ -2,13 +2,13 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { OrgMemberView } from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
|
||||
export class OrgMembersDataSource extends DataSource<Member.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]> = new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -20,14 +20,13 @@ export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchMyOrgMembers(pageSize, offset)).pipe(
|
||||
from(this.mgmtService.listOrgMembers(pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details.viewTimestamp;
|
||||
}
|
||||
return response.resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -42,7 +41,7 @@ export class OrgMembersDataSource extends DataSource<OrgMemberView.AsObject> {
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<OrgMemberView.AsObject[]> {
|
||||
public connect(): Observable<Member.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { Org, OrgMemberView, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -23,17 +25,19 @@ export class OrgMembersComponent {
|
||||
public memberRoleOptions: string[] = [];
|
||||
public changePageFactory!: Function;
|
||||
public changePage: EventEmitter<void> = new EventEmitter();
|
||||
public selection: Array<OrgMemberView.AsObject> = [];
|
||||
public selection: Array<Member.AsObject> = [];
|
||||
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
) {
|
||||
this.mgmtService.GetMyOrg().then(org => {
|
||||
this.org = org.toObject();
|
||||
this.dataSource = new OrgMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(0, this.INITIALPAGESIZE);
|
||||
this.mgmtService.getMyOrg().then(resp => {
|
||||
if (resp.org) {
|
||||
this.org = resp.org;
|
||||
this.dataSource = new OrgMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(0, this.INITIALPAGESIZE);
|
||||
}
|
||||
});
|
||||
|
||||
this.getRoleOptions();
|
||||
@@ -47,15 +51,15 @@ export class OrgMembersComponent {
|
||||
}
|
||||
|
||||
public getRoleOptions(): void {
|
||||
this.mgmtService.GetOrgMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.toObject().rolesList;
|
||||
this.mgmtService.listOrgMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
updateRoles(member: OrgMemberView.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.ChangeMyOrgMember(member.userId, selectionChange.value)
|
||||
updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.updateOrgMember(member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
@@ -65,7 +69,7 @@ export class OrgMembersComponent {
|
||||
|
||||
public removeOrgMemberSelection(): void {
|
||||
Promise.all(this.selection.map(member => {
|
||||
return this.mgmtService.RemoveMyOrgMember(member.userId).then(() => {
|
||||
return this.mgmtService.removeOrgMember(member.userId).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -77,8 +81,8 @@ export class OrgMembersComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public removeOrgMember(member: OrgMemberView.AsObject): void {
|
||||
this.mgmtService.RemoveMyOrgMember(member.userId).then(() => {
|
||||
public removeOrgMember(member: Member.AsObject): void {
|
||||
this.mgmtService.removeOrgMember(member.userId).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERREMOVED', true);
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -99,12 +103,12 @@ export class OrgMembersComponent {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
</mat-step>
|
||||
|
||||
<!-- skip for native applications -->
|
||||
<mat-step *ngIf="oidcApp.applicationType !== OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"
|
||||
[stepControl]="secondFormGroup" [editable]="true">
|
||||
<mat-step *ngIf="oidcAppRequest.appType !== OIDCAppType.OIDC_APP_TYPE_NATIVE" [stepControl]="secondFormGroup"
|
||||
[editable]="true">
|
||||
<form [formGroup]="secondFormGroup">
|
||||
<ng-template matStepLabel>{{'APP.AUTHMETHODSECTION' | translate}}</ng-template>
|
||||
|
||||
@@ -65,31 +65,29 @@
|
||||
<ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template>
|
||||
|
||||
<p class="step-title">{{'APP.OIDC.REDIRECTTITLE' | translate}}</p>
|
||||
<p class="step-description"
|
||||
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
|
||||
<p class="step-description" *ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB">
|
||||
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE"
|
||||
(changedUris)="oidcApp.redirectUrisList = $event" [urisList]="oidcApp.redirectUrisList"
|
||||
[isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE"
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $event" [urisList]="oidcAppRequest.redirectUrisList"
|
||||
[getValues]="requestRedirectValuesSubject$" title="{{ 'APP.OIDC.REDIRECT' | translate }}">
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p>
|
||||
<p class="step-description"
|
||||
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
<p class="step-description" *ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</p>
|
||||
<p class="step-description"
|
||||
*ngIf="oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB || oidcApp.applicationType === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT">
|
||||
*ngIf="oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_WEB || oidcAppRequest.appType === OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}</p>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
(changedUris)="oidcApp.postLogoutRedirectUrisList = $event"
|
||||
[urisList]="oidcApp.postLogoutRedirectUrisList" title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
(changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $event"
|
||||
[urisList]="oidcAppRequest.postLogoutRedirectUrisList"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<div class="actions">
|
||||
@@ -107,7 +105,7 @@
|
||||
{{ 'APP.NAME' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
{{oidcApp.name}}
|
||||
{{oidcAppRequest.name}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -117,27 +115,27 @@
|
||||
{{ 'APP.TYPE' | translate }}
|
||||
</span>
|
||||
<span class="right">
|
||||
{{'APP.OIDC.APPTYPE.'+oidcApp.applicationType | translate}}
|
||||
{{'APP.OIDC.APPTYPE.'+oidcAppRequest.appType | translate}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="left">
|
||||
{{ 'APP.GRANT' | translate }}
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcApp.grantTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcApp.grantTypesList; index as i">
|
||||
<span class="right" *ngIf="oidcAppRequest.grantTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcAppRequest.grantTypesList; index as i">
|
||||
{{'APP.OIDC.GRANT.'+element | translate}}
|
||||
{{i < oidcApp.grantTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{i < oidcAppRequest.grantTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.RESPONSETYPE' | translate }}
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcApp.responseTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcApp.responseTypesList; index as i">
|
||||
<span class="right" *ngIf="oidcAppRequest.responseTypesList?.length > 0">
|
||||
[<span *ngFor="let element of oidcAppRequest.responseTypesList; index as i">
|
||||
{{('APP.OIDC.RESPONSE.'+element | translate)}}
|
||||
{{i < oidcApp.responseTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{i < oidcAppRequest.responseTypesList.length - 1 ? ', ' : '' }} </span>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +145,7 @@
|
||||
</span>
|
||||
<span class="right">
|
||||
<span>
|
||||
{{'APP.OIDC.AUTHMETHOD.'+oidcApp?.authMethodType | translate}}
|
||||
{{'APP.OIDC.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -156,10 +154,10 @@
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.REDIRECT' | translate }}
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcApp.redirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcApp.redirectUrisList; index as i">
|
||||
<span class="right" *ngIf="oidcAppRequest.redirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcAppRequest.redirectUrisList; index as i">
|
||||
{{redirect}}
|
||||
{{i < oidcApp.redirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{i < oidcAppRequest.redirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -167,10 +165,10 @@
|
||||
<span class="left">
|
||||
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
|
||||
</span>
|
||||
<span class="right" *ngIf="oidcApp.postLogoutRedirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList; index as i">
|
||||
<span class="right" *ngIf="oidcAppRequest.postLogoutRedirectUrisList?.length > 0">
|
||||
[<span *ngFor="let redirect of oidcAppRequest.postLogoutRedirectUrisList; index as i">
|
||||
{{redirect}}
|
||||
{{i < oidcApp.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
{{i < oidcAppRequest.postLogoutRedirectUrisList.length - 1 ? ', ' : '' }} </span>]
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -182,7 +180,7 @@
|
||||
</span>
|
||||
<span class="right">
|
||||
<span>
|
||||
{{'APP.API.AUTHMETHOD.'+apiApp?.authMethodType | translate}}
|
||||
{{'APP.API.AUTHMETHOD.'+oidcAppRequest?.authMethodType | translate}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -247,17 +245,18 @@
|
||||
<div class="content" *ngIf="formappType?.value?.createType == AppCreateType.OIDC">
|
||||
<div class="formfield full-width">
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
(changedUris)="oidcApp.redirectUrisList = $event" [urisList]="oidcApp.redirectUrisList"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}" [getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
(changedUris)="oidcAppRequest.redirectUrisList = $event"
|
||||
[urisList]="oidcAppRequest.redirectUrisList" title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<cnsl-redirect-uris class="redirect-section" [canWrite]="true"
|
||||
(changedUris)="oidcApp.postLogoutRedirectUrisList = $event"
|
||||
[urisList]="oidcApp.postLogoutRedirectUrisList"
|
||||
(changedUris)="oidcAppRequest.postLogoutRedirectUrisList = $event"
|
||||
[urisList]="oidcAppRequest.postLogoutRedirectUrisList"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[getValues]="requestRedirectValuesSubject$"
|
||||
[isNative]="oidcApp.applicationType == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
[isNative]="oidcAppRequest.appType == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
</cnsl-redirect-uris>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
@@ -8,31 +9,28 @@ import { Subject, Subscription } from 'rxjs';
|
||||
import { debounceTime, takeUntil } from 'rxjs/operators';
|
||||
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
|
||||
import {
|
||||
APIApplicationCreate,
|
||||
APIAuthMethodType,
|
||||
Application,
|
||||
OIDCApplicationCreate,
|
||||
OIDCApplicationType,
|
||||
App,
|
||||
OIDCAppType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCConfig,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import { AddAPIAppRequest, AddOIDCAppRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import {
|
||||
WEB_TYPE,
|
||||
NATIVE_TYPE,
|
||||
USER_AGENT_TYPE,
|
||||
API_TYPE,
|
||||
RadioItemAppType,
|
||||
AppCreateType
|
||||
} from '../authtypes';
|
||||
|
||||
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
|
||||
import { CODE_METHOD, getPartialConfigFromAuthMethod, IMPLICIT_METHOD, BASIC_AUTH_METHOD, PKCE_METHOD, PK_JWT_METHOD, POST_METHOD } from '../authmethods';
|
||||
import { StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||
import {
|
||||
BASIC_AUTH_METHOD,
|
||||
CODE_METHOD,
|
||||
getPartialConfigFromAuthMethod,
|
||||
IMPLICIT_METHOD,
|
||||
PK_JWT_METHOD,
|
||||
PKCE_METHOD,
|
||||
POST_METHOD,
|
||||
} from '../authmethods';
|
||||
import { API_TYPE, AppCreateType, NATIVE_TYPE, RadioItemAppType, USER_AGENT_TYPE, WEB_TYPE } from '../authtypes';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -47,19 +45,19 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
public projectId: string = '';
|
||||
public loading: boolean = false;
|
||||
|
||||
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject();
|
||||
public apiApp: APIApplicationCreate.AsObject = new APIApplicationCreate().toObject();
|
||||
public oidcAppRequest: AddOIDCAppRequest.AsObject = new AddOIDCAppRequest().toObject();
|
||||
public apiAppRequest: AddAPIAppRequest.AsObject = new AddAPIAppRequest().toObject();
|
||||
|
||||
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; disabled: boolean; }[] = [
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN, checked: false, disabled: false },
|
||||
{ type: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN, checked: false, disabled: false },
|
||||
];
|
||||
|
||||
public oidcAppTypes: OIDCApplicationType[] = [
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||
public oidcAppTypes: OIDCAppType[] = [
|
||||
OIDCAppType.OIDC_APP_TYPE_WEB,
|
||||
OIDCAppType.OIDC_APP_TYPE_NATIVE,
|
||||
OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
|
||||
];
|
||||
public appTypes: any = [
|
||||
WEB_TYPE,
|
||||
@@ -77,9 +75,9 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
// set to oidc first
|
||||
public authMethodTypes: { type: OIDCAuthMethodType | APIAuthMethodType, checked: boolean, disabled: boolean; api?: boolean; oidc?: boolean; }[] = [
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
|
||||
];
|
||||
|
||||
// stepper
|
||||
@@ -90,7 +88,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
public form!: FormGroup;
|
||||
|
||||
public AppCreateType: any = AppCreateType;
|
||||
public OIDCApplicationType: any = OIDCApplicationType;
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
public OIDCGrantType: any = OIDCGrantType;
|
||||
public OIDCAuthMethodType: any = OIDCAuthMethodType;
|
||||
|
||||
@@ -99,8 +97,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
checked: boolean,
|
||||
disabled: boolean,
|
||||
}[] = [
|
||||
{ type: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
|
||||
{ type: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, checked: false, disabled: true },
|
||||
{ type: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
|
||||
{ type: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT, checked: false, disabled: true },
|
||||
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
|
||||
// TODO show when implemented
|
||||
];
|
||||
@@ -134,28 +132,28 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.firstFormGroup.valueChanges.subscribe(value => {
|
||||
if (this.firstFormGroup.valid) {
|
||||
this.oidcApp.name = this.name?.value;
|
||||
this.apiApp.name = this.name?.value;
|
||||
this.oidcAppRequest.name = this.name?.value;
|
||||
this.apiAppRequest.name = this.name?.value;
|
||||
|
||||
if (this.isStepperOIDC) {
|
||||
const oidcAppType = (this.appType?.value as RadioItemAppType).oidcApplicationType;
|
||||
const oidcAppType = (this.appType?.value as RadioItemAppType).oidcAppType;
|
||||
if (oidcAppType !== undefined) {
|
||||
this.oidcApp.applicationType = oidcAppType;
|
||||
this.oidcAppRequest.appType = oidcAppType;
|
||||
}
|
||||
|
||||
switch (this.oidcApp.applicationType) {
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE:
|
||||
switch (this.oidcAppRequest.appType) {
|
||||
case OIDCAppType.OIDC_APP_TYPE_NATIVE:
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
];
|
||||
|
||||
// automatically set to PKCE and skip step
|
||||
this.oidcApp.responseTypesList = [OIDCResponseType.OIDCRESPONSETYPE_CODE];
|
||||
this.oidcApp.grantTypesList = [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE];
|
||||
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
|
||||
this.oidcAppRequest.responseTypesList = [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE];
|
||||
this.oidcAppRequest.grantTypesList = [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE];
|
||||
this.oidcAppRequest.authMethodType = OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
|
||||
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB:
|
||||
case OIDCAppType.OIDC_APP_TYPE_WEB:
|
||||
// PK_JWT_METHOD.recommended = false;
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
@@ -166,7 +164,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.authMethod?.setValue(PKCE_METHOD.key);
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT:
|
||||
case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
IMPLICIT_METHOD,
|
||||
@@ -194,11 +192,11 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
const partialConfig = getPartialConfigFromAuthMethod(form.authMethod);
|
||||
|
||||
if (this.isStepperOIDC && partialConfig && partialConfig.oidc) {
|
||||
this.oidcApp.responseTypesList = partialConfig.oidc?.responseTypesList ?? [];
|
||||
this.oidcApp.grantTypesList = partialConfig.oidc?.grantTypesList ?? [];
|
||||
this.oidcApp.authMethodType = partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
|
||||
this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList ?? [];
|
||||
this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList ?? [];
|
||||
this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
|
||||
} else if (this.isStepperAPI && partialConfig && partialConfig.api) {
|
||||
this.apiApp.authMethodType = partialConfig.api?.authMethodType ?? APIAuthMethodType.APIAUTHMETHODTYPE_BASIC;
|
||||
this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -216,18 +214,18 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
this.form.valueChanges.pipe(
|
||||
takeUntil(this.destroyed$),
|
||||
debounceTime(150)).subscribe(() => {
|
||||
this.oidcApp.name = this.formname?.value;
|
||||
this.apiApp.name = this.formname?.value;
|
||||
this.oidcAppRequest.name = this.formname?.value;
|
||||
this.apiAppRequest.name = this.formname?.value;
|
||||
|
||||
this.oidcApp.responseTypesList = this.formresponseTypesList?.value;
|
||||
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
|
||||
this.oidcAppRequest.responseTypesList = this.formresponseTypesList?.value;
|
||||
this.oidcAppRequest.grantTypesList = this.formgrantTypesList?.value;
|
||||
|
||||
this.oidcApp.authMethodType = this.formauthMethodType?.value;
|
||||
this.apiApp.authMethodType = this.formauthMethodType?.value;
|
||||
this.oidcAppRequest.authMethodType = this.formauthMethodType?.value;
|
||||
this.apiAppRequest.authMethodType = this.formauthMethodType?.value;
|
||||
|
||||
const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcApplicationType;
|
||||
const oidcAppType = (this.formappType?.value as RadioItemAppType).oidcAppType;
|
||||
if (oidcAppType !== undefined) {
|
||||
this.oidcApp.applicationType = oidcAppType;
|
||||
this.oidcAppRequest.appType = oidcAppType;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -245,20 +243,20 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
this.form.addControl('responseTypesList', responseTypesControl);
|
||||
|
||||
this.authMethodTypes = [
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
|
||||
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
|
||||
];
|
||||
this.authMethod?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC);
|
||||
this.authMethod?.setValue(OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC);
|
||||
} else if (this.isDevAPI) {
|
||||
this.form.removeControl('grantTypesList');
|
||||
this.form.removeControl('responseTypesList');
|
||||
|
||||
this.authMethodTypes = [
|
||||
{ type: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true },
|
||||
{ type: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC, checked: false, disabled: false, api: true },
|
||||
{ type: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, checked: false, disabled: false, api: true },
|
||||
{ type: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, api: true },
|
||||
];
|
||||
this.authMethod?.setValue(APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT);
|
||||
this.authMethod?.setValue(APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT);
|
||||
}
|
||||
this.form.updateValueAndValidity();
|
||||
}
|
||||
@@ -271,8 +269,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
private async getData({ projectid }: Params): Promise<void> {
|
||||
this.projectId = projectid;
|
||||
this.oidcApp.projectId = projectid;
|
||||
this.apiApp.projectId = projectid;
|
||||
this.oidcAppRequest.projectId = projectid;
|
||||
this.apiAppRequest.projectId = projectid;
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
@@ -288,15 +286,14 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.loading = true;
|
||||
this.mgmtService
|
||||
.CreateOIDCApp(this.oidcApp)
|
||||
.then((data: Application) => {
|
||||
.addOIDCApp(this.oidcAppRequest)
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
const response = data.toObject();
|
||||
if (response.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE) {
|
||||
this.showSavedDialog(response);
|
||||
} else {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', response.id]);
|
||||
}
|
||||
// if (resp.oidcConfig?.authMethodType !== OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE) {
|
||||
// this.showSavedDialog(resp);
|
||||
// } else {
|
||||
// this.router.navigate(['projects', this.projectId, 'apps', response.id]);
|
||||
// }
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
@@ -305,15 +302,15 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
} else if (appAPICheck) {
|
||||
this.loading = true;
|
||||
this.mgmtService
|
||||
.CreateAPIApplication(this.apiApp)
|
||||
.then((data: Application) => {
|
||||
.addAPIApp(this.apiAppRequest)
|
||||
.then((resp) => {
|
||||
this.loading = false;
|
||||
const response = data.toObject();
|
||||
if (response.apiConfig?.authMethodType == APIAuthMethodType.APIAUTHMETHODTYPE_BASIC) {
|
||||
this.showSavedDialog(response);
|
||||
} else {
|
||||
this.router.navigate(['projects', this.projectId, 'apps', response.id]);
|
||||
}
|
||||
// const response = resp.toObject();
|
||||
// if (response.apiConfig?.authMethodType == APIAuthMethodType.APIAUTHMETHODTYPE_BASIC) {
|
||||
// this.showSavedDialog(resp);
|
||||
// } else {
|
||||
// this.router.navigate(['projects', this.projectId, 'apps', response.id]);
|
||||
// }
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
@@ -322,7 +319,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public showSavedDialog(app: Application.AsObject): void {
|
||||
public showSavedDialog(app: App.AsObject): void {
|
||||
if (app.oidcConfig?.clientSecret !== undefined) {
|
||||
const dialogRef = this.dialog.open(AppSecretDialogComponent, {
|
||||
data: app.oidcConfig,
|
||||
|
||||
@@ -106,29 +106,28 @@
|
||||
{{ 'APP.OIDC.DEVMODE' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section class="step-description"
|
||||
*ngIf="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
<cnsl-info-section class="step-description" *ngIf="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
<span>{{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}}</span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section class="step-description"
|
||||
*ngIf="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB || applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT">
|
||||
*ngIf="OIDCAppType?.value == OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value == OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
|
||||
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}
|
||||
</cnsl-info-section>
|
||||
|
||||
<div style="margin: .5rem" class="divider"></div>
|
||||
<cnsl-redirect-uris *ngIf="applicationType?.value !== undefined" class="redirect-section"
|
||||
[canWrite]="canWrite" [devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$"
|
||||
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
|
||||
[devMode]="devMode?.value" [getValues]="requestRedirectValuesSubject$"
|
||||
(changedUris)="redirectUrisList = $event" [urisList]="redirectUrisList"
|
||||
title="{{ 'APP.OIDC.REDIRECT' | translate }}"
|
||||
[isNative]="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
[isNative]="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
</cnsl-redirect-uris>
|
||||
|
||||
<cnsl-redirect-uris *ngIf="applicationType?.value !== undefined" class="redirect-section"
|
||||
[canWrite]="canWrite" [devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $event"
|
||||
<cnsl-redirect-uris *ngIf="appType?.value !== undefined" class="redirect-section" [canWrite]="canWrite"
|
||||
[devMode]="devMode?.value" (changedUris)="postLogoutRedirectUrisList = $event"
|
||||
[urisList]="postLogoutRedirectUrisList" [getValues]="requestRedirectValuesSubject$"
|
||||
title="{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}"
|
||||
[isNative]="applicationType?.value == OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||
[isNative]="appType?.value == OIDCAppType.OIDC_APP_TYPE_NATIVE">
|
||||
</cnsl-redirect-uris>
|
||||
</div>
|
||||
|
||||
@@ -262,7 +261,7 @@
|
||||
<div class="meta-row">
|
||||
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
|
||||
<span *ngIf="app && app.state !== undefined"
|
||||
[ngClass]="{'active': app.state === AppState.APPSTATE_ACTIVE, 'inactive': app.state === AppState.APPSTATE_INACTIVE}"
|
||||
[ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}"
|
||||
class="state">{{'APP.PAGES.DETAIL.STATE.'+app.state | translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,11 +2,11 @@ import { COMMA, ENTER, SPACE } from '@angular/cdk/keycodes';
|
||||
import { Location } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { ActivatedRoute, Params, Router, RouterLink } from '@angular/router';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
@@ -18,25 +18,36 @@ import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.com
|
||||
import {
|
||||
APIAuthMethodType,
|
||||
APIConfig,
|
||||
APIConfigUpdate,
|
||||
Application,
|
||||
App,
|
||||
AppState,
|
||||
ClientSecret,
|
||||
OIDCApplicationType,
|
||||
OIDCAppType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCConfig,
|
||||
OIDCConfigUpdate,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
OIDCTokenType,
|
||||
ZitadelDocs,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import {
|
||||
GetOIDCInformationResponse,
|
||||
UpdateAPIAppConfigRequest,
|
||||
UpdateOIDCAppConfigRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog.component';
|
||||
import { CODE_METHOD, getAuthMethodFromPartialConfig, getPartialConfigFromAuthMethod, IMPLICIT_METHOD, PKCE_METHOD, PK_JWT_METHOD, POST_METHOD, CUSTOM_METHOD, BASIC_AUTH_METHOD } from '../authmethods';
|
||||
import {
|
||||
BASIC_AUTH_METHOD,
|
||||
CODE_METHOD,
|
||||
CUSTOM_METHOD,
|
||||
getAuthMethodFromPartialConfig,
|
||||
getPartialConfigFromAuthMethod,
|
||||
IMPLICIT_METHOD,
|
||||
PK_JWT_METHOD,
|
||||
PKCE_METHOD,
|
||||
POST_METHOD,
|
||||
} from '../authmethods';
|
||||
|
||||
@Component({
|
||||
selector: 'app-app-detail',
|
||||
@@ -56,33 +67,33 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public authMethods: RadioItemAuthType[] = [];
|
||||
private subscription?: Subscription;
|
||||
public projectId: string = '';
|
||||
public app!: Application.AsObject;
|
||||
public app!: App.AsObject;
|
||||
public oidcResponseTypes: OIDCResponseType[] = [
|
||||
OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN,
|
||||
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN,
|
||||
OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
|
||||
OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN,
|
||||
];
|
||||
public oidcGrantTypes: OIDCGrantType[] = [
|
||||
OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
OIDCGrantType.OIDCGRANTTYPE_IMPLICIT,
|
||||
OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN,
|
||||
OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT,
|
||||
OIDCGrantType.OIDC_GRANT_TYPE_REFRESH_TOKEN,
|
||||
];
|
||||
public oidcAppTypes: OIDCApplicationType[] = [
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||
public oidcAppTypes: OIDCAppType[] = [
|
||||
OIDCAppType.OIDC_APP_TYPE_WEB,
|
||||
OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
|
||||
OIDCAppType.OIDC_APP_TYPE_NATIVE,
|
||||
];
|
||||
|
||||
public oidcAuthMethodType: OIDCAuthMethodType[] = [
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
];
|
||||
|
||||
public oidcTokenTypes: OIDCTokenType[] = [
|
||||
OIDCTokenType.OIDCTOKENTYPE_BEARER,
|
||||
OIDCTokenType.OIDCTOKENTYPE_JWT,
|
||||
OIDCTokenType.OIDC_TOKEN_TYPE_BEARER,
|
||||
OIDCTokenType.OIDC_TOKEN_TYPE_JWT,
|
||||
];
|
||||
|
||||
public AppState: any = AppState;
|
||||
@@ -94,9 +105,9 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
public postLogoutRedirectUrisList: string[] = [];
|
||||
|
||||
public isZitadel: boolean = false;
|
||||
public docs!: ZitadelDocs.AsObject;
|
||||
public docs!: GetOIDCInformationResponse.AsObject;
|
||||
|
||||
public OIDCApplicationType: any = OIDCApplicationType;
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
public OIDCAuthMethodType: any = OIDCAuthMethodType;
|
||||
public APIAuthMethodType: any = APIAuthMethodType;
|
||||
public OIDCTokenType: any = OIDCTokenType;
|
||||
@@ -142,7 +153,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
clientId: [{ value: '', disabled: true }],
|
||||
responseTypesList: [{ value: [], disabled: true }],
|
||||
grantTypesList: [{ value: [], disabled: true }],
|
||||
applicationType: [{ value: '', disabled: true }],
|
||||
appType: [{ value: '', disabled: true }],
|
||||
authMethodType: [{ value: '', disabled: true }],
|
||||
accessTokenType: [{ value: '', disabled: true }],
|
||||
accessTokenRoleAssertion: [{ value: false, disabled: true }],
|
||||
@@ -192,93 +203,95 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.initLinks();
|
||||
|
||||
this.mgmtService.GetIam().then(iam => {
|
||||
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
||||
this.mgmtService.getIAM().then(iam => {
|
||||
this.isZitadel = iam.iamProjectId === this.projectId;
|
||||
});
|
||||
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => {
|
||||
this.canWrite = allowed;
|
||||
this.mgmtService.GetApplicationById(projectid, id).then(app => {
|
||||
this.app = app.toObject();
|
||||
this.appNameForm.patchValue(this.app);
|
||||
this.mgmtService.getAppByID(projectid, id).then(app => {
|
||||
if (app.app) {
|
||||
this.app = app.app;
|
||||
this.appNameForm.patchValue(this.app);
|
||||
|
||||
if (this.app.oidcConfig) {
|
||||
this.getAuthMethodOptions('OIDC');
|
||||
if (this.app.oidcConfig) {
|
||||
this.getAuthMethodOptions('OIDC');
|
||||
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
|
||||
this.currentAuthMethod = this.initialAuthMethod;
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
|
||||
this.currentAuthMethod = this.initialAuthMethod;
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
} else if (this.app.apiConfig) {
|
||||
this.getAuthMethodOptions('API');
|
||||
} else if (this.app.apiConfig) {
|
||||
this.getAuthMethodOptions('API');
|
||||
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
|
||||
this.currentAuthMethod = this.initialAuthMethod;
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
|
||||
this.currentAuthMethod = this.initialAuthMethod;
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed) {
|
||||
this.appNameForm.enable();
|
||||
this.oidcForm.enable();
|
||||
this.apiForm.enable();
|
||||
}
|
||||
|
||||
if (this.app.oidcConfig?.redirectUrisList) {
|
||||
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
|
||||
}
|
||||
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
|
||||
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
|
||||
}
|
||||
if (this.app.oidcConfig?.clockSkew) {
|
||||
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
|
||||
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
|
||||
}
|
||||
if (this.app.oidcConfig) {
|
||||
this.oidcForm.patchValue(this.app.oidcConfig);
|
||||
}
|
||||
|
||||
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
|
||||
this.showSaveSnack();
|
||||
});
|
||||
|
||||
this.apiForm.valueChanges.subscribe((apiConfig) => {
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
if (allowed) {
|
||||
this.appNameForm.enable();
|
||||
this.oidcForm.enable();
|
||||
this.apiForm.enable();
|
||||
}
|
||||
|
||||
this.showSaveSnack();
|
||||
});
|
||||
if (this.app.oidcConfig?.redirectUrisList) {
|
||||
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
|
||||
}
|
||||
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
|
||||
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
|
||||
}
|
||||
if (this.app.oidcConfig?.clockSkew) {
|
||||
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
|
||||
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
|
||||
}
|
||||
if (this.app.oidcConfig) {
|
||||
this.oidcForm.patchValue(this.app.oidcConfig);
|
||||
}
|
||||
|
||||
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
|
||||
this.showSaveSnack();
|
||||
});
|
||||
|
||||
this.apiForm.valueChanges.subscribe((apiConfig) => {
|
||||
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
|
||||
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
|
||||
if (!this.authMethods.includes(CUSTOM_METHOD)) {
|
||||
this.authMethods.push(CUSTOM_METHOD);
|
||||
}
|
||||
} else {
|
||||
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
|
||||
}
|
||||
|
||||
this.showSaveSnack();
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
this.toast.showError(error);
|
||||
this.errorMessage = error.message;
|
||||
});
|
||||
});
|
||||
this.docs = (await this.mgmtService.GetZitadelDocs()).toObject();
|
||||
this.docs = (await this.mgmtService.getOIDCInformation());
|
||||
}
|
||||
|
||||
private async showSaveSnack(): Promise<void> {
|
||||
@@ -297,14 +310,14 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
private getAuthMethodOptions(type: string): void {
|
||||
if (type == 'OIDC') {
|
||||
switch (this.app.oidcConfig?.applicationType) {
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE:
|
||||
switch (this.app.oidcConfig?.appType) {
|
||||
case OIDCAppType.OIDC_APP_TYPE_NATIVE:
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
CUSTOM_METHOD,
|
||||
];
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB:
|
||||
case OIDCAppType.OIDC_APP_TYPE_WEB:
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
CODE_METHOD,
|
||||
@@ -312,7 +325,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
POST_METHOD,
|
||||
];
|
||||
break;
|
||||
case OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT:
|
||||
case OIDCAppType.OIDC_APP_TYPE_USER_AGENT:
|
||||
this.authMethods = [
|
||||
PKCE_METHOD,
|
||||
IMPLICIT_METHOD,
|
||||
@@ -338,10 +351,10 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
if (partialConfig && partialConfig.oidc && this.app.oidcConfig) {
|
||||
this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? [];
|
||||
this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? [];
|
||||
this.app.oidcConfig.authMethodType = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE;
|
||||
this.app.oidcConfig.authMethodType = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
|
||||
this.oidcForm.patchValue(this.app.oidcConfig);
|
||||
} else if (partialConfig && partialConfig.api && this.app.apiConfig) {
|
||||
this.app.apiConfig.authMethodType = (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.APIAUTHMETHODTYPE_BASIC;
|
||||
this.app.apiConfig.authMethodType = (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
|
||||
this.apiAuthMethodType?.setValue(this.app.apiConfig.authMethodType);
|
||||
}
|
||||
}
|
||||
@@ -358,7 +371,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && this.projectId && this.app.id) {
|
||||
this.mgmtService.RemoveApplication(this.projectId, this.app.id).then(() => {
|
||||
this.mgmtService.removeApp(this.projectId, this.app.id).then(() => {
|
||||
this.toast.showInfo('APP.TOAST.DELETED', true);
|
||||
|
||||
this.router.navigate(['/projects', this.projectId]);
|
||||
@@ -370,14 +383,14 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public changeState(event: MatButtonToggleChange): void {
|
||||
if (event.value === AppState.APPSTATE_ACTIVE) {
|
||||
this.mgmtService.ReactivateApplication(this.projectId, this.app.id).then(() => {
|
||||
if (event.value === AppState.APP_STATE_ACTIVE) {
|
||||
this.mgmtService.reactivateApp(this.projectId, this.app.id).then(() => {
|
||||
this.toast.showInfo('APP.TOAST.REACTIVATED', true);
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (event.value === AppState.APPSTATE_INACTIVE) {
|
||||
this.mgmtService.DeactivateApplication(this.projectId, this.app.id).then(() => {
|
||||
} else if (event.value === AppState.APP_STATE_INACTIVE) {
|
||||
this.mgmtService.deactivateApp(this.projectId, this.app.id).then(() => {
|
||||
this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
@@ -390,7 +403,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
this.app.name = this.name?.value;
|
||||
|
||||
this.mgmtService
|
||||
.UpdateApplication(this.projectId, this.app.id, this.name?.value)
|
||||
.updateApp(this.projectId, this.app.id, this.name?.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('APP.TOAST.UPDATED', true);
|
||||
this.editState = false;
|
||||
@@ -412,7 +425,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
if (this.app.oidcConfig) {
|
||||
this.app.oidcConfig.responseTypesList = this.responseTypesList?.value;
|
||||
this.app.oidcConfig.grantTypesList = this.grantTypesList?.value;
|
||||
this.app.oidcConfig.applicationType = this.applicationType?.value;
|
||||
this.app.oidcConfig.appType = this.appType?.value;
|
||||
this.app.oidcConfig.authMethodType = this.authMethodType?.value;
|
||||
this.app.oidcConfig.redirectUrisList = this.redirectUrisList;
|
||||
this.app.oidcConfig.postLogoutRedirectUrisList = this.postLogoutRedirectUrisList;
|
||||
@@ -422,16 +435,15 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
this.app.oidcConfig.idTokenRoleAssertion = this.idTokenRoleAssertion?.value;
|
||||
this.app.oidcConfig.idTokenUserinfoAssertion = this.idTokenUserinfoAssertion?.value;
|
||||
|
||||
|
||||
const req = new OIDCConfigUpdate();
|
||||
const req = new UpdateOIDCAppConfigRequest();
|
||||
req.setProjectId(this.projectId);
|
||||
req.setApplicationId(this.app.id);
|
||||
req.setAppId(this.app.id);
|
||||
req.setRedirectUrisList(this.app.oidcConfig.redirectUrisList);
|
||||
req.setResponseTypesList(this.app.oidcConfig.responseTypesList);
|
||||
req.setAuthMethodType(this.app.oidcConfig.authMethodType);
|
||||
req.setPostLogoutRedirectUrisList(this.app.oidcConfig.postLogoutRedirectUrisList);
|
||||
req.setGrantTypesList(this.app.oidcConfig.grantTypesList);
|
||||
req.setApplicationType(this.app.oidcConfig.applicationType);
|
||||
req.setAppType(this.app.oidcConfig.appType);
|
||||
req.setDevMode(this.app.oidcConfig.devMode);
|
||||
req.setAccessTokenType(this.app.oidcConfig.accessTokenType);
|
||||
req.setAccessTokenRoleAssertion(this.app.oidcConfig.accessTokenRoleAssertion);
|
||||
@@ -444,7 +456,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
req.setClockSkew(dur);
|
||||
}
|
||||
this.mgmtService
|
||||
.UpdateOIDCAppConfig(req)
|
||||
.updateOIDCAppConfig(req)
|
||||
.then(() => {
|
||||
if (this.app.oidcConfig) {
|
||||
const config = { oidc: this.app.oidcConfig };
|
||||
@@ -463,13 +475,13 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
if (this.apiForm.valid && this.app.apiConfig) {
|
||||
this.app.apiConfig.authMethodType = this.apiAuthMethodType?.value;
|
||||
|
||||
const req = new APIConfigUpdate();
|
||||
const req = new UpdateAPIAppConfigRequest();
|
||||
req.setProjectId(this.projectId);
|
||||
req.setApplicationId(this.app.id);
|
||||
req.setAppId(this.app.id);
|
||||
req.setAuthMethodType(this.app.apiConfig.authMethodType);
|
||||
|
||||
this.mgmtService
|
||||
.UpdateAPIAppConfig(req)
|
||||
.updateAPIAppConfig(req)
|
||||
.then(() => {
|
||||
if (this.app.apiConfig) {
|
||||
const config = { api: this.app.apiConfig };
|
||||
@@ -484,12 +496,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public regenerateOIDCClientSecret(): void {
|
||||
this.mgmtService.RegenerateOIDCClientSecret(this.app.id, this.projectId).then((data: ClientSecret) => {
|
||||
this.mgmtService.regenerateOIDCClientSecret(this.app.id, this.projectId).then(resp => {
|
||||
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
|
||||
this.dialog.open(AppSecretDialogComponent, {
|
||||
data: {
|
||||
// clientId: data.toObject() as ClientSecret.AsObject.clientId,
|
||||
clientSecret: data.toObject().clientSecret,
|
||||
clientSecret: resp.clientSecret,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
@@ -500,12 +512,12 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public regenerateAPIClientSecret(): void {
|
||||
this.mgmtService.RegenerateAPIClientSecret(this.app.id, this.projectId).then((data: ClientSecret) => {
|
||||
this.mgmtService.regenerateAPIClientSecret(this.app.id, this.projectId).then(resp => {
|
||||
this.toast.showInfo('APP.TOAST.CLIENTSECRETREGENERATED', true);
|
||||
this.dialog.open(AppSecretDialogComponent, {
|
||||
data: {
|
||||
// clientId: data.toObject().clientId ?? '',
|
||||
clientSecret: data.toObject().clientSecret,
|
||||
clientSecret: resp.clientSecret,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
@@ -535,8 +547,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
|
||||
return this.oidcForm.get('grantTypesList');
|
||||
}
|
||||
|
||||
public get applicationType(): AbstractControl | null {
|
||||
return this.oidcForm.get('applicationType');
|
||||
public get appType(): AbstractControl | null {
|
||||
return this.oidcForm.get('appType');
|
||||
}
|
||||
|
||||
public get authMethodType(): AbstractControl | null {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { RadioItemAuthType } from 'src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component';
|
||||
import { APIAuthMethodType, APIConfig, OIDCAuthMethodType, OIDCConfig, OIDCGrantType, OIDCResponseType } from 'src/app/proto/generated/management_pb';
|
||||
import {
|
||||
APIAuthMethodType,
|
||||
APIConfig,
|
||||
OIDCAuthMethodType,
|
||||
OIDCConfig,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
|
||||
export const CODE_METHOD: RadioItemAuthType = {
|
||||
key: 'CODE',
|
||||
@@ -8,9 +15,9 @@ export const CODE_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'CODE',
|
||||
background: 'rgb(89 115 128)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
|
||||
recommended: false,
|
||||
};
|
||||
export const PKCE_METHOD: RadioItemAuthType = {
|
||||
@@ -20,9 +27,9 @@ export const PKCE_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'PKCE',
|
||||
background: 'rgb(80 110 92)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
recommended: true,
|
||||
};
|
||||
export const POST_METHOD: RadioItemAuthType = {
|
||||
@@ -32,9 +39,9 @@ export const POST_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'POST',
|
||||
background: 'rgb(144 75 75)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
|
||||
notRecommended: true,
|
||||
};
|
||||
export const PK_JWT_METHOD: RadioItemAuthType = {
|
||||
@@ -44,10 +51,10 @@ export const PK_JWT_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'JWT',
|
||||
background: 'rgb(89, 93, 128)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
apiAuthMethod: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
apiAuthMethod: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
// recommended: true,
|
||||
};
|
||||
export const BASIC_AUTH_METHOD: RadioItemAuthType = {
|
||||
@@ -57,10 +64,10 @@ export const BASIC_AUTH_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'BASIC',
|
||||
background: 'rgb(144 75 75)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
||||
apiAuthMethod: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_CODE,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
|
||||
apiAuthMethod: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC,
|
||||
};
|
||||
|
||||
export const IMPLICIT_METHOD: RadioItemAuthType = {
|
||||
@@ -70,9 +77,9 @@ export const IMPLICIT_METHOD: RadioItemAuthType = {
|
||||
disabled: false,
|
||||
prefix: 'IMP',
|
||||
background: 'rgb(144 75 75)',
|
||||
responseType: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN,
|
||||
grantType: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT,
|
||||
authMethod: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
responseType: OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN,
|
||||
grantType: OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT,
|
||||
authMethod: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
notRecommended: true,
|
||||
};
|
||||
|
||||
@@ -97,61 +104,61 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
|
||||
case CODE_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
||||
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
|
||||
},
|
||||
};
|
||||
return config;
|
||||
case PKCE_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
}
|
||||
};
|
||||
return config;
|
||||
case POST_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
||||
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
|
||||
}
|
||||
};
|
||||
return config;
|
||||
case PK_JWT_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
},
|
||||
api: {
|
||||
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
}
|
||||
};
|
||||
return config;
|
||||
case BASIC_AUTH_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
|
||||
},
|
||||
api: {
|
||||
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_BASIC,
|
||||
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC,
|
||||
}
|
||||
};
|
||||
return config;
|
||||
case IMPLICIT_METHOD.key:
|
||||
config = {
|
||||
oidc: {
|
||||
responseTypesList: [OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN],
|
||||
grantTypesList: [OIDCGrantType.OIDCGRANTTYPE_IMPLICIT],
|
||||
authMethodType: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
|
||||
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
|
||||
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
},
|
||||
api: {
|
||||
authMethodType: APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
}
|
||||
};
|
||||
return config;
|
||||
@@ -165,41 +172,41 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
|
||||
const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList, config.oidc.authMethodType];
|
||||
const code = JSON.stringify(
|
||||
[
|
||||
[OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
||||
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
|
||||
]
|
||||
);
|
||||
|
||||
const pkce = JSON.stringify(
|
||||
[
|
||||
[OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
]
|
||||
);
|
||||
|
||||
const post = JSON.stringify(
|
||||
[
|
||||
[OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
||||
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
|
||||
]
|
||||
);
|
||||
|
||||
const pk_jwt = JSON.stringify(
|
||||
[
|
||||
[OIDCResponseType.OIDCRESPONSETYPE_CODE],
|
||||
[OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_PRIVATE_KEY_JWT,
|
||||
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
|
||||
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
|
||||
]
|
||||
);
|
||||
|
||||
const implicit = JSON.stringify(
|
||||
[
|
||||
[OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN],
|
||||
[OIDCGrantType.OIDCGRANTTYPE_IMPLICIT],
|
||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
||||
[OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
|
||||
[OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
|
||||
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -214,8 +221,8 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
|
||||
}
|
||||
} else if (config.api && config.api.authMethodType !== undefined) {
|
||||
switch (config.api.authMethodType.toString()) {
|
||||
case APIAuthMethodType.APIAUTHMETHODTYPE_PRIVATE_KEY_JWT.toString(): return PK_JWT_METHOD.key;
|
||||
case APIAuthMethodType.APIAUTHMETHODTYPE_BASIC.toString(): return BASIC_AUTH_METHOD.key;
|
||||
case APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT.toString(): return PK_JWT_METHOD.key;
|
||||
case APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC.toString(): return BASIC_AUTH_METHOD.key;
|
||||
default:
|
||||
return CUSTOM_METHOD.key;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OIDCApplicationType } from 'src/app/proto/generated/management_pb';
|
||||
import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
|
||||
// export enum AppType {
|
||||
// "WEB",
|
||||
@@ -15,7 +15,7 @@ export enum AppCreateType {
|
||||
export interface RadioItemAppType {
|
||||
// key: string;
|
||||
createType: AppCreateType;
|
||||
oidcApplicationType?: OIDCApplicationType;
|
||||
oidcAppType?: OIDCAppType;
|
||||
titleI18nKey: string;
|
||||
descI18nKey: string;
|
||||
prefix: string;
|
||||
@@ -27,7 +27,7 @@ export const WEB_TYPE = {
|
||||
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.TITLE',
|
||||
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.WEB.DESCRIPTION',
|
||||
createType: AppCreateType.OIDC,
|
||||
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||
oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_WEB,
|
||||
prefix: 'WEB',
|
||||
background: 'rgb(80, 110, 110)',
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export const USER_AGENT_TYPE = {
|
||||
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.TITLE',
|
||||
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.USERAGENT.DESCRIPTION',
|
||||
createType: AppCreateType.OIDC,
|
||||
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||
oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
|
||||
prefix: 'UA',
|
||||
background: '#6a506e',
|
||||
};
|
||||
@@ -47,7 +47,7 @@ export const NATIVE_TYPE = {
|
||||
titleI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.TITLE',
|
||||
descI18nKey: 'APP.OIDC.SELECTION.APPTYPE.NATIVE.DESCRIPTION',
|
||||
createType: AppCreateType.OIDC,
|
||||
oidcApplicationType: OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||
oidcApplicationType: OIDCAppType.OIDC_APP_TYPE_NATIVE,
|
||||
prefix: 'N',
|
||||
background: '#595d80',
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<app-meta-layout>
|
||||
<div class="max-width-container">
|
||||
<div class="head" *ngIf="project?.id">
|
||||
<div class="head" *ngIf="project?.projectId">
|
||||
<a [routerLink]="[ '/granted-projects' ]" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
|
||||
+20
-47
@@ -1,29 +1,17 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import {
|
||||
Application,
|
||||
ApplicationSearchResponse,
|
||||
ProjectGrantView,
|
||||
ProjectMember,
|
||||
ProjectMemberSearchResponse,
|
||||
ProjectMemberView,
|
||||
ProjectRole,
|
||||
ProjectRoleSearchResponse,
|
||||
ProjectState,
|
||||
ProjectType,
|
||||
UserGrantSearchKey,
|
||||
UserView,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { GrantedProject, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -35,41 +23,22 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
public projectId: string = '';
|
||||
public grantId: string = '';
|
||||
public project!: ProjectGrantView.AsObject;
|
||||
|
||||
public pageSizeRoles: number = 10;
|
||||
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
|
||||
public roleResult!: ProjectRoleSearchResponse.AsObject;
|
||||
public roleColumns: string[] = ['name', 'displayname', 'group', 'actions'];
|
||||
|
||||
public pageSizeMembers: number = 10;
|
||||
public projectDataSource: MatTableDataSource<ProjectMember.AsObject> = new MatTableDataSource<ProjectMember.AsObject>();
|
||||
public memberResult!: ProjectMemberSearchResponse.AsObject;
|
||||
public memberColumns: string[] = ['firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
|
||||
|
||||
public pageSizeApps: number = 10;
|
||||
public appsDataSource: MatTableDataSource<Application.AsObject> = new MatTableDataSource<Application.AsObject>();
|
||||
public appsResult!: ApplicationSearchResponse.AsObject;
|
||||
public appsColumns: string[] = ['name'];
|
||||
public project!: GrantedProject.AsObject;
|
||||
|
||||
public ProjectState: any = ProjectState;
|
||||
public ProjectType: any = ProjectType;
|
||||
public ChangeType: any = ChangeType;
|
||||
|
||||
public grid: boolean = true;
|
||||
private subscription?: Subscription;
|
||||
public editstate: boolean = false;
|
||||
|
||||
public isZitadel: boolean = false;
|
||||
|
||||
UserGrantContext: any = UserGrantContext;
|
||||
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
||||
|
||||
// members
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]>
|
||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -96,13 +65,15 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
this.projectId = id;
|
||||
this.grantId = grantId;
|
||||
|
||||
this.mgmtService.GetIam().then(iam => {
|
||||
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
||||
this.mgmtService.getIAM().then(iam => {
|
||||
this.isZitadel = iam.iamProjectId === this.projectId;
|
||||
});
|
||||
|
||||
if (this.projectId && this.grantId) {
|
||||
this.mgmtService.GetGrantedProjectByID(this.projectId, this.grantId).then(proj => {
|
||||
this.project = proj.toObject();
|
||||
this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(proj => {
|
||||
if (proj.grantedProject) {
|
||||
this.project = proj.grantedProject;
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@@ -113,11 +84,13 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public loadMembers(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchProjectGrantMembers(this.projectId,
|
||||
from(this.mgmtService.listProjectGrantMembers(this.projectId,
|
||||
this.grantId, 100, 0)).pipe(
|
||||
map(resp => {
|
||||
this.totalMemberResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalMemberResult = resp.details.totalResult;
|
||||
}
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -140,12 +113,12 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectGrantMember(
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
this.projectId,
|
||||
this.grantId,
|
||||
user.id,
|
||||
|
||||
+7
-6
@@ -2,8 +2,9 @@ import { animate, animateChild, query, stagger, style, transition, trigger } fro
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Org } from 'src/app/proto/generated/auth_pb';
|
||||
import { ProjectGrantView, ProjectState, ProjectType } from 'src/app/proto/generated/management_pb';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { GrantedProject, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { StorageKey, StorageService } from 'src/app/services/storage.service';
|
||||
|
||||
@Component({
|
||||
@@ -31,12 +32,12 @@ import { StorageKey, StorageService } from 'src/app/services/storage.service';
|
||||
],
|
||||
})
|
||||
export class GrantedProjectGridComponent implements OnChanges {
|
||||
@Input() items: Array<ProjectGrantView.AsObject> = [];
|
||||
public notPinned: Array<ProjectGrantView.AsObject> = [];
|
||||
@Input() items: Array<GrantedProject.AsObject> = [];
|
||||
public notPinned: Array<GrantedProject.AsObject> = [];
|
||||
@Output() newClicked: EventEmitter<boolean> = new EventEmitter();
|
||||
@Output() changedView: EventEmitter<boolean> = new EventEmitter();
|
||||
@Input() loading: boolean = false;
|
||||
public selection: SelectionModel<ProjectGrantView.AsObject> = new SelectionModel<ProjectGrantView.AsObject>(true, []);
|
||||
public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
|
||||
|
||||
public showNewProject: boolean = false;
|
||||
public ProjectState: any = ProjectState;
|
||||
@@ -71,7 +72,7 @@ export class GrantedProjectGridComponent implements OnChanges {
|
||||
this.getPrefixedItem('pinned-granted-projects').then(storageEntry => {
|
||||
if (storageEntry) {
|
||||
const array: string[] = JSON.parse(storageEntry);
|
||||
const toSelect: ProjectGrantView.AsObject[] = this.items.filter((item, index) => {
|
||||
const toSelect: GrantedProject.AsObject[] = this.items.filter((item, index) => {
|
||||
if (array.includes(item.projectId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+12
-11
@@ -7,7 +7,7 @@ import { Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { ProjectGrantView } from 'src/app/proto/generated/management_pb';
|
||||
import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -39,13 +39,13 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
|
||||
public dataSource: MatTableDataSource<ProjectGrantView.AsObject> =
|
||||
new MatTableDataSource<ProjectGrantView.AsObject>();
|
||||
public dataSource: MatTableDataSource<GrantedProject.AsObject> =
|
||||
new MatTableDataSource<GrantedProject.AsObject>();
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
|
||||
public grantedProjectList: ProjectGrantView.AsObject[] = [];
|
||||
public grantedProjectList: GrantedProject.AsObject[] = [];
|
||||
public displayedColumns: string[] = ['select', 'name', 'resourceOwnerName', 'state', 'creationDate', 'changeDate'];
|
||||
public selection: SelectionModel<ProjectGrantView.AsObject> = new SelectionModel<ProjectGrantView.AsObject>(true, []);
|
||||
public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
|
||||
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@@ -89,12 +89,13 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
this.mgmtService.SearchGrantedProjects(limit, offset).then(res => {
|
||||
const response = res.toObject();
|
||||
this.grantedProjectList = response.resultList;
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
this.mgmtService.listGrantedProjects(limit, offset).then(resp => {
|
||||
this.grantedProjectList = resp.resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalResult = resp.details.totalResult;
|
||||
}
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
if (this.totalResult > 5) {
|
||||
this.grid = false;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AuthGuard } from 'src/app/guards/auth.guard';
|
||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||
import { ProjectType } from 'src/app/proto/generated/management_pb';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
|
||||
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
|
||||
import { GrantedProjectsComponent } from './granted-projects.component';
|
||||
|
||||
+7
-6
@@ -1,8 +1,9 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { Application, OIDCApplicationType, OIDCResponseType } from 'src/app/proto/generated/management_pb';
|
||||
import { App, OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
import { NATIVE_TYPE, USER_AGENT_TYPE, WEB_TYPE } from '../../../apps/authtypes';
|
||||
|
||||
@Component({
|
||||
@@ -14,10 +15,10 @@ export class ApplicationGridComponent implements OnInit {
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@Output() public changeView: EventEmitter<void> = new EventEmitter();
|
||||
public appsSubject: BehaviorSubject<Application.AsObject[]> = new BehaviorSubject<Application.AsObject[]>([]);
|
||||
public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
public OIDCApplicationType: any = OIDCApplicationType;
|
||||
public OIDCApplicationType: any = OIDCAppType;
|
||||
|
||||
public NATIVE_TYPE: any = NATIVE_TYPE;
|
||||
public WEB_TYPE: any = WEB_TYPE;
|
||||
@@ -30,14 +31,14 @@ export class ApplicationGridComponent implements OnInit {
|
||||
}
|
||||
|
||||
public loadApps(): void {
|
||||
from(this.mgmtService.SearchApplications(this.projectId, 100, 0)).pipe(
|
||||
from(this.mgmtService.listApps(this.projectId, 100, 0)).pipe(
|
||||
map(resp => {
|
||||
return resp.toObject().resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
).subscribe((apps) => {
|
||||
this.appsSubject.next(apps as Application.AsObject[]);
|
||||
this.appsSubject.next(apps as App.AsObject[]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { Application } from 'src/app/proto/generated/management_pb';
|
||||
import { App } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
/**
|
||||
@@ -10,11 +10,11 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class ProjectApplicationsDataSource extends DataSource<Application.AsObject> {
|
||||
export class ProjectApplicationsDataSource extends DataSource<App.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
|
||||
public appsSubject: BehaviorSubject<Application.AsObject[]> = new BehaviorSubject<Application.AsObject[]>([]);
|
||||
public appsSubject: BehaviorSubject<App.AsObject[]> = new BehaviorSubject<App.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -26,12 +26,14 @@ export class ProjectApplicationsDataSource extends DataSource<Application.AsObje
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchApplications(projectId, pageSize, offset)).pipe(
|
||||
from(this.mgmtService.listApps(projectId, pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
const response = resp;
|
||||
if (response.details?.totalResult) {
|
||||
this.totalResult = response.details.totalResult;
|
||||
}
|
||||
if (response.details?.viewTimestamp) {
|
||||
this.viewTimestamp = response.details.viewTimestamp;
|
||||
}
|
||||
return response.resultList;
|
||||
}),
|
||||
@@ -48,7 +50,7 @@ export class ProjectApplicationsDataSource extends DataSource<Application.AsObje
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<Application.AsObject[]> {
|
||||
public connect(): Observable<App.AsObject[]> {
|
||||
return this.appsSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
+5
-6
@@ -5,9 +5,8 @@ import { MatSort } from '@angular/material/sort';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { merge, of } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { Application } from 'src/app/proto/generated/management_pb';
|
||||
import { App } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { ProjectApplicationsDataSource } from './applications-datasource';
|
||||
|
||||
@@ -22,13 +21,13 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
|
||||
@Input() public disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
@ViewChild(MatTable) public table!: MatTable<Application.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<App.AsObject>;
|
||||
public dataSource!: ProjectApplicationsDataSource;
|
||||
public selection: SelectionModel<Application.AsObject> = new SelectionModel<Application.AsObject>(true, []);
|
||||
public selection: SelectionModel<App.AsObject> = new SelectionModel<App.AsObject>(true, []);
|
||||
|
||||
public displayedColumns: string[] = ['select', 'name', 'type'];
|
||||
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService) { }
|
||||
constructor(private mgmtService: ManagementService) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.dataSource = new ProjectApplicationsDataSource(this.mgmtService);
|
||||
@@ -60,7 +59,7 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.appsSubject.value.forEach((row: Application.AsObject) => this.selection.select(row));
|
||||
this.dataSource.appsSubject.value.forEach((row: App.AsObject) => this.selection.select(row));
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
<app-meta-layout>
|
||||
<div class="max-width-container">
|
||||
<div class="head" *ngIf="project?.projectId">
|
||||
<div class="head" *ngIf="project?.id">
|
||||
<a [routerLink]="[ '/projects' ]" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
@@ -22,12 +22,12 @@
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button mat-stroked-button color="warn"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECTSTATE_ACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECTSTATE_INACTIVE)">{{'PROJECT.TABLE.DEACTIVATE' |
|
||||
translate}}</button>
|
||||
<button mat-stroked-button color="warn"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.projectId]| hasRole | async) == false"
|
||||
[disabled]="isZitadel || (['project.write$', 'project.write:'+ project.id]| hasRole | async) == false"
|
||||
*ngIf="project?.state === ProjectState.PROJECTSTATE_INACTIVE"
|
||||
(click)="changeState(ProjectState.PROJECTSTATE_ACTIVE)">{{'PROJECT.TABLE.ACTIVATE' |
|
||||
translate}}</button>
|
||||
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="project">
|
||||
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']">
|
||||
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.id, 'project.app.read']">
|
||||
<app-application-grid *ngIf="grid" [disabled]="isZitadel" (changeView)="grid = false"
|
||||
[projectId]="projectId"></app-application-grid>
|
||||
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
|
||||
@@ -68,18 +68,18 @@
|
||||
|
||||
<ng-container *ngIf="isZitadel == false">
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
|
||||
[appHasRole]="['project.grant.read:' + project.id, 'project.grant.read']">
|
||||
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
|
||||
<app-project-grants
|
||||
[refreshOnPreviousRoutes]="['/projects/'+projectId+'/grants/create','/projects/'+projectId+'/roles/create']"
|
||||
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.projectId]| hasRole | async))== false"
|
||||
[disabled]="((['project.grant.write$', 'project.grant.write:'+ project.id]| hasRole | async))== false"
|
||||
[projectId]="projectId">
|
||||
</app-project-grants>
|
||||
</app-card>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']">
|
||||
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.id, 'project.role.read']">
|
||||
<app-card id="roles" title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
|
||||
<p>{{'PROJECT.ROLE.OPTIONS' | translate}}</p>
|
||||
@@ -92,14 +92,14 @@
|
||||
<p class="desc">{{'PROJECT.ROLE.CHECK_DESCRIPTION' | translate}}</p>
|
||||
<div class="divider"></div>
|
||||
<app-project-roles
|
||||
[disabled]="(['project.role.write$', 'project.role.write:'+ project.projectId]| hasRole | async) == false"
|
||||
[disabled]="(['project.role.write$', 'project.role.write:'+ project.id]| hasRole | async) == false"
|
||||
[actionsVisible]="true" [projectId]="projectId">
|
||||
</app-project-roles>
|
||||
</app-card>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.grant.read']">
|
||||
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
|
||||
<app-card *ngIf="project?.id" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
|
||||
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
|
||||
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
|
||||
[refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]"
|
||||
@@ -131,11 +131,11 @@
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()"
|
||||
[disabled]="(['project.member.write$', 'project.member.write:'+ project.projectId]| hasRole | async) == false">
|
||||
[disabled]="(['project.member.write$', 'project.member.write:'+ project.id]| hasRole | async) == false">
|
||||
</app-contributors>
|
||||
</mat-tab>
|
||||
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="meta-flex-col">
|
||||
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></app-changes>
|
||||
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
+40
-51
@@ -1,4 +1,3 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, EventEmitter, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
@@ -9,22 +8,14 @@ import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import {
|
||||
Application,
|
||||
ApplicationSearchResponse,
|
||||
ProjectMember,
|
||||
ProjectMemberSearchResponse,
|
||||
ProjectMemberView,
|
||||
ProjectRole,
|
||||
ProjectRoleSearchResponse,
|
||||
ProjectState,
|
||||
ProjectType,
|
||||
ProjectView,
|
||||
UserGrantSearchKey,
|
||||
UserView,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { App } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
import { ListAppsResponse, UpdateProjectRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { Project, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -36,22 +27,11 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
})
|
||||
export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
public projectId: string = '';
|
||||
public project!: ProjectView.AsObject;
|
||||
|
||||
public pageSizeRoles: number = 10;
|
||||
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
|
||||
public roleResult!: ProjectRoleSearchResponse.AsObject;
|
||||
public roleColumns: string[] = ['name', 'displayname', 'group', 'actions'];
|
||||
|
||||
public pageSizeMembers: number = 10;
|
||||
public projectDataSource: MatTableDataSource<ProjectMember.AsObject> = new MatTableDataSource<ProjectMember.AsObject>();
|
||||
public memberResult!: ProjectMemberSearchResponse.AsObject;
|
||||
public memberColumns: string[] = ['firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
|
||||
public project!: Project.AsObject;
|
||||
|
||||
public pageSizeApps: number = 10;
|
||||
public appsDataSource: MatTableDataSource<Application.AsObject> = new MatTableDataSource<Application.AsObject>();
|
||||
public appsResult!: ApplicationSearchResponse.AsObject;
|
||||
public appsDataSource: MatTableDataSource<App.AsObject> = new MatTableDataSource<App.AsObject>();
|
||||
public appsResult!: ListAppsResponse.AsObject;
|
||||
public appsColumns: string[] = ['name'];
|
||||
|
||||
public ProjectState: any = ProjectState;
|
||||
@@ -64,13 +44,12 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public isZitadel: boolean = false;
|
||||
|
||||
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
||||
public UserGrantContext: any = UserGrantContext;
|
||||
|
||||
// members
|
||||
public totalMemberResult: number = 0;
|
||||
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]>
|
||||
= new BehaviorSubject<Member.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
public refreshChanges$: EventEmitter<void> = new EventEmitter();
|
||||
@@ -96,12 +75,14 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
private async getData({ id }: Params): Promise<void> {
|
||||
this.projectId = id;
|
||||
|
||||
this.mgmtService.GetIam().then(iam => {
|
||||
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
||||
this.mgmtService.getIAM().then(iam => {
|
||||
this.isZitadel = iam.iamProjectId === this.projectId;
|
||||
});
|
||||
|
||||
this.mgmtService.GetProjectById(id).then(proj => {
|
||||
this.project = proj.toObject();
|
||||
this.mgmtService.getProjectByID(id).then(resp => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
this.toast.showError(error);
|
||||
@@ -112,10 +93,12 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
|
||||
public loadMembers(): void {
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchProjectMembers(this.projectId, 100, 0)).pipe(
|
||||
from(this.mgmtService.listProjectMembers(this.projectId, 100, 0)).pipe(
|
||||
map(resp => {
|
||||
this.totalMemberResult = resp.toObject().totalResult;
|
||||
return resp.toObject().resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalMemberResult = resp.details?.totalResult;
|
||||
}
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -125,7 +108,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public changeState(newState: ProjectState): void {
|
||||
if (newState === ProjectState.PROJECTSTATE_ACTIVE) {
|
||||
if (newState === ProjectState.PROJECT_STATE_ACTIVE) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.REACTIVATE',
|
||||
@@ -137,9 +120,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtService.ReactivateProject(this.projectId).then(() => {
|
||||
this.mgmtService.reactivateProject(this.projectId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true);
|
||||
this.project.state = ProjectState.PROJECTSTATE_ACTIVE;
|
||||
this.project.state = ProjectState.PROJECT_STATE_ACTIVE;
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -147,7 +130,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
});
|
||||
|
||||
} else if (newState === ProjectState.PROJECTSTATE_INACTIVE) {
|
||||
} else if (newState === ProjectState.PROJECT_STATE_INACTIVE) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DEACTIVATE',
|
||||
@@ -159,9 +142,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtService.DeactivateProject(this.projectId).then(() => {
|
||||
this.mgmtService.deactivateProject(this.projectId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
|
||||
this.project.state = ProjectState.PROJECTSTATE_INACTIVE;
|
||||
this.project.state = ProjectState.PROJECT_STATE_INACTIVE;
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -183,7 +166,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtService.RemoveProject(this.projectId).then(() => {
|
||||
this.mgmtService.removeProject(this.projectId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DELETED', true);
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
@@ -197,7 +180,13 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public saveProject(): void {
|
||||
this.mgmtService.UpdateProject(this.project.projectId, this.project).then(() => {
|
||||
const req = new UpdateProjectRequest();
|
||||
req.setId(this.project.id);
|
||||
req.setName(this.project.name);
|
||||
req.setProjectRoleAssertion(this.project.projectRoleAssertion);
|
||||
req.setProjectRoleCheck(this.project.projectRoleCheck);
|
||||
|
||||
this.mgmtService.updateProject(req).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.UPDATED', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
@@ -218,19 +207,19 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
data: {
|
||||
creationType: CreationType.PROJECT_OWNED,
|
||||
projectId: this.project.projectId,
|
||||
projectId: this.project.id,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: UserView.AsObject[] = resp.users;
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectMember(this.projectId, user.id, roles)
|
||||
return this.mgmtService.addProjectMember(this.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -246,6 +235,6 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public showDetail(): void {
|
||||
this.router.navigate(['projects', this.project.projectId, 'members']);
|
||||
this.router.navigate(['projects', this.project.id, 'members']);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-10
@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { ProjectGrant } from 'src/app/proto/generated/management_pb';
|
||||
import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
/**
|
||||
@@ -10,10 +10,10 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
||||
export class ProjectGrantsDataSource extends DataSource<GrantedProject.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public grantsSubject: BehaviorSubject<ProjectGrant.AsObject[]> = new BehaviorSubject<ProjectGrant.AsObject[]>([]);
|
||||
public grantsSubject: BehaviorSubject<GrantedProject.AsObject[]> = new BehaviorSubject<GrantedProject.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -25,14 +25,15 @@ export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtService.SearchProjectGrants(projectId, pageSize, offset)).pipe(
|
||||
from(this.mgmtService.listProjectGrants(projectId, pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalResult = resp.details.totalResult;
|
||||
}
|
||||
return response.resultList;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -47,7 +48,7 @@ export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<ProjectGrant.AsObject[]> {
|
||||
public connect(): Observable<GrantedProject.AsObject[]> {
|
||||
return this.grantsSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -5,7 +5,7 @@ import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { ProjectGrant, ProjectRoleView } from 'src/app/proto/generated/management_pb';
|
||||
import { GrantedProject, Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -28,10 +28,10 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatTable) public table!: MatTable<ProjectGrant.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>;
|
||||
public dataSource!: ProjectGrantsDataSource;
|
||||
public selection: SelectionModel<ProjectGrant.AsObject> = new SelectionModel<ProjectGrant.AsObject>(true, []);
|
||||
public memberRoleOptions: ProjectRoleView.AsObject[] = [];
|
||||
public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
|
||||
public memberRoleOptions: Role.AsObject[] = [];
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'grantedOrgName', 'dates'];
|
||||
@@ -73,14 +73,14 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
public getRoleOptions(projectId: string): void {
|
||||
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => {
|
||||
this.memberRoleOptions = resp.toObject().resultList;
|
||||
this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
});
|
||||
}
|
||||
|
||||
updateRoles(grant: ProjectGrant.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.UpdateProjectGrant(grant.id, grant.projectId, selectionChange.value)
|
||||
.then((newgrant: ProjectGrant) => {
|
||||
updateRoles(grant: GrantedProject.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.updateProjectGrant(grant.grantId, grant.projectId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -89,14 +89,14 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
deleteSelectedGrants(): void {
|
||||
const promises = this.selection.selected.map(grant => {
|
||||
return this.mgmtService.RemoveProjectGrant(grant.id, grant.projectId);
|
||||
return this.mgmtService.removeProjectGrant(grant.grantId, grant.projectId);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
|
||||
const data = this.dataSource.grantsSubject.getValue();
|
||||
this.selection.selected.forEach((item) => {
|
||||
const index = data.findIndex(i => i.id === item.id);
|
||||
const index = data.findIndex(i => i.grantId === item.grantId);
|
||||
if (index > -1) {
|
||||
data.splice(index, 1);
|
||||
this.dataSource.grantsSubject.next(data);
|
||||
|
||||
+20
-19
@@ -3,9 +3,10 @@ import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { Org } from 'src/app/proto/generated/auth_pb';
|
||||
import { ProjectState, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { Project, ProjectState } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { StorageKey, StorageService } from 'src/app/services/storage.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -39,14 +40,14 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
],
|
||||
})
|
||||
export class OwnedProjectGridComponent implements OnChanges {
|
||||
@Input() items: Array<ProjectView.AsObject> = [];
|
||||
public notPinned: Array<ProjectView.AsObject> = [];
|
||||
@Input() items: Array<Project.AsObject> = [];
|
||||
public notPinned: Array<Project.AsObject> = [];
|
||||
|
||||
@Output() newClicked: EventEmitter<boolean> = new EventEmitter();
|
||||
@Output() changedView: EventEmitter<boolean> = new EventEmitter();
|
||||
@Input() loading: boolean = false;
|
||||
|
||||
public selection: SelectionModel<ProjectView.AsObject> = new SelectionModel<ProjectView.AsObject>(true, []);
|
||||
public selection: SelectionModel<Project.AsObject> = new SelectionModel<Project.AsObject>(true, []);
|
||||
|
||||
public showNewProject: boolean = false;
|
||||
public ProjectState: any = ProjectState;
|
||||
@@ -61,10 +62,10 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
) {
|
||||
this.selection.changed.subscribe(selection => {
|
||||
this.setPrefixedItem('pinned-projects', JSON.stringify(
|
||||
this.selection.selected.map(item => item.projectId),
|
||||
this.selection.selected.map(item => item.id),
|
||||
)).then(() => {
|
||||
selection.added.forEach(item => {
|
||||
const index = this.notPinned.findIndex(i => i.projectId === item.projectId);
|
||||
const index = this.notPinned.findIndex(i => i.id === item.id);
|
||||
this.notPinned.splice(index, 1);
|
||||
});
|
||||
this.notPinned.push(...selection.removed);
|
||||
@@ -72,11 +73,11 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
public selectItem(item: ProjectView.AsObject, event?: any): void {
|
||||
public selectItem(item: Project.AsObject, event?: any): void {
|
||||
if (event && !event.target.classList.contains('mat-icon')) {
|
||||
this.router.navigate(['/projects', item.projectId]);
|
||||
this.router.navigate(['/projects', item.id]);
|
||||
} else if (!event) {
|
||||
this.router.navigate(['/projects', item.projectId]);
|
||||
this.router.navigate(['/projects', item.id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +96,8 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.getPrefixedItem('pinned-projects').then(storageEntry => {
|
||||
if (storageEntry) {
|
||||
const array: string[] = JSON.parse(storageEntry);
|
||||
const toSelect: ProjectView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (array.includes(item.projectId)) {
|
||||
const toSelect: Project.AsObject[] = this.items.filter((item, index) => {
|
||||
if (array.includes(item.id)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -125,12 +126,12 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.changedView.emit(true);
|
||||
}
|
||||
|
||||
public toggle(item: ProjectView.AsObject, event: any): void {
|
||||
public toggle(item: Project.AsObject, event: any): void {
|
||||
event.stopPropagation();
|
||||
this.selection.toggle(item);
|
||||
}
|
||||
|
||||
public deleteProject(event: any, item: ProjectView.AsObject): void {
|
||||
public deleteProject(event: any, item: Project.AsObject): void {
|
||||
event.stopPropagation();
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
@@ -143,20 +144,20 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && item.projectId !== this.zitadelProjectId) {
|
||||
this.mgmtService.RemoveProject(item.projectId).then(() => {
|
||||
if (resp && item.id !== this.zitadelProjectId) {
|
||||
this.mgmtService.removeProject(item.id).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DELETED', true);
|
||||
const index = this.items.findIndex(iter => iter.projectId === item.projectId);
|
||||
const index = this.items.findIndex(iter => iter.id === item.id);
|
||||
if (index > -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
|
||||
const indexSelection = this.selection.selected.findIndex(iter => iter.projectId === item.projectId);
|
||||
const indexSelection = this.selection.selected.findIndex(iter => iter.id === item.id);
|
||||
if (indexSelection > -1) {
|
||||
this.selection.selected.splice(indexSelection, 1);
|
||||
}
|
||||
|
||||
const indexPinned = this.notPinned.findIndex(iter => iter.projectId === item.projectId);
|
||||
const indexPinned = this.notPinned.findIndex(iter => iter.id === item.id);
|
||||
if (indexPinned > -1) {
|
||||
this.notPinned.splice(indexPinned, 1);
|
||||
}
|
||||
|
||||
+19
-18
@@ -10,7 +10,7 @@ import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { ProjectView } from 'src/app/proto/generated/management_pb';
|
||||
import { Project } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -42,14 +42,14 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
|
||||
public dataSource: MatTableDataSource<ProjectView.AsObject> =
|
||||
new MatTableDataSource<ProjectView.AsObject>();
|
||||
public dataSource: MatTableDataSource<Project.AsObject> =
|
||||
new MatTableDataSource<Project.AsObject>();
|
||||
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
|
||||
public ownedProjectList: ProjectView.AsObject[] = [];
|
||||
public ownedProjectList: Project.AsObject[] = [];
|
||||
public displayedColumns: string[] = ['select', 'name', 'state', 'creationDate', 'changeDate', 'actions'];
|
||||
public selection: SelectionModel<ProjectView.AsObject> = new SelectionModel<ProjectView.AsObject>(true, []);
|
||||
public selection: SelectionModel<Project.AsObject> = new SelectionModel<Project.AsObject>(true, []);
|
||||
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@@ -66,8 +66,8 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog,
|
||||
) {
|
||||
this.mgmtService.GetIam().then(iam => {
|
||||
this.zitadelProjectId = iam.toObject().iamProjectId;
|
||||
this.mgmtService.getIAM().then(iam => {
|
||||
this.zitadelProjectId = iam.iamProjectId;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,15 +108,16 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
|
||||
private async getData(limit?: number, offset?: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
this.mgmtService.SearchProjects(limit, offset).then(res => {
|
||||
const response = res.toObject();
|
||||
this.ownedProjectList = response.resultList;
|
||||
this.totalResult = response.totalResult;
|
||||
this.mgmtService.listProjects(limit, offset).then(resp => {
|
||||
this.ownedProjectList = resp.resultList;
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalResult = resp.details.totalResult;
|
||||
}
|
||||
if (this.totalResult > 10) {
|
||||
this.grid = false;
|
||||
}
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
this.dataSource.data = this.ownedProjectList;
|
||||
this.loadingSubject.next(false);
|
||||
@@ -131,7 +132,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
|
||||
public reactivateSelectedProjects(): void {
|
||||
const promises = this.selection.selected.map(project => {
|
||||
this.mgmtService.ReactivateProject(project.projectId);
|
||||
this.mgmtService.reactivateProject(project.id);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
@@ -144,7 +145,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
|
||||
public deactivateSelectedProjects(): void {
|
||||
const promises = this.selection.selected.map(project => {
|
||||
this.mgmtService.DeactivateProject(project.projectId);
|
||||
this.mgmtService.deactivateProject(project.id);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
@@ -159,7 +160,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||
}
|
||||
|
||||
public deleteProject(item: ProjectView.AsObject): void {
|
||||
public deleteProject(item: Project.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@@ -171,8 +172,8 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (this.zitadelProjectId && resp && item.projectId !== this.zitadelProjectId) {
|
||||
this.mgmtService.RemoveProject(item.projectId).then(() => {
|
||||
if (this.zitadelProjectId && resp && item.id !== this.zitadelProjectId) {
|
||||
this.mgmtService.removeProject(item.id).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DELETED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||
import { ProjectType } from 'src/app/proto/generated/management_pb';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
|
||||
import { OwnedProjectsComponent } from './owned-projects.component';
|
||||
|
||||
|
||||
+6
-6
@@ -10,7 +10,7 @@
|
||||
<div class="row">
|
||||
<span class="first">{{'PROJECT.GRANT.DETAIL.RESOURCEOWNER' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<span>{{grant?.resourceOwnerName}}</span>
|
||||
<span>{{grant?.details?.resourceOwner}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.roleKeysList">
|
||||
<cnsl-form-field class="formfield" appearance="outline" *ngIf="grant && grant.grantedRoleKeysList">
|
||||
<cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple (selectionChange)="updateRoles($event)">
|
||||
<mat-select [(ngModel)]="grant.grantedRoleKeysList" multiple (selectionChange)="updateRoles($event)">
|
||||
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
|
||||
{{role.key}}
|
||||
</mat-option>
|
||||
@@ -39,17 +39,17 @@
|
||||
<h1 class="h1">{{ 'PROJECT.GRANT.DETAIL.MEMBERTITLE' | translate }}</h1>
|
||||
<p class="desc">{{ 'PROJECT.GRANT.DETAIL.MEMBERDESC' | translate }}</p>
|
||||
|
||||
<app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource" [canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.id] | hasRole | async"
|
||||
<app-members-table *ngIf="grant" style="width: 100%;" [dataSource]="dataSource" [canWrite]="['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async"
|
||||
[memberRoleOptions]="memberRoleOptions" (updateRoles)="updateMemberRoles($event.member, $event.change)"
|
||||
[factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage">
|
||||
<button selectactions (click)="removeProjectMemberSelection()"
|
||||
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.id] | hasRole | async) == false"
|
||||
[disabled]="(['project.grant.member.delete','project.grant.member.delete:' + grant.grantId] | hasRole | async) == false"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" color="warn" mat-raised-button>
|
||||
<i class="las la-trash"></i>
|
||||
{{'ACTIONS.SELECTIONDELETE' | translate}}
|
||||
</button>
|
||||
<a writeactions color="primary"
|
||||
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.id] | hasRole | async) == false"
|
||||
[disabled]="(['project.grant.member.write','project.grant.member.write:' + grant.grantId] | hasRole | async) == false"
|
||||
(click)="openAddMember()" color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
|
||||
+28
-32
@@ -3,15 +3,9 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { PageEvent } from '@angular/material/paginator';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import {
|
||||
ProjectGrant,
|
||||
ProjectGrantMember,
|
||||
ProjectGrantMemberView,
|
||||
ProjectGrantState,
|
||||
ProjectGrantView,
|
||||
ProjectRoleView,
|
||||
ProjectType,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { ProjectType } from 'src/app/modules/project-members/project-members.component';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -29,7 +23,7 @@ import { ProjectGrantMembersDataSource } from './project-grant-members-datasourc
|
||||
export class ProjectGrantDetailComponent {
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
|
||||
public grant!: ProjectGrantView.AsObject;
|
||||
public grant!: GrantedProject.AsObject;
|
||||
public projectid: string = '';
|
||||
public grantid: string = '';
|
||||
|
||||
@@ -39,12 +33,12 @@ export class ProjectGrantDetailComponent {
|
||||
public isZitadel: boolean = false;
|
||||
ProjectGrantState: any = ProjectGrantState;
|
||||
|
||||
public projectRoleOptions: ProjectRoleView.AsObject[] = [];
|
||||
public projectRoleOptions: Role.AsObject[] = [];
|
||||
public memberRoleOptions: Array<string> = [];
|
||||
|
||||
public changePageFactory!: Function;
|
||||
public changePage: EventEmitter<void> = new EventEmitter();
|
||||
public selection: Array<ProjectGrantMemberView.AsObject> = [];
|
||||
public selection: Array<Member.AsObject> = [];
|
||||
public dataSource!: ProjectGrantMembersDataSource;
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
@@ -71,22 +65,24 @@ export class ProjectGrantDetailComponent {
|
||||
);
|
||||
};
|
||||
|
||||
this.mgmtService.ProjectGrantByID(this.grantid, this.projectid).then((grant) => {
|
||||
this.grant = grant.toObject();
|
||||
this.mgmtService.getProjectGrantByID(this.grantid, this.projectid).then((resp) => {
|
||||
if (resp.projectGrant) {
|
||||
this.grant = resp.projectGrant;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public changeState(newState: ProjectGrantState): void {
|
||||
if (newState === ProjectGrantState.PROJECTGRANTSTATE_ACTIVE) {
|
||||
this.mgmtService.ReactivateProjectGrant(this.grantid, this.projectid).then(() => {
|
||||
if (newState === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE) {
|
||||
this.mgmtService.reactivateProjectGrant(this.grantid, this.projectid).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true);
|
||||
this.grant.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (newState === ProjectGrantState.PROJECTGRANTSTATE_INACTIVE) {
|
||||
this.mgmtService.DeactivateProjectGrant(this.grantid, this.projectid).then(() => {
|
||||
} else if (newState === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE) {
|
||||
this.mgmtService.deactivateProjectGrant(this.grantid, this.projectid).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
|
||||
this.grant.state = newState;
|
||||
}).catch(error => {
|
||||
@@ -96,22 +92,22 @@ export class ProjectGrantDetailComponent {
|
||||
}
|
||||
|
||||
public getRoleOptions(projectId: string): void {
|
||||
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => {
|
||||
this.projectRoleOptions = resp.toObject().resultList;
|
||||
this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
|
||||
this.projectRoleOptions = resp.resultList;
|
||||
});
|
||||
}
|
||||
|
||||
public getMemberRoleOptions(): void {
|
||||
this.mgmtService.GetProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.toObject().rolesList;
|
||||
this.mgmtService.listProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
updateRoles(selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.UpdateProjectGrant(this.grant.id, this.grant.projectId, selectionChange.value)
|
||||
.then((newgrant: ProjectGrant) => {
|
||||
this.mgmtService.updateProjectGrant(this.grant.grantId, this.grant.projectId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.GRANTUPDATED');
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -120,7 +116,7 @@ export class ProjectGrantDetailComponent {
|
||||
|
||||
public removeProjectMemberSelection(): void {
|
||||
Promise.all(this.selection.map(member => {
|
||||
return this.mgmtService.RemoveProjectGrantMember(this.grant.projectId, this.grant.id, member.userId).then(() => {
|
||||
return this.mgmtService.removeProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
@@ -132,11 +128,11 @@ export class ProjectGrantDetailComponent {
|
||||
}
|
||||
|
||||
public async openAddMember(): Promise<any> {
|
||||
const keysList = (await this.mgmtService.GetProjectGrantMemberRoles()).toObject();
|
||||
const keysList = (await this.mgmtService.listProjectGrantMemberRoles());
|
||||
|
||||
const dialogRef = this.dialog.open(ProjectGrantMembersCreateDialogComponent, {
|
||||
data: {
|
||||
roleKeysList: keysList.rolesList,
|
||||
roleKeysList: keysList.resultList,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
@@ -144,9 +140,9 @@ export class ProjectGrantDetailComponent {
|
||||
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => {
|
||||
if (dataToAdd) {
|
||||
Promise.all(dataToAdd.userIds.map((userid: string) => {
|
||||
return this.mgmtService.AddProjectGrantMember(
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
this.grant.projectId,
|
||||
this.grant.id,
|
||||
this.grant.grantId,
|
||||
userid,
|
||||
dataToAdd.rolesKeyList,
|
||||
);
|
||||
@@ -162,9 +158,9 @@ export class ProjectGrantDetailComponent {
|
||||
});
|
||||
}
|
||||
|
||||
updateMemberRoles(member: ProjectGrantMember.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.ChangeProjectGrantMember(this.grant.projectId, this.grant.id, member.userId, selectionChange.value)
|
||||
.then((_: ProjectGrantMember) => {
|
||||
updateMemberRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
|
||||
this.mgmtService.updateProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
export interface ProjectGrantMembersCreateDialogExportType {
|
||||
userIds: string[];
|
||||
@@ -22,7 +22,7 @@ export class ProjectGrantMembersCreateDialogComponent {
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) { }
|
||||
|
||||
public selectUsers(users: UserView.AsObject[]): void {
|
||||
public selectUsers(users: User.AsObject[]): void {
|
||||
this.userIds = users.map(user => user.id);
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -2,7 +2,7 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { ProjectMember } from 'src/app/proto/generated/management_pb';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
/**
|
||||
@@ -10,11 +10,11 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
* encapsulate all logic for fetching and manipulating the displayed data
|
||||
* (including sorting, pagination, and filtering).
|
||||
*/
|
||||
export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsObject> {
|
||||
export class ProjectGrantMembersDataSource extends DataSource<Member.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
|
||||
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Member.AsObject[]> = new BehaviorSubject<Member.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -28,15 +28,14 @@ export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsOb
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
from(this.mgmtService.SearchProjectGrantMembers(projectId,
|
||||
from(this.mgmtService.listProjectGrantMembers(projectId,
|
||||
grantId, pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
return response.resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -51,7 +50,7 @@ export class ProjectGrantMembersDataSource extends DataSource<ProjectMember.AsOb
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<ProjectMember.AsObject[]> {
|
||||
public connect(): Observable<Member.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Project, ProjectCreateRequest } from 'src/app/proto/generated/management_pb';
|
||||
import { AddProjectRequest, AddProjectResponse } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
styleUrls: ['./project-create.component.scss'],
|
||||
})
|
||||
export class ProjectCreateComponent implements OnInit {
|
||||
public project: ProjectCreateRequest.AsObject = new ProjectCreateRequest().toObject();
|
||||
public project: AddProjectRequest.AsObject = new AddProjectRequest().toObject();
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@@ -26,9 +26,9 @@ export class ProjectCreateComponent implements OnInit {
|
||||
|
||||
public saveProject(): void {
|
||||
this.mgmtService
|
||||
.CreateProject(this.project)
|
||||
.then((data: Project) => {
|
||||
this.router.navigate(['projects', data.getId()]);
|
||||
.addProject(this.project)
|
||||
.then((resp: AddProjectResponse.AsObject) => {
|
||||
this.router.navigate(['projects', resp.id]);
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
|
||||
+14
-11
@@ -2,7 +2,8 @@ import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Org, ProjectRole } from 'src/app/proto/generated/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -42,13 +43,15 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
public searchOrg(domain: string): void {
|
||||
this.mgmtService.getOrgByDomainGlobal(domain).then((ret) => {
|
||||
const tmp = ret.toObject();
|
||||
this.authService.GetActiveOrg().then((org) => {
|
||||
if (tmp !== org) {
|
||||
this.org = tmp;
|
||||
}
|
||||
});
|
||||
this.org = ret.toObject();
|
||||
if (ret.org) {
|
||||
const tmp = ret.org;
|
||||
this.authService.getActiveOrg().then((org) => {
|
||||
if (tmp !== org) {
|
||||
this.org = tmp;
|
||||
}
|
||||
});
|
||||
this.org = ret.org;
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@@ -60,8 +63,8 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
|
||||
|
||||
public addGrant(): void {
|
||||
this.mgmtService
|
||||
.CreateProjectGrant(this.org.id, this.projectId, this.rolesKeyList)
|
||||
.then((data) => {
|
||||
.addProjectGrant(this.org.id, this.projectId, this.rolesKeyList)
|
||||
.then(() => {
|
||||
this.close();
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -69,7 +72,7 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
public selectRoles(roles: ProjectRole.AsObject[]): void {
|
||||
public selectRoles(roles: Role.AsObject[]): void {
|
||||
this.rolesKeyList = roles.map(role => role.key);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ProjectRoleAdd } from 'src/app/proto/generated/management_pb';
|
||||
import { BulkAddProjectRolesRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -84,16 +84,15 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public addRole(): void {
|
||||
const rolesToAdd: ProjectRoleAdd[] = this.formArray.value.map((element: any) => {
|
||||
const role = new ProjectRoleAdd();
|
||||
const rolesToAdd: BulkAddProjectRolesRequest.Role[] = this.formArray.value.map((element: any) => {
|
||||
const role = new BulkAddProjectRolesRequest.Role;
|
||||
role.setKey(element.key);
|
||||
role.setDisplayName(element.displayName);
|
||||
role.setGroup(element.group);
|
||||
role.setId(this.projectId);
|
||||
return role;
|
||||
});
|
||||
|
||||
this.mgmtService.BulkAddProjectRole(this.projectId, rolesToAdd).then(() => {
|
||||
this.mgmtService.bulkAddProjectRoles(this.projectId, rolesToAdd).then(() => {
|
||||
this.router.navigate(['projects', this.projectId]);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
|
||||
@@ -4,8 +4,9 @@ import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { UserTarget } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import { Org } from 'src/app/proto/generated/auth_pb';
|
||||
import { ProjectGrantView, ProjectRole, ProjectView, UserGrant, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { GrantedProject, Project, Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -22,7 +23,7 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
public userId: string = '';
|
||||
|
||||
public projectId: string = '';
|
||||
public project!: ProjectGrantView.AsObject | ProjectView.AsObject;
|
||||
public project!: GrantedProject.AsObject | Project.AsObject;
|
||||
|
||||
public grantId: string = '';
|
||||
public rolesList: string[] = [];
|
||||
@@ -38,11 +39,11 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
|
||||
public grantRolesKeyList: string[] = [];
|
||||
|
||||
public user!: UserView.AsObject;
|
||||
public user!: User.AsObject;
|
||||
public UserTarget: any = UserTarget;
|
||||
|
||||
public ProjectGrantView: any = ProjectGrantView;
|
||||
public ProjectView: any = ProjectView;
|
||||
public ProjectGrantView: any = GrantedProject;
|
||||
public ProjectView: any = Project;
|
||||
constructor(
|
||||
private userService: ManagementService,
|
||||
private toast: ToastService,
|
||||
@@ -63,22 +64,26 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
this.context = UserGrantContext.OWNED_PROJECT;
|
||||
} else if (this.projectId && this.grantId) {
|
||||
this.context = UserGrantContext.GRANTED_PROJECT;
|
||||
this.mgmtService.GetGrantedProjectByID(this.projectId, this.grantId).then(resp => {
|
||||
this.grantRolesKeyList = resp.toObject().roleKeysList;
|
||||
this.mgmtService.getGrantedProjectByID(this.projectId, this.grantId).then(resp => {
|
||||
if (resp.grantedProject?.grantedRoleKeysList) {
|
||||
this.grantRolesKeyList = resp.grantedProject?.grantedRoleKeysList;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.userId) {
|
||||
this.context = UserGrantContext.USER;
|
||||
this.mgmtService.GetUserByID(this.userId).then(resp => {
|
||||
this.user = resp.toObject();
|
||||
this.mgmtService.getUserByID(this.userId).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.authService.GetActiveOrg().then(org => {
|
||||
this.authService.getActiveOrg().then(org => {
|
||||
this.org = org;
|
||||
});
|
||||
}
|
||||
@@ -90,11 +95,11 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
public addGrant(): void {
|
||||
switch (this.context) {
|
||||
case UserGrantContext.OWNED_PROJECT:
|
||||
this.userService.CreateUserGrant(
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
).then((data: UserGrant) => {
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
@@ -102,12 +107,12 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
});
|
||||
break;
|
||||
case UserGrantContext.GRANTED_PROJECT:
|
||||
this.userService.CreateUserGrant(
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.projectId,
|
||||
this.grantId,
|
||||
).then((data: UserGrant) => {
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
@@ -117,16 +122,16 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
case UserGrantContext.USER:
|
||||
let grantId;
|
||||
|
||||
if ((this.project as ProjectGrantView.AsObject)?.id) {
|
||||
grantId = (this.project as ProjectGrantView.AsObject).id;
|
||||
if ((this.project as GrantedProject.AsObject)?.grantId) {
|
||||
grantId = (this.project as GrantedProject.AsObject).grantId;
|
||||
}
|
||||
|
||||
this.userService.CreateUserGrant(
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.project.projectId,
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
grantId,
|
||||
).then((data: UserGrant) => {
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
@@ -136,16 +141,16 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
case UserGrantContext.NONE:
|
||||
let tempGrantId;
|
||||
|
||||
if ((this.project as ProjectGrantView.AsObject)?.id) {
|
||||
tempGrantId = (this.project as ProjectGrantView.AsObject).id;
|
||||
if ((this.project as GrantedProject.AsObject)?.projectId) {
|
||||
tempGrantId = (this.project as GrantedProject.AsObject).projectId;
|
||||
}
|
||||
|
||||
this.userService.CreateUserGrant(
|
||||
this.userService.addUserGrant(
|
||||
this.userId,
|
||||
this.rolesList,
|
||||
this.project.projectId,
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
tempGrantId,
|
||||
).then((data: UserGrant) => {
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true);
|
||||
this.close();
|
||||
}).catch((error: any) => {
|
||||
@@ -156,17 +161,17 @@ export class UserGrantCreateComponent implements OnDestroy {
|
||||
|
||||
}
|
||||
|
||||
public selectProject(project: ProjectView.AsObject | ProjectGrantView.AsObject | any): void {
|
||||
public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
|
||||
this.project = project;
|
||||
this.projectId = project.projectId;
|
||||
this.grantRolesKeyList = project.roleKeysList ?? [];
|
||||
}
|
||||
|
||||
public selectUser(user: UserView.AsObject): void {
|
||||
public selectUser(user: User.AsObject): void {
|
||||
this.userId = user.id;
|
||||
}
|
||||
|
||||
public selectRoles(roles: ProjectRole.AsObject[]): void {
|
||||
public selectRoles(roles: Role.AsObject[]): void {
|
||||
this.rolesList = roles.map(role => role.key);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,36 +2,17 @@ import { Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CreateMachineRequest } from 'src/app/proto/generated/admin_pb';
|
||||
import { UserResponse } from 'src/app/proto/generated/management_pb';
|
||||
import { AddMachineUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
function noEmailValidator(c: AbstractControl): any {
|
||||
const EMAIL_REGEXP: RegExp = /^((?!@).)*$/gm;
|
||||
if (!c.parent || !c) {
|
||||
return;
|
||||
}
|
||||
const username = c.parent.get('userName');
|
||||
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
return EMAIL_REGEXP.test(username.value) ? null : {
|
||||
noEmailValidator: {
|
||||
valid: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-create-machine',
|
||||
templateUrl: './user-create-machine.component.html',
|
||||
styleUrls: ['./user-create-machine.component.scss'],
|
||||
})
|
||||
export class UserCreateMachineComponent implements OnDestroy {
|
||||
public user: CreateMachineRequest.AsObject = new CreateMachineRequest().toObject();
|
||||
public user: AddMachineUserRequest.AsObject = new AddMachineUserRequest().toObject();
|
||||
public userForm!: FormGroup;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
@@ -64,16 +45,17 @@ export class UserCreateMachineComponent implements OnDestroy {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const machineReq = new CreateMachineRequest();
|
||||
const machineReq = new AddMachineUserRequest();
|
||||
machineReq.setDescription(this.description?.value);
|
||||
machineReq.setName(this.name?.value);
|
||||
machineReq.setUserName(this.userName?.value);
|
||||
|
||||
this.userService
|
||||
.CreateUserMachine(this.userName?.value, machineReq)
|
||||
.then((data: UserResponse) => {
|
||||
.addMachineUser(machineReq)
|
||||
.then((data) => {
|
||||
this.loading = false;
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
const id = data.getId();
|
||||
const id = data.userId;
|
||||
if (id) {
|
||||
this.router.navigate(['users', id]);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,9 @@ import { ChangeDetectorRef, Component, OnDestroy, ViewChild } from '@angular/cor
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import {
|
||||
CreateHumanRequest,
|
||||
CreateUserRequest,
|
||||
Gender,
|
||||
OrgDomain,
|
||||
UserResponse,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { AddHumanUserRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Domain } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { Gender } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -36,7 +32,7 @@ function noEmailValidator(c: AbstractControl): any {
|
||||
styleUrls: ['./user-create.component.scss'],
|
||||
})
|
||||
export class UserCreateComponent implements OnDestroy {
|
||||
public user: CreateUserRequest.AsObject = new CreateUserRequest().toObject();
|
||||
public user: AddHumanUserRequest.AsObject = new AddHumanUserRequest().toObject();
|
||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
public userForm!: FormGroup;
|
||||
@@ -47,7 +43,7 @@ export class UserCreateComponent implements OnDestroy {
|
||||
public loading: boolean = false;
|
||||
|
||||
@ViewChild('suffix') public suffix!: any;
|
||||
private primaryDomain!: OrgDomain.AsObject;
|
||||
private primaryDomain!: Domain.AsObject;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@@ -58,8 +54,10 @@ export class UserCreateComponent implements OnDestroy {
|
||||
) {
|
||||
this.loading = true;
|
||||
this.loadOrg();
|
||||
this.mgmtService.GetMyOrgIamPolicy().then((iampolicy) => {
|
||||
this.userLoginMustBeDomain = iampolicy.toObject().userLoginMustBeDomain;
|
||||
this.mgmtService.getOrgIAMPolicy().then((resp) => {
|
||||
if (resp.policy?.userLoginMustBeDomain) {
|
||||
this.userLoginMustBeDomain = resp.policy.userLoginMustBeDomain;
|
||||
}
|
||||
this.initForm();
|
||||
this.loading = false;
|
||||
this.envSuffixLabel = this.envSuffix();
|
||||
@@ -74,8 +72,8 @@ export class UserCreateComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private async loadOrg(): Promise<void> {
|
||||
const domains = (await this.mgmtService.SearchMyOrgDomains().then(doms => doms.toObject()));
|
||||
const found = domains.resultList.find(domain => domain.primary);
|
||||
const domains = (await this.mgmtService.listOrgDomains());
|
||||
const found = domains.resultList.find(resp => resp.isPrimary);
|
||||
if (found) {
|
||||
this.primaryDomain = found;
|
||||
}
|
||||
@@ -110,22 +108,26 @@ export class UserCreateComponent implements OnDestroy {
|
||||
|
||||
this.loading = true;
|
||||
|
||||
const humanReq = new CreateHumanRequest();
|
||||
humanReq.setFirstName(this.firstName?.value);
|
||||
humanReq.setLastName(this.lastName?.value);
|
||||
humanReq.setNickName(this.nickName?.value);
|
||||
humanReq.setPreferredLanguage(this.preferredLanguage?.value);
|
||||
const profileReq = new AddHumanUserRequest.Profile();
|
||||
profileReq.setFirstName(this.firstName?.value);
|
||||
profileReq.setLastName(this.lastName?.value);
|
||||
profileReq.setNickName(this.nickName?.value);
|
||||
profileReq.setPreferredLanguage(this.preferredLanguage?.value);
|
||||
profileReq.setGender(this.gender?.value);
|
||||
|
||||
const humanReq = new AddHumanUserRequest();
|
||||
humanReq.setUserName(this.userName?.value);
|
||||
humanReq.setProfile(profileReq);
|
||||
|
||||
humanReq.setEmail(this.email?.value);
|
||||
humanReq.setPhone(this.phone?.value);
|
||||
humanReq.setGender(this.gender?.value);
|
||||
humanReq.setCountry(this.country?.value);
|
||||
|
||||
this.mgmtService
|
||||
.CreateUserHuman(this.userName?.value, humanReq)
|
||||
.then((data: UserResponse) => {
|
||||
.addHumanUser(humanReq)
|
||||
.then((data) => {
|
||||
this.loading = false;
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
this.router.navigate(['users', data.getId()]);
|
||||
this.router.navigate(['users', data.userId]);
|
||||
})
|
||||
.catch(error => {
|
||||
this.loading = false;
|
||||
@@ -161,25 +163,10 @@ export class UserCreateComponent implements OnDestroy {
|
||||
public get phone(): AbstractControl | null {
|
||||
return this.userForm.get('phone');
|
||||
}
|
||||
public get streetAddress(): AbstractControl | null {
|
||||
return this.userForm.get('streetAddress');
|
||||
}
|
||||
public get postalCode(): AbstractControl | null {
|
||||
return this.userForm.get('postalCode');
|
||||
}
|
||||
public get locality(): AbstractControl | null {
|
||||
return this.userForm.get('locality');
|
||||
}
|
||||
public get region(): AbstractControl | null {
|
||||
return this.userForm.get('region');
|
||||
}
|
||||
public get country(): AbstractControl | null {
|
||||
return this.userForm.get('country');
|
||||
}
|
||||
|
||||
private envSuffix(): string {
|
||||
if (this.userLoginMustBeDomain && this.primaryDomain?.domain) {
|
||||
return `@${this.primaryDomain.domain}`;
|
||||
if (this.userLoginMustBeDomain && this.primaryDomain?.domainName) {
|
||||
return `@${this.primaryDomain.domainName}`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
{{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
<i matTooltip="verified" *ngIf="mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
+31
-32
@@ -4,7 +4,7 @@ import { MatSort } from '@angular/material/sort';
|
||||
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { MFAState, WebAuthNResponse, WebAuthNToken } from 'src/app/proto/generated/auth_pb';
|
||||
import { AuthFactorState, WebAuthNToken } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -35,7 +35,7 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<WebAuthNToken.AsObject>;
|
||||
|
||||
public MFAState: any = MFAState;
|
||||
public AuthFactorState: any = AuthFactorState;
|
||||
public error: string = '';
|
||||
|
||||
constructor(private service: GrpcAuthService,
|
||||
@@ -51,45 +51,44 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public addPasswordless(): void {
|
||||
this.service.AddMyPasswordless().then((u2fresp) => {
|
||||
const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject();
|
||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string));
|
||||
this.service.addMyPasswordless().then((resp) => {
|
||||
if (resp.key) {
|
||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(resp.key.publicKey as string));
|
||||
|
||||
if (credOptions.publicKey?.challenge) {
|
||||
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
||||
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
|
||||
if (credOptions.publicKey.excludeCredentials) {
|
||||
credOptions.publicKey.excludeCredentials.map(cred => {
|
||||
cred.id = _base64ToArrayBuffer(cred.id as any);
|
||||
return cred;
|
||||
if (credOptions.publicKey?.challenge) {
|
||||
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
||||
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
|
||||
if (credOptions.publicKey.excludeCredentials) {
|
||||
credOptions.publicKey.excludeCredentials.map(cred => {
|
||||
cred.id = _base64ToArrayBuffer(cred.id as any);
|
||||
return cred;
|
||||
});
|
||||
}
|
||||
const dialogRef = this.dialog.open(DialogU2FComponent, {
|
||||
width: '400px',
|
||||
data: {
|
||||
credOptions,
|
||||
type: U2FComponentDestination.PASSWORDLESS,
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(done => {
|
||||
if (done) {
|
||||
this.getPasswordless();
|
||||
} else {
|
||||
this.getPasswordless();
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(credOptions);
|
||||
const dialogRef = this.dialog.open(DialogU2FComponent, {
|
||||
width: '400px',
|
||||
data: {
|
||||
credOptions,
|
||||
type: U2FComponentDestination.PASSWORDLESS,
|
||||
},
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(done => {
|
||||
if (done) {
|
||||
this.getPasswordless();
|
||||
} else {
|
||||
this.getPasswordless();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}, error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public getPasswordless(): void {
|
||||
this.service.GetMyPasswordless().then(passwordless => {
|
||||
this.dataSource = new MatTableDataSource(passwordless.toObject().tokensList);
|
||||
this.service.listMyPasswordless().then(passwordless => {
|
||||
this.dataSource = new MatTableDataSource(passwordless.resultList);
|
||||
this.dataSource.sort = this.sort;
|
||||
}).catch(error => {
|
||||
this.error = error.message;
|
||||
@@ -109,7 +108,7 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && id) {
|
||||
this.service.RemoveMyPasswordless(id).then(() => {
|
||||
this.service.removeMyPasswordless(id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true);
|
||||
this.getPasswordless();
|
||||
}).catch(error => {
|
||||
|
||||
+30
-38
@@ -4,15 +4,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import {
|
||||
Gender,
|
||||
UserAddress,
|
||||
UserEmail,
|
||||
UserPhone,
|
||||
UserProfile,
|
||||
UserState,
|
||||
UserView,
|
||||
} from 'src/app/proto/generated/auth_pb';
|
||||
import { Email, Gender, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -25,8 +17,7 @@ import { EditDialogComponent } from './edit-dialog/edit-dialog.component';
|
||||
styleUrls: ['./auth-user-detail.component.scss'],
|
||||
})
|
||||
export class AuthUserDetailComponent implements OnDestroy {
|
||||
public user!: UserView.AsObject;
|
||||
public address: UserAddress.AsObject = { id: '' } as any;
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
@@ -55,8 +46,10 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
|
||||
refreshUser(): void {
|
||||
this.refreshChanges$.emit();
|
||||
this.userService.GetMyUser().then(user => {
|
||||
this.user = user.toObject();
|
||||
this.userService.getMyUser().then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -68,26 +61,20 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
public saveProfile(profileData: UserProfile.AsObject): void {
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.firstName = profileData.firstName;
|
||||
this.user.human.lastName = profileData.lastName;
|
||||
this.user.human.nickName = profileData.nickName;
|
||||
this.user.human.displayName = profileData.displayName;
|
||||
this.user.human.gender = profileData.gender;
|
||||
this.user.human.preferredLanguage = profileData.preferredLanguage;
|
||||
this.user.human.profile = profileData;
|
||||
|
||||
this.userService
|
||||
.SaveMyUserProfile(
|
||||
this.user.human.firstName,
|
||||
this.user.human.lastName,
|
||||
this.user.human.nickName,
|
||||
this.user.human.preferredLanguage,
|
||||
this.user.human.gender,
|
||||
.updateMyProfile(
|
||||
this.user.human.profile?.firstName,
|
||||
this.user.human.profile?.lastName,
|
||||
this.user.human.profile?.nickName,
|
||||
this.user.human.profile?.preferredLanguage,
|
||||
this.user.human.profile?.gender,
|
||||
)
|
||||
.then((data: UserProfile) => {
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.user = Object.assign(this.user, data.toObject());
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -98,10 +85,12 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
|
||||
public saveEmail(email: string): void {
|
||||
this.userService
|
||||
.SaveMyUserEmail(email).then((data: UserEmail) => {
|
||||
.setMyPhone(email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.email = data.toObject().email;
|
||||
const mailToSet = new Email();
|
||||
mailToSet.setEmail(email);
|
||||
this.user.human.email = mailToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
@@ -110,7 +99,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public enteredPhoneCode(code: string): void {
|
||||
this.userService.VerifyMyUserPhone(code).then(() => {
|
||||
this.userService.verifyMyPhone(code).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
this.refreshUser();
|
||||
}).catch(error => {
|
||||
@@ -123,7 +112,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.userService.ResendPhoneVerification().then(() => {
|
||||
this.userService.resendMyPhoneVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
@@ -132,7 +121,7 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.userService.ResendMyEmailVerificationMail().then(() => {
|
||||
this.userService.resendMyEmailVerification().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
@@ -141,10 +130,11 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.userService.RemoveMyUserPhone().then(() => {
|
||||
this.userService.removeMyPhone().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = '';
|
||||
if (this.user.human?.phone) {
|
||||
const phone = new Phone();
|
||||
this.user.human.phone = phone.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
@@ -155,10 +145,12 @@ export class AuthUserDetailComponent implements OnDestroy {
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.human) {
|
||||
this.userService
|
||||
.SaveMyUserPhone(phone).then((data: UserPhone) => {
|
||||
.setMyPhone(phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = data.toObject().phone;
|
||||
const phoneToSet = new Phone();
|
||||
phoneToSet.setPhone(phone);
|
||||
this.user.human.phone = phoneToSet.toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
{{'USER.MFA.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
<i matTooltip="verified" *ngIf="mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
@@ -28,7 +28,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||
(click)="deleteMFA(mfa)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
+34
-32
@@ -4,7 +4,7 @@ import { MatSort } from '@angular/material/sort';
|
||||
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { MfaOtpResponse, MFAState, MfaType, MultiFactor, WebAuthNResponse } from 'src/app/proto/generated/auth_pb';
|
||||
import { AuthFactor, AuthFactorState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -32,19 +32,20 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ViewChild(MatTable) public table!: MatTable<MultiFactor.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<AuthFactor.AsObject>;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<MultiFactor.AsObject>;
|
||||
public dataSource!: MatTableDataSource<AuthFactor.AsObject>;
|
||||
|
||||
public MfaType: any = MfaType;
|
||||
public MFAState: any = MFAState;
|
||||
public AuthFactorState: any = AuthFactorState;
|
||||
|
||||
public error: string = '';
|
||||
public otpAvailable: boolean = false;
|
||||
|
||||
constructor(private service: GrpcAuthService,
|
||||
constructor(
|
||||
private service: GrpcAuthService,
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog) { }
|
||||
private dialog: MatDialog
|
||||
) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.getMFAs();
|
||||
@@ -55,8 +56,8 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public addOTP(): void {
|
||||
this.service.AddMfaOTP().then((otpresp) => {
|
||||
const otp: MfaOtpResponse.AsObject = otpresp.toObject();
|
||||
this.service.addMyMultiFactorOTP().then((otpresp) => {
|
||||
const otp = otpresp;
|
||||
const dialogRef = this.dialog.open(DialogOtpComponent, {
|
||||
data: otp.url,
|
||||
width: '400px',
|
||||
@@ -64,7 +65,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe((code) => {
|
||||
if (code) {
|
||||
this.service.VerifyMfaOTP(code).then(() => {
|
||||
this.service.verifyMyMultiFactorOTP(code).then(() => {
|
||||
this.getMFAs();
|
||||
});
|
||||
}
|
||||
@@ -75,9 +76,8 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public addU2F(): void {
|
||||
this.service.AddMyMfaU2F().then((u2fresp) => {
|
||||
const webauthn: WebAuthNResponse.AsObject = u2fresp.toObject();
|
||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(webauthn.publicKey as string));
|
||||
this.service.addMyMultiFactorU2F().then((u2fresp) => {
|
||||
const credOptions: CredentialCreationOptions = JSON.parse(atob(u2fresp.key?.publicKey as string));
|
||||
|
||||
if (credOptions.publicKey?.challenge) {
|
||||
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
|
||||
@@ -112,11 +112,12 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public getMFAs(): void {
|
||||
this.service.GetMyMfas().then(mfas => {
|
||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||
this.service.listMyMultiFactors().then(mfas => {
|
||||
const list = mfas.resultList;
|
||||
this.dataSource = new MatTableDataSource(list);
|
||||
this.dataSource.sort = this.sort;
|
||||
|
||||
const index = mfas.toObject().mfasList.findIndex(mfa => mfa.type === MfaType.MFATYPE_OTP);
|
||||
const index = list.findIndex(mfa => mfa.otp);
|
||||
if (index === -1) {
|
||||
this.otpAvailable = true;
|
||||
}
|
||||
@@ -125,7 +126,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
public deleteMFA(type: MfaType, id?: string): void {
|
||||
public deleteMFA(factor: AuthFactor.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@@ -138,11 +139,11 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
if (type === MfaType.MFATYPE_OTP) {
|
||||
this.service.RemoveMfaOTP().then(() => {
|
||||
if (factor.otp) {
|
||||
this.service.removeMyMultiFactorOTP().then(() => {
|
||||
this.toast.showInfo('USER.TOAST.OTPREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||
const index = this.dataSource.data.findIndex(mfa => !!mfa.otp);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
@@ -150,19 +151,20 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (type === MfaType.MFATYPE_U2F && id) {
|
||||
this.service.RemoveMyMfaU2F(id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
} else
|
||||
if (factor.u2f) {
|
||||
this.service.removeMyMultiFactorU2F(factor.u2f.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
this.getMFAs();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
const index = this.dataSource.data.findIndex(mfa => !!mfa.u2f);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
this.getMFAs();
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ export class DialogU2FComponent {
|
||||
|
||||
const base64 = btoa(data);
|
||||
if (this.type === U2FComponentDestination.MFA) {
|
||||
this.service.VerifyMyMfaU2F(base64, this.name).then(() => {
|
||||
this.service.verifyMyMultiFactorU2F(base64, this.name).then(() => {
|
||||
this.translate.get('USER.MFA.U2F_SUCCESS').pipe(take(1)).subscribe(msg => {
|
||||
this.toast.showInfo(msg);
|
||||
});
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
<div class="left">
|
||||
<span class="label">{{ 'USER.EMAIL' | translate }}</span>
|
||||
<span class="name">{{human?.email}}</span>
|
||||
<span *ngIf="human?.isEmailVerified" class="contact-state verified">{{'USER.EMAILVERIFIED' |
|
||||
<span *ngIf="human?.profile.email.isEmailVerified" class="contact-state verified">{{'USER.EMAILVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="!human?.isEmailVerified" class="block">
|
||||
<div *ngIf="!human?.profile.email.isEmailVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
|
||||
<ng-container *ngIf="human?.email">
|
||||
@@ -46,9 +46,9 @@
|
||||
<div class="left">
|
||||
<span class="label">{{ 'USER.PHONE' | translate }}</span>
|
||||
<span class="name">{{human?.phone ? human.phone : ('USER.PHONEEMPTY' | translate)}}</span>
|
||||
<span *ngIf="human?.isPhoneVerified" class="contact-state verified">{{'USER.PHONEVERIFIED' |
|
||||
<span *ngIf="human?.profile.phone.isPhoneVerified" class="contact-state verified">{{'USER.PHONEVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="!human?.isPhoneVerified" class="block">
|
||||
<div *ngIf="!human?.profile.phone.isPhoneVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
|
||||
<ng-container *ngIf="human?.phone">
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { HumanView as AuthHumanView, UserState as AuthUserState } from 'src/app/proto/generated/auth_pb';
|
||||
import { HumanView as MgmtHumanView, UserState as MgmtUserState } from 'src/app/proto/generated/management_pb';
|
||||
import { Human, UserState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
import { CodeDialogComponent } from '../auth-user-detail/code-dialog/code-dialog.component';
|
||||
import { EditDialogType } from '../user-detail/user-detail.component';
|
||||
@@ -15,8 +14,8 @@ import { EditDialogType } from '../user-detail/user-detail.component';
|
||||
export class ContactComponent {
|
||||
@Input() disablePhoneCode: boolean = false;
|
||||
@Input() canWrite: boolean = false;
|
||||
@Input() human!: AuthHumanView.AsObject | MgmtHumanView.AsObject;
|
||||
@Input() state!: AuthUserState | MgmtUserState;
|
||||
@Input() human!: Human.AsObject;
|
||||
@Input() state!: UserState;
|
||||
@Output() editType: EventEmitter<EditDialogType> = new EventEmitter();
|
||||
@Output() resendEmailVerification: EventEmitter<void> = new EventEmitter();
|
||||
@Output() resendPhoneVerification: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { UserView } from '../../../../proto/generated/management_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'app-detail-form-machine',
|
||||
@@ -11,7 +10,7 @@ import { UserView } from '../../../../proto/generated/management_pb';
|
||||
})
|
||||
export class DetailFormMachineComponent implements OnInit, OnDestroy {
|
||||
@Input() public username!: string;
|
||||
@Input() public user!: UserView;
|
||||
@Input() public user!: User;
|
||||
@Input() public disabled: boolean = false;
|
||||
@Output() public submitData: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Gender as authGender, UserProfile as authUP, UserView as authUV } from 'src/app/proto/generated/auth_pb';
|
||||
import { Gender as mgmtGender, UserProfile as mgmtUP, UserView as mgmtUV } from 'src/app/proto/generated/management_pb';
|
||||
import { Gender, User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -12,11 +11,11 @@ import { Gender as mgmtGender, UserProfile as mgmtUP, UserView as mgmtUV } from
|
||||
})
|
||||
export class DetailFormComponent implements OnDestroy, OnChanges {
|
||||
@Input() public username!: string;
|
||||
@Input() public user!: mgmtUV | authUV;
|
||||
@Input() public user!: User;
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public genders: mgmtGender[] | authGender[] = [];
|
||||
@Input() public genders: Gender[] = [];
|
||||
@Input() public languages: string[] = ['de', 'en'];
|
||||
@Output() public submitData: EventEmitter<mgmtUP | authUP> = new EventEmitter<mgmtUP | authUP>();
|
||||
@Output() public submitData: EventEmitter<User> = new EventEmitter<User>();
|
||||
@Output() public changedLanguage: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
public profileForm!: FormGroup;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[timestamp]="externalIdpResult?.viewTimestamp" [selection]="selection">
|
||||
[timestamp]="viewTimestamp" [selection]="selection">
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
@@ -52,7 +52,7 @@
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<mat-paginator #paginator class="paginator" [length]="externalIdpResult?.totalResult || 0" [pageSize]="10"
|
||||
<mat-paginator #paginator class="paginator" [length]="totalResult || 0" [pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { IDPUserLink } from 'src/app/proto/generated/zitadel/idp_pb';
|
||||
|
||||
import { ExternalIDPView as AuthExternalIDPView } from '../../../../proto/generated/auth_pb';
|
||||
import {
|
||||
ExternalIDPSearchResponse,
|
||||
ExternalIDPView as MgmtExternalIDPView,
|
||||
} from '../../../../proto/generated/management_pb';
|
||||
import { GrpcAuthService } from '../../../../services/grpc-auth.service';
|
||||
import { ManagementService } from '../../../../services/mgmt.service';
|
||||
import { ToastService } from '../../../../services/toast.service';
|
||||
@@ -24,11 +21,12 @@ export class ExternalIdpsComponent implements OnInit {
|
||||
@Input() service!: GrpcAuthService | ManagementService;
|
||||
@Input() userId!: string;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
public externalIdpResult!: ExternalIDPSearchResponse.AsObject;
|
||||
public dataSource: MatTableDataSource<MgmtExternalIDPView.AsObject | AuthExternalIDPView.AsObject>
|
||||
= new MatTableDataSource<MgmtExternalIDPView.AsObject | AuthExternalIDPView.AsObject>();
|
||||
public selection: SelectionModel<MgmtExternalIDPView.AsObject | AuthExternalIDPView.AsObject>
|
||||
= new SelectionModel<MgmtExternalIDPView.AsObject | AuthExternalIDPView.AsObject>(true, []);
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public dataSource: MatTableDataSource<IDPUserLink.AsObject>
|
||||
= new MatTableDataSource<IDPUserLink.AsObject>();
|
||||
public selection: SelectionModel<IDPUserLink.AsObject>
|
||||
= new SelectionModel<IDPUserLink.AsObject>(true, []);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@Input() public displayedColumns: string[] = ['idpConfigId', 'idpName', 'externalUserId', 'externalUserDisplayName', 'actions'];
|
||||
@@ -60,15 +58,20 @@ export class ExternalIdpsComponent implements OnInit {
|
||||
|
||||
let promise;
|
||||
if (this.service instanceof ManagementService) {
|
||||
promise = (this.service as ManagementService).SearchUserExternalIDPs(limit, offset, this.userId);
|
||||
promise = (this.service as ManagementService).listHumanLinkedIDPs(this.userId, limit, offset);
|
||||
} else if (this.service instanceof GrpcAuthService) {
|
||||
promise = (this.service as GrpcAuthService).SearchMyExternalIdps(limit, offset);
|
||||
promise = (this.service as GrpcAuthService).listMyLinkedIDPs(limit, offset);
|
||||
}
|
||||
|
||||
if (promise) {
|
||||
promise.then(resp => {
|
||||
this.externalIdpResult = resp.toObject();
|
||||
this.dataSource.data = this.externalIdpResult.resultList;
|
||||
this.dataSource.data = resp.resultList;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details.viewTimestamp;
|
||||
}
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalResult = resp.details?.totalResult;
|
||||
}
|
||||
this.loadingSubject.next(false);
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
@@ -81,7 +84,7 @@ export class ExternalIdpsComponent implements OnInit {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||
}
|
||||
|
||||
public removeExternalIdp(idp: AuthExternalIDPView.AsObject | MgmtExternalIDPView.AsObject): void {
|
||||
public removeExternalIdp(idp: IDPUserLink.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.REMOVE',
|
||||
@@ -97,10 +100,10 @@ export class ExternalIdpsComponent implements OnInit {
|
||||
let promise;
|
||||
if (this.service instanceof ManagementService) {
|
||||
promise = (this.service as ManagementService)
|
||||
.RemoveExternalIDP(idp.externalUserId, idp.idpConfigId, idp.userId);
|
||||
.removeHumanLinkedIDP(idp.providedUserId, idp.idpId, idp.userId);
|
||||
} else if (this.service instanceof GrpcAuthService) {
|
||||
promise = (this.service as GrpcAuthService)
|
||||
.RemoveExternalIDP(idp.externalUserId, idp.idpConfigId);
|
||||
.removeMyLinkedIDP(idp.providedUserId, idp.idpId);
|
||||
}
|
||||
|
||||
if (promise) {
|
||||
|
||||
+10
-11
@@ -2,14 +2,14 @@ import { DataSource } from '@angular/cdk/collections';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||
import { catchError, finalize, map } from 'rxjs/operators';
|
||||
import { UserMembershipView } from 'src/app/proto/generated/management_pb';
|
||||
import { Membership } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
|
||||
export class MembershipDetailDataSource extends DataSource<UserMembershipView.AsObject> {
|
||||
export class MembershipDetailDataSource extends DataSource<Membership.AsObject> {
|
||||
public totalResult: number = 0;
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public membersSubject: BehaviorSubject<UserMembershipView.AsObject[]>
|
||||
= new BehaviorSubject<UserMembershipView.AsObject[]>([]);
|
||||
public membersSubject: BehaviorSubject<Membership.AsObject[]>
|
||||
= new BehaviorSubject<Membership.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@@ -21,14 +21,13 @@ export class MembershipDetailDataSource extends DataSource<UserMembershipView.As
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
from(this.mgmtUserService.SearchUserMemberships(userId, pageSize, offset)).pipe(
|
||||
from(this.mgmtUserService.listUserMemberships(userId, pageSize, offset)).pipe(
|
||||
map(resp => {
|
||||
const response = resp.toObject();
|
||||
this.totalResult = response.totalResult;
|
||||
if (response.viewTimestamp) {
|
||||
this.viewTimestamp = response.viewTimestamp;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details.viewTimestamp;
|
||||
}
|
||||
return response.resultList;
|
||||
return resp.resultList;
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => this.loadingSubject.next(false)),
|
||||
@@ -43,7 +42,7 @@ export class MembershipDetailDataSource extends DataSource<UserMembershipView.As
|
||||
* the returned stream emits new items.
|
||||
* @returns A stream of the items to be rendered.
|
||||
*/
|
||||
public connect(): Observable<UserMembershipView.AsObject[]> {
|
||||
public connect(): Observable<Membership.AsObject[]> {
|
||||
return this.membersSubject.asObservable();
|
||||
}
|
||||
|
||||
|
||||
+26
-24
@@ -6,7 +6,7 @@ import { MatTable } from '@angular/material/table';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { UserMembershipSearchResponse, UserMembershipView, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { Membership, User } from 'src/app/proto/generated/zitadel/user_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';
|
||||
@@ -19,13 +19,13 @@ import { MembershipDetailDataSource } from './membership-detail-datasource';
|
||||
styleUrls: ['./membership-detail.component.scss'],
|
||||
})
|
||||
export class MembershipDetailComponent implements AfterViewInit {
|
||||
public user!: UserView.AsObject;
|
||||
public user!: User.AsObject;
|
||||
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild(MatTable) public table!: MatTable<UserMembershipView.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<Membership.AsObject>;
|
||||
public dataSource!: MembershipDetailDataSource;
|
||||
public selection: SelectionModel<UserMembershipView.AsObject>
|
||||
= new SelectionModel<UserMembershipView.AsObject>(true, []);
|
||||
public selection: SelectionModel<Membership.AsObject>
|
||||
= new SelectionModel<Membership.AsObject>(true, []);
|
||||
|
||||
public memberRoleOptions: string[] = [];
|
||||
|
||||
@@ -33,7 +33,7 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
public displayedColumns: string[] = ['select', 'memberType', 'displayName', 'creationDate', 'changeDate', 'roles'];
|
||||
|
||||
public loading: boolean = false;
|
||||
public memberships!: UserMembershipSearchResponse.AsObject;
|
||||
public memberships!: Membership.AsObject[];
|
||||
|
||||
constructor(
|
||||
activatedRoute: ActivatedRoute,
|
||||
@@ -45,14 +45,16 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
activatedRoute.params.subscribe(data => {
|
||||
const { id } = data;
|
||||
if (id) {
|
||||
this.mgmtService.GetUserByID(id).then(user => {
|
||||
this.user = user.toObject();
|
||||
this.dataSource = new MembershipDetailDataSource(this.mgmtService);
|
||||
this.dataSource.loadMemberships(
|
||||
this.user.id,
|
||||
0,
|
||||
50,
|
||||
);
|
||||
this.mgmtService.getUserByID(id).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
this.dataSource = new MembershipDetailDataSource(this.mgmtService);
|
||||
this.dataSource.loadMemberships(
|
||||
this.user.id,
|
||||
0,
|
||||
50,
|
||||
);
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
@@ -117,19 +119,19 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
public async loadManager(userId: string): Promise<void> {
|
||||
this.mgmtService.SearchUserMemberships(userId, 100, 0, []).then(response => {
|
||||
this.memberships = response.toObject();
|
||||
this.mgmtService.listUserMemberships(userId, 100, 0, []).then(response => {
|
||||
this.memberships = response.resultList;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public createIamMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -142,12 +144,12 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
private createOrgMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -160,12 +162,12 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
private createGrantedProjectMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectGrantMember(
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
response.projectId,
|
||||
response.grantId,
|
||||
user.id,
|
||||
@@ -183,12 +185,12 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
private createOwnedProjectMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
|
||||
return this.mgmtService.addProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<div class="membership-groups">
|
||||
<span class="me-header">{{ 'USER.MEMBERSHIPS.TITLE' | translate }}</span>
|
||||
<div class="people" *ngIf="memberships">
|
||||
<div class="img-list" [@cardAnimation]="memberships.totalResult">
|
||||
<div class="img-list" [@cardAnimation]="totalResult">
|
||||
<mat-spinner class="spinner" diameter="20" *ngIf="loading"></mat-spinner>
|
||||
<ng-container *ngIf="memberships.totalResult < 8; else compact">
|
||||
<ng-container *ngFor="let membership of memberships.resultList; index as i">
|
||||
<ng-container *ngIf="totalResult < 8; else compact">
|
||||
<ng-container *ngFor="let membership of memberships; index as i">
|
||||
<div @animate class="avatar-circle" (click)="navigateToObject()"
|
||||
matTooltip="{{ membership.displayName }} | {{membership.rolesList?.join(' ')}}"
|
||||
[ngStyle]="{'z-index': 100 - i}">
|
||||
<div class="membership-avatar"
|
||||
[ngStyle]="{'background-color': getColor(membership.memberType)}">
|
||||
<i *ngIf="membership.memberType == MemberType.MEMBERTYPE_ORGANISATION"
|
||||
class="las la-archway"></i>
|
||||
<i *ngIf="membership.memberType == MemberType.MEMBERTYPE_PROJECT"
|
||||
class="icon las la-layer-group"></i>
|
||||
<i *ngIf="membership.memberType == MemberType.MEMBERTYPE_PROJECT_GRANT"
|
||||
class="icon las la-layer-group"></i>
|
||||
<div class="membership-avatar" [ngStyle]="{'background-color': getColor(membership)}">
|
||||
<i *ngIf="membership.orgId" class="las la-archway"></i>
|
||||
<i *ngIf="membership.projectId && !membership.grantId" class="icon las la-layer-group"></i>
|
||||
<i *ngIf="membership.projectId && membership.grantId" class="icon las la-layer-group"></i>
|
||||
|
||||
<span>{{membership.displayName}}</span>
|
||||
</div>
|
||||
@@ -25,7 +21,7 @@
|
||||
<ng-template #compact>
|
||||
<div class="avatar-circle" matTooltip="Click to show detail" (click)="navigateToObject()" role="button">
|
||||
<div class="membership-avatar">
|
||||
<span style="font-size: 16px;">{{memberships.totalResult}}</span>
|
||||
<span style="font-size: 16px;">{{totalResult}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||
import { AuthServiceClient } from 'src/app/proto/generated/auth_grpc_web_pb';
|
||||
import { MemberType, UserMembershipSearchResponse, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { UserGrant } from 'src/app/proto/generated/zitadel/auth_pb';
|
||||
import { Membership, User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@@ -33,14 +33,13 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
})
|
||||
export class MembershipsComponent implements OnInit {
|
||||
public loading: boolean = false;
|
||||
public memberships!: UserMembershipSearchResponse.AsObject;
|
||||
public memberships!: Membership.AsObject[] | UserGrant.AsObject[];
|
||||
public totalResult: number = 0;
|
||||
|
||||
@Input() public auth: boolean = false;
|
||||
@Input() public user!: UserView.AsObject;
|
||||
@Input() public user!: User.AsObject;
|
||||
@Input() public disabled: boolean = false;
|
||||
|
||||
public MemberType: any = MemberType;
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private mgmtService: ManagementService,
|
||||
@@ -56,13 +55,14 @@ export class MembershipsComponent implements OnInit {
|
||||
|
||||
public async loadManager(userId: string): Promise<void> {
|
||||
if (this.auth) {
|
||||
this.authService.SearchUserMemberships(100, 0, []).then(response => {
|
||||
this.memberships = response.toObject();
|
||||
this.authService.listMyUserGrants(100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.totalResult = resp.details?.totalResult || 0;
|
||||
this.loading = false;
|
||||
});
|
||||
} else {
|
||||
this.mgmtService.SearchUserMemberships(userId, 100, 0, []).then(response => {
|
||||
this.memberships = response.toObject();
|
||||
this.mgmtService.listUserMemberships(userId, 100, 0, []).then(resp => {
|
||||
this.memberships = resp.resultList;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
@@ -103,12 +103,12 @@ export class MembershipsComponent implements OnInit {
|
||||
}
|
||||
|
||||
public createIamMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
return this.adminService.addIAMMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -121,12 +121,12 @@ export class MembershipsComponent implements OnInit {
|
||||
}
|
||||
|
||||
private createOrgMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
return this.mgmtService.addOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -139,12 +139,12 @@ export class MembershipsComponent implements OnInit {
|
||||
}
|
||||
|
||||
private createGrantedProjectMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectGrantMember(
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
response.projectId,
|
||||
response.grantId,
|
||||
user.id,
|
||||
@@ -162,12 +162,12 @@ export class MembershipsComponent implements OnInit {
|
||||
}
|
||||
|
||||
private createOwnedProjectMember(response: any): void {
|
||||
const users: UserView.AsObject[] = response.users;
|
||||
const users: User.AsObject[] = response.users;
|
||||
const roles: string[] = response.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
users.forEach(user => {
|
||||
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
|
||||
return this.mgmtService.addProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
@@ -180,7 +180,7 @@ export class MembershipsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
getColor(type: MemberType): string {
|
||||
getColor(type: Membership.AsObject[] | UserGrant.AsObject[]): string {
|
||||
const gen = type.toString();
|
||||
const colors = [
|
||||
'rgb(201, 115, 88)',
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/fo
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/auth_pb';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -50,8 +50,10 @@ export class PasswordComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
const validators: Validators[] = [Validators.required];
|
||||
this.authService.GetMyPasswordComplexityPolicy().then(complexity => {
|
||||
this.policy = complexity.toObject();
|
||||
this.authService.getMyPasswordComplexityPolicy().then(resp => {
|
||||
if (resp.policy) {
|
||||
this.policy = resp.policy;
|
||||
}
|
||||
if (this.policy.minLength) {
|
||||
validators.push(Validators.minLength(this.policy.minLength));
|
||||
}
|
||||
@@ -96,7 +98,7 @@ export class PasswordComponent implements OnDestroy {
|
||||
|
||||
public setInitialPassword(userId: string): void {
|
||||
if (this.passwordForm.valid && this.password && this.password.value) {
|
||||
this.mgmtUserService.SetInitialPassword(userId, this.password.value).then((data: any) => {
|
||||
this.mgmtUserService.setHumanInitialPassword(userId, this.password.value).then((data: any) => {
|
||||
this.toast.showInfo('USER.TOAST.INITIALPASSWORDSET', true);
|
||||
window.history.back();
|
||||
}).catch(error => {
|
||||
@@ -109,7 +111,7 @@ export class PasswordComponent implements OnDestroy {
|
||||
if (this.passwordForm.valid && this.currentPassword &&
|
||||
this.currentPassword.value &&
|
||||
this.newPassword && this.newPassword.value && this.newPassword.valid) {
|
||||
this.authService.ChangeMyPassword(this.currentPassword.value, this.newPassword.value)
|
||||
this.authService.updateMyPassword(this.currentPassword.value, this.newPassword.value)
|
||||
.then((data: any) => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDCHANGED', true);
|
||||
window.history.back();
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.PASSWORDLESS.TABLESTATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||
{{'USER.PASSWORDLESS.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
<i matTooltip="verified" *ngIf="mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
+6
-7
@@ -4,8 +4,7 @@ import { MatSort } from '@angular/material/sort';
|
||||
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { MFAState, WebAuthNToken } from 'src/app/proto/generated/auth_pb';
|
||||
import { UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { AuthFactorState, User, WebAuthNToken } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -25,7 +24,7 @@ export interface WebAuthNOptions {
|
||||
styleUrls: ['./passwordless.component.scss'],
|
||||
})
|
||||
export class PasswordlessComponent implements OnInit, OnDestroy {
|
||||
@Input() private user!: UserView.AsObject;
|
||||
@Input() private user!: User.AsObject;
|
||||
public displayedColumns: string[] = ['name', 'state', 'actions'];
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@@ -34,7 +33,7 @@ export class PasswordlessComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<WebAuthNToken.AsObject>;
|
||||
|
||||
public MFAState: any = MFAState;
|
||||
public AuthFactorState: any = AuthFactorState;
|
||||
public error: string = '';
|
||||
|
||||
constructor(private service: ManagementService,
|
||||
@@ -50,8 +49,8 @@ export class PasswordlessComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public getPasswordless(): void {
|
||||
this.service.GetPasswordless(this.user.id).then(passwordless => {
|
||||
this.dataSource = new MatTableDataSource(passwordless.toObject().tokensList);
|
||||
this.service.listHumanPasswordless(this.user.id).then(passwordless => {
|
||||
this.dataSource = new MatTableDataSource(passwordless.resultList);
|
||||
this.dataSource.sort = this.sort;
|
||||
}).catch(error => {
|
||||
this.error = error.message;
|
||||
@@ -71,7 +70,7 @@ export class PasswordlessComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp && id) {
|
||||
this.service.RemovePasswordless(id, this.user.id).then(() => {
|
||||
this.service.removeHumanPasswordless(id, this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDLESSREMOVED', true);
|
||||
this.getPasswordless();
|
||||
}).catch(error => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<a (click)="navigateBack()" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<h1>{{user.human ? user.human?.displayName : user.machine?.name}}</h1>
|
||||
<h1>{{user.human ? user.human?.profile?.displayName : user.machine?.name}}</h1>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.delete$', 'user.delete:'+user?.id]">
|
||||
<button mat-icon-button color="warn" matTooltip="{{'USER.PAGES.DELETE' | translate}}"
|
||||
|
||||
@@ -7,17 +7,8 @@ import { take } from 'rxjs/operators';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import {
|
||||
Gender,
|
||||
MachineResponse,
|
||||
MachineView,
|
||||
NotificationType,
|
||||
UserEmail,
|
||||
UserPhone,
|
||||
UserProfile,
|
||||
UserState,
|
||||
UserView,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { SendHumanResetPasswordNotificationRequest } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Email, Gender, Machine, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -35,7 +26,7 @@ export enum EditDialogType {
|
||||
styleUrls: ['./user-detail.component.scss'],
|
||||
})
|
||||
export class UserDetailComponent implements OnInit {
|
||||
public user!: UserView.AsObject;
|
||||
public user!: User.AsObject;
|
||||
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
|
||||
@@ -63,8 +54,10 @@ export class UserDetailComponent implements OnInit {
|
||||
this.refreshChanges$.emit();
|
||||
this.route.params.pipe(take(1)).subscribe(params => {
|
||||
const { id } = params;
|
||||
this.mgmtUserService.GetUserByID(id).then(user => {
|
||||
this.user = user.toObject();
|
||||
this.mgmtUserService.getUserByID(id).then(resp => {
|
||||
if (resp.user) {
|
||||
this.user = resp.user;
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
@@ -76,15 +69,15 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
public changeState(newState: UserState): void {
|
||||
if (newState === UserState.USERSTATE_ACTIVE) {
|
||||
this.mgmtUserService.ReactivateUser(this.user.id).then(() => {
|
||||
if (newState === UserState.USER_STATE_ACTIVE) {
|
||||
this.mgmtUserService.reactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.REACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (newState === UserState.USERSTATE_INACTIVE) {
|
||||
this.mgmtUserService.DeactivateUser(this.user.id).then(() => {
|
||||
} else if (newState === UserState.USER_STATE_INACTIVE) {
|
||||
this.mgmtUserService.deactivateUser(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.DEACTIVATED', true);
|
||||
this.user.state = newState;
|
||||
}).catch(error => {
|
||||
@@ -93,25 +86,19 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public saveProfile(profileData: UserProfile.AsObject): void {
|
||||
public saveProfile(profileData: Profile.AsObject): void {
|
||||
if (this.user.human) {
|
||||
this.user.human.firstName = profileData.firstName;
|
||||
this.user.human.lastName = profileData.lastName;
|
||||
this.user.human.nickName = profileData.nickName;
|
||||
this.user.human.displayName = profileData.displayName;
|
||||
this.user.human.gender = profileData.gender;
|
||||
this.user.human.preferredLanguage = profileData.preferredLanguage;
|
||||
this.user.human.profile = profileData;
|
||||
this.mgmtUserService
|
||||
.SaveUserProfile(
|
||||
.updateHumanProfile(
|
||||
this.user.id,
|
||||
this.user.human.firstName,
|
||||
this.user.human.lastName,
|
||||
this.user.human.nickName,
|
||||
this.user.human.preferredLanguage,
|
||||
this.user.human.gender)
|
||||
.then((data: UserProfile) => {
|
||||
this.user.human.profile.firstName,
|
||||
this.user.human.profile.lastName,
|
||||
this.user.human.profile.nickName,
|
||||
this.user.human.profile.preferredLanguage,
|
||||
this.user.human.profile.gender)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.user = Object.assign(this.user, data.toObject());
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -120,18 +107,17 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public saveMachine(machineData: MachineView.AsObject): void {
|
||||
public saveMachine(machineData: Machine.AsObject): void {
|
||||
if (this.user.machine) {
|
||||
this.user.machine.name = machineData.name;
|
||||
this.user.machine.description = machineData.description;
|
||||
|
||||
this.mgmtUserService
|
||||
.UpdateUserMachine(
|
||||
.updateMachine(
|
||||
this.user.id,
|
||||
this.user.machine.description)
|
||||
.then((data: MachineResponse) => {
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SAVED', true);
|
||||
this.user = Object.assign(this.user, data.toObject());
|
||||
this.refreshChanges$.emit();
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -141,7 +127,7 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
public resendEmailVerification(): void {
|
||||
this.mgmtUserService.ResendEmailVerification(this.user.id).then(() => {
|
||||
this.mgmtUserService.resendHumanEmailVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
@@ -150,7 +136,7 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
public resendPhoneVerification(): void {
|
||||
this.mgmtUserService.ResendPhoneVerification(this.user.id).then(() => {
|
||||
this.mgmtUserService.resendHumanPhoneVerification(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
@@ -159,10 +145,10 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
public deletePhone(): void {
|
||||
this.mgmtUserService.RemoveUserPhone(this.user.id).then(() => {
|
||||
this.mgmtUserService.removeHumanPhone(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = '';
|
||||
this.user.human.phone = new Phone().setPhone('').toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
@@ -172,10 +158,10 @@ export class UserDetailComponent implements OnInit {
|
||||
|
||||
public saveEmail(email: string): void {
|
||||
if (this.user.id && email) {
|
||||
this.mgmtUserService.SaveUserEmail(this.user.id, email).then((data: UserEmail) => {
|
||||
this.mgmtUserService.updateHumanEmail(this.user.id, email).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.email = data.toObject().email;
|
||||
this.user.human.email = new Email().setEmail(email).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
@@ -187,10 +173,10 @@ export class UserDetailComponent implements OnInit {
|
||||
public savePhone(phone: string): void {
|
||||
if (this.user.id && phone) {
|
||||
this.mgmtUserService
|
||||
.SaveUserPhone(this.user.id, phone).then((data: UserPhone) => {
|
||||
.updateHumanPhone(this.user.id, phone).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PHONESAVED', true);
|
||||
if (this.user.human) {
|
||||
this.user.human.phone = data.toObject().phone;
|
||||
this.user.human.phone = new Phone().setPhone(phone).toObject();
|
||||
this.refreshUser();
|
||||
}
|
||||
}).catch(error => {
|
||||
@@ -204,7 +190,7 @@ export class UserDetailComponent implements OnInit {
|
||||
}
|
||||
|
||||
public sendSetPasswordNotification(): void {
|
||||
this.mgmtUserService.SendSetPasswordNotification(this.user.id, NotificationType.NOTIFICATIONTYPE_EMAIL)
|
||||
this.mgmtUserService.sendHumanResetPasswordNotification(this.user.id, SendHumanResetPasswordNotificationRequest.Type.TYPE_EMAIL)
|
||||
.then(() => {
|
||||
this.toast.showInfo('USER.TOAST.PASSWORDNOTIFICATIONSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
@@ -226,7 +212,7 @@ export class UserDetailComponent implements OnInit {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtUserService.DeleteUser(this.user.id).then(() => {
|
||||
this.mgmtUserService.removeUser(this.user.id).then(() => {
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
};
|
||||
@@ -246,7 +232,7 @@ export class UserDetailComponent implements OnInit {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp.send && this.user.id) {
|
||||
this.mgmtUserService.ResendInitialMail(this.user.id, resp.email ?? '').then(() => {
|
||||
this.mgmtUserService.resendHumanInitialization(this.user.id, resp.email ?? '').then(() => {
|
||||
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
|
||||
this.refreshChanges$.emit();
|
||||
}).catch(error => {
|
||||
|
||||
+1
-3
@@ -19,8 +19,6 @@
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<span class="centered">
|
||||
{{'USER.MFA.STATE.'+ mfa.state | translate}}
|
||||
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||
class="verified las la-check-circle"></i>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -29,7 +27,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let mfa">
|
||||
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||
(click)="deleteMFA(mfa.type, mfa.id)">
|
||||
(click)="deleteMFA(mfa)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
+15
-16
@@ -4,7 +4,7 @@ import { MatSort } from '@angular/material/sort';
|
||||
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { MFAState, MfaType, UserMultiFactor, UserView } from 'src/app/proto/generated/management_pb';
|
||||
import { AuthFactor, AuthFactorState, User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@@ -21,17 +21,16 @@ export interface MFAItem {
|
||||
})
|
||||
export class UserMfaComponent implements OnInit, OnDestroy {
|
||||
public displayedColumns: string[] = ['type', 'attr', 'state', 'actions'];
|
||||
@Input() private user!: UserView.AsObject;
|
||||
public mfaSubject: BehaviorSubject<UserMultiFactor.AsObject[]> = new BehaviorSubject<UserMultiFactor.AsObject[]>([]);
|
||||
@Input() private user!: User.AsObject;
|
||||
public mfaSubject: BehaviorSubject<AuthFactor.AsObject[]> = new BehaviorSubject<AuthFactor.AsObject[]>([]);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
@ViewChild(MatTable) public table!: MatTable<UserMultiFactor.AsObject>;
|
||||
@ViewChild(MatTable) public table!: MatTable<AuthFactor.AsObject>;
|
||||
@ViewChild(MatSort) public sort!: MatSort;
|
||||
public dataSource!: MatTableDataSource<UserMultiFactor.AsObject>;
|
||||
public dataSource!: MatTableDataSource<AuthFactor.AsObject>;
|
||||
|
||||
public MfaType: any = MfaType;
|
||||
public MFAState: any = MFAState;
|
||||
public AuthFactorState: any = AuthFactorState;
|
||||
|
||||
public error: string = '';
|
||||
constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { }
|
||||
@@ -46,15 +45,15 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public getMFAs(): void {
|
||||
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
||||
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||
this.mgmtUserService.listHumanMultiFactors(this.user.id).then(mfas => {
|
||||
this.dataSource = new MatTableDataSource(mfas.resultList);
|
||||
this.dataSource.sort = this.sort;
|
||||
}).catch(error => {
|
||||
this.error = error.message;
|
||||
});
|
||||
}
|
||||
|
||||
public deleteMFA(type: MfaType, id?: string): void {
|
||||
public deleteMFA(factor: AuthFactor.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@@ -67,11 +66,11 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
if (type === MfaType.MFATYPE_OTP) {
|
||||
this.mgmtUserService.removeMfaOTP(this.user.id).then(() => {
|
||||
if (factor.otp) {
|
||||
this.mgmtUserService.removeHumanMultiFactorOTP(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.OTPREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||
const index = this.dataSource.data.findIndex(mfa => !!mfa.otp);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
@@ -79,11 +78,11 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (type === MfaType.MFATYPE_U2F && id) {
|
||||
this.mgmtUserService.RemoveMfaU2F(this.user.id, id).then(() => {
|
||||
} else if (factor.u2f) {
|
||||
this.mgmtUserService.removeHumanAuthFactorU2F(this.user.id).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.U2FREMOVED', true);
|
||||
|
||||
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||
const index = this.dataSource.data.findIndex(mfa => !!mfa.u2f);
|
||||
if (index > -1) {
|
||||
this.dataSource.data.splice(index, 1);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { Type } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
import { UserListComponent, UserType } from './user-list.component';
|
||||
import { UserListComponent } from './user-list.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -10,7 +11,7 @@ const routes: Routes = [
|
||||
component: UserListComponent,
|
||||
data: {
|
||||
animation: 'HomePage',
|
||||
type: UserType.HUMAN,
|
||||
type: Type.TYPE_HUMAN,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -18,7 +19,7 @@ const routes: Routes = [
|
||||
component: UserListComponent,
|
||||
data: {
|
||||
animation: 'HomePage',
|
||||
type: UserType.MACHINE,
|
||||
type: Type.TYPE_MACHINE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<div class="max-width-container" [ngSwitch]="type">
|
||||
<ng-container *ngSwitchCase="UserType.HUMAN">
|
||||
<ng-container *ngSwitchCase="Type.TYPE_HUMAN">
|
||||
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
||||
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<app-user-table [userType]="UserType.HUMAN" [disabled]="(['user.write$'] | hasRole | async) == false">
|
||||
<app-user-table [type]="Type.TYPE_HUMAN" [disabled]="(['user.write$'] | hasRole | async) == false">
|
||||
</app-user-table>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="UserType.MACHINE">
|
||||
<ng-container *ngSwitchCase="Type.TYPE_MACHINE">
|
||||
<h1>{{ 'USER.PAGES.LISTMACHINE' | translate }}</h1>
|
||||
<p class="sub">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p>
|
||||
|
||||
<app-user-table [userType]="UserType.MACHINE"
|
||||
<app-user-table [type]="Type.TYPE_MACHINE"
|
||||
[displayedColumns]="['select','name', 'username', 'description','state', 'actions']"
|
||||
[disabled]="(['user.write$'] | hasRole | async) == false">
|
||||
</app-user-table>
|
||||
|
||||
@@ -2,19 +2,17 @@ import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { Type } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
|
||||
export enum UserType {
|
||||
HUMAN = 'human',
|
||||
MACHINE = 'machine',
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-user-list',
|
||||
templateUrl: './user-list.component.html',
|
||||
styleUrls: ['./user-list.component.scss'],
|
||||
})
|
||||
export class UserListComponent {
|
||||
public UserType: any = UserType;
|
||||
public type: UserType = UserType.HUMAN;
|
||||
public Type: any = Type;
|
||||
public type: Type = Type.TYPE_HUMAN;
|
||||
|
||||
constructor(public translate: TranslateService, activatedRoute: ActivatedRoute) {
|
||||
activatedRoute.data.pipe(take(1)).subscribe(params => {
|
||||
const { type } = params;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="userResult?.totalResult"
|
||||
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="totalResult"
|
||||
[timestamp]="viewTimestamp" [selection]="selection" [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
|
||||
<cnsl-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
|
||||
<input cnslInput (keyup)="applyFilter($event)"
|
||||
[placeholder]="('USER.TABLE.FILTER.' + userSearchKey.toString()) | translate" #input>
|
||||
@@ -15,7 +14,7 @@
|
||||
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||
<mat-icon svgIcon="mdi_account_check_outline"></mat-icon>
|
||||
</button>
|
||||
<a [routerLink]="[ '/users',userType == UserType.HUMAN ? 'create' : 'create-machine']" color="primary"
|
||||
<a [routerLink]="[ '/users',type == Type.TYPE_HUMAN ? 'create' : 'create-machine']" color="primary"
|
||||
mat-raised-button [disabled]="disabled">
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
@@ -34,8 +33,8 @@
|
||||
<mat-checkbox [disabled]="disabled" color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(user) : null" [checked]="selection.isSelected(user)">
|
||||
<app-avatar
|
||||
*ngIf="user[userType] && user[userType].displayName && user[userType]?.firstName && user[userType]?.lastName; else cog"
|
||||
class="avatar" [name]="user[userType].displayName" [size]="32">
|
||||
*ngIf="user[type] && user[type].displayName && user[type]?.firstName && user[type]?.lastName; else cog"
|
||||
class="avatar" [name]="user[type].displayName" [size]="32">
|
||||
</app-avatar>
|
||||
<ng-template #cog>
|
||||
<div class="sa-icon">
|
||||
@@ -48,35 +47,35 @@
|
||||
|
||||
<ng-container matColumnDef="firstname">
|
||||
<th mat-header-cell *matHeaderCellDef
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserSearchKey.USERSEARCHKEY_FIRST_NAME}">
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.FIRST_NAME}">
|
||||
{{ 'USER.PROFILE.FIRSTNAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_FIRST_NAME}"></template>
|
||||
[ngTemplateOutletContext]="{key: UserListSearchKey.FIRST_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.firstName}} </td>
|
||||
{{user[type]?.firstName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="lastname">
|
||||
<th mat-header-cell *matHeaderCellDef
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserSearchKey.USERSEARCHKEY_LAST_NAME}">
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.LAST_NAME}">
|
||||
{{ 'USER.PROFILE.LASTNAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_LAST_NAME}"></template>
|
||||
[ngTemplateOutletContext]="{key: UserListSearchKey.LAST_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.lastName}} </td>
|
||||
{{user[type]?.lastName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayName">
|
||||
<th mat-header-cell *matHeaderCellDef
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserSearchKey.USERSEARCHKEY_DISPLAY_NAME}">
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.USERSEARCHKEY_DISPLAY_NAME}">
|
||||
{{ 'USER.PROFILE.DISPLAYNAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_DISPLAY_NAME}"></template>
|
||||
[ngTemplateOutletContext]="{key: UserListSearchKey.USERSEARCHKEY_DISPLAY_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.displayName}} </td>
|
||||
{{user[type]?.displayName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
@@ -84,21 +83,21 @@
|
||||
{{ 'USER.MACHINE.NAME' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.name}} </td>
|
||||
{{user[type]?.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.DESCRIPTION' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.description}} </td>
|
||||
{{user[type]?.description}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="username">
|
||||
<th mat-header-cell *matHeaderCellDef
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserSearchKey.USERSEARCHKEY_USER_NAME}">
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.USER_NAME}">
|
||||
{{ 'USER.PROFILE.USERNAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_USER_NAME}"></template>
|
||||
[ngTemplateOutletContext]="{key: UserListSearchKey.USER_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user.userName}} </td>
|
||||
@@ -106,20 +105,21 @@
|
||||
|
||||
<ng-container matColumnDef="email">
|
||||
<th mat-header-cell *matHeaderCellDef
|
||||
[ngClass]="{'search-active': this.userSearchKey == UserSearchKey.USERSEARCHKEY_EMAIL}">
|
||||
[ngClass]="{'search-active': this.UserListSearchKey == UserListSearchKey.EMAIL}">
|
||||
{{ 'USER.EMAIL' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_EMAIL}"></template>
|
||||
[ngTemplateOutletContext]="{key: UserListSearchKey.EMAIL}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.email}} </td>
|
||||
{{user[type]?.email}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.DATA.STATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
<span class="state"
|
||||
[ngClass]="{'active': user.state === UserState.USERSTATE_ACTIVE, 'inactive': user.state === UserState.USERSTATE_INACTIVE}">{{ 'USER.DATA.STATE'+user.state | translate }}</span>
|
||||
[ngClass]="{'active': user.state === UserState.USERSTATE_ACTIVE, 'inactive': user.state === UserState.USERSTATE_INACTIVE}">{{
|
||||
'USER.DATA.STATE'+user.state | translate }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
<i class="las la-exclamation"></i>
|
||||
<span>{{'USER.TABLE.EMPTY' | translate}}</span>
|
||||
</div>
|
||||
<mat-paginator #paginator class="paginator" [length]="userResult?.totalResult || 0" [pageSize]="10"
|
||||
<mat-paginator #paginator class="paginator" [length]="totalResult || 0" [pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
|
||||
@@ -9,18 +9,30 @@ import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { UserView } from 'src/app/proto/generated/auth_pb';
|
||||
import { Timestamp } from 'src/app/proto/generated/google/protobuf/timestamp_pb';
|
||||
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
|
||||
import {
|
||||
SearchMethod,
|
||||
UserSearchKey,
|
||||
UserSearchQuery,
|
||||
UserSearchResponse,
|
||||
DisplayNameQuery,
|
||||
EmailQuery,
|
||||
FirstNameQuery,
|
||||
LastNameQuery,
|
||||
SearchQuery,
|
||||
Type,
|
||||
TypeQuery,
|
||||
User,
|
||||
UserNameQuery,
|
||||
UserState,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
} from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { UserType } from '../user-list.component';
|
||||
enum UserListSearchKey {
|
||||
FIRST_NAME,
|
||||
LAST_NAME,
|
||||
DISPLAY_NAME,
|
||||
USER_NAME,
|
||||
EMAIL,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-table',
|
||||
@@ -31,24 +43,26 @@ import { UserType } from '../user-list.component';
|
||||
],
|
||||
})
|
||||
export class UserTableComponent implements OnInit {
|
||||
public userSearchKey: UserSearchKey | undefined = undefined;
|
||||
public UserType: any = UserType;
|
||||
@Input() userType: UserType = UserType.HUMAN;
|
||||
public userSearchKey: UserListSearchKey | undefined = undefined;
|
||||
public Type: any = Type;
|
||||
@Input() type: Type = Type.TYPE_HUMAN;
|
||||
@Input() refreshOnPreviousRoutes: string[] = [];
|
||||
@Input() disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
@ViewChild('input') public filter!: Input;
|
||||
public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>();
|
||||
public selection: SelectionModel<UserView.AsObject> = new SelectionModel<UserView.AsObject>(true, []);
|
||||
public userResult!: UserSearchResponse.AsObject;
|
||||
|
||||
public viewTimestamp!: Timestamp.AsObject;
|
||||
public totalResult: number = 0;
|
||||
public dataSource: MatTableDataSource<User.AsObject> = new MatTableDataSource<User.AsObject>();
|
||||
public selection: SelectionModel<User.AsObject> = new SelectionModel<User.AsObject>(true, []);
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@Input() public displayedColumns: string[] = ['select', 'displayName', 'username', 'email', 'state', 'actions'];
|
||||
|
||||
@Output() public changedSelection: EventEmitter<Array<UserView.AsObject>> = new EventEmitter();
|
||||
UserSearchKey: any = UserSearchKey;
|
||||
@Output() public changedSelection: EventEmitter<Array<User.AsObject>> = new EventEmitter();
|
||||
|
||||
public UserState: any = UserState;
|
||||
public UserListSearchKey: any = UserListSearchKey;
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
@@ -64,10 +78,10 @@ export class UserTableComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.queryParams.pipe(take(1)).subscribe(params => {
|
||||
this.getData(10, 0, this.userType);
|
||||
this.getData(10, 0, this.type);
|
||||
if (params.deferredReload) {
|
||||
setTimeout(() => {
|
||||
this.getData(10, 0, this.userType);
|
||||
this.getData(10, 0, this.type);
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
@@ -87,12 +101,12 @@ export class UserTableComponent implements OnInit {
|
||||
|
||||
|
||||
public changePage(event: PageEvent): void {
|
||||
this.getData(event.pageSize, event.pageIndex * event.pageSize, this.userType);
|
||||
this.getData(event.pageSize, event.pageIndex * event.pageSize, this.type);
|
||||
}
|
||||
|
||||
public deactivateSelectedUsers(): void {
|
||||
Promise.all(this.selection.selected.map(value => {
|
||||
return this.userService.DeactivateUser(value.id);
|
||||
return this.userService.deactivateUser(value.id);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SELECTEDDEACTIVATED', true);
|
||||
this.selection.clear();
|
||||
@@ -106,7 +120,7 @@ export class UserTableComponent implements OnInit {
|
||||
|
||||
public reactivateSelectedUsers(): void {
|
||||
Promise.all(this.selection.selected.map(value => {
|
||||
return this.userService.ReactivateUser(value.id);
|
||||
return this.userService.reactivateUser(value.id);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('USER.TOAST.SELECTEDREACTIVATED', true);
|
||||
this.selection.clear();
|
||||
@@ -118,24 +132,61 @@ export class UserTableComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number, filterTypeValue: UserType, filterName?: string): Promise<void> {
|
||||
private async getData(limit: number, offset: number, type: Type, searchValue?: string): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
const query = new UserSearchQuery();
|
||||
query.setKey(UserSearchKey.USERSEARCHKEY_TYPE);
|
||||
query.setMethod(SearchMethod.SEARCHMETHOD_EQUALS);
|
||||
query.setValue(filterTypeValue);
|
||||
const query = new SearchQuery();
|
||||
const typeQuery = new TypeQuery();
|
||||
typeQuery.setType(type);
|
||||
query.setTypeQuery(typeQuery);
|
||||
|
||||
let namequery;
|
||||
if (filterName && this.userSearchKey !== undefined) {
|
||||
namequery = new UserSearchQuery();
|
||||
namequery.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE);
|
||||
namequery.setKey(this.userSearchKey);
|
||||
namequery.setValue(filterName.toLowerCase());
|
||||
if (searchValue && this.userSearchKey !== undefined) {
|
||||
switch (this.userSearchKey) {
|
||||
case UserListSearchKey.DISPLAY_NAME:
|
||||
const dNQuery = new DisplayNameQuery();
|
||||
dNQuery.setDisplayName(searchValue);
|
||||
dNQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
|
||||
query.setDisplayNameQuery(dNQuery);
|
||||
break;
|
||||
case UserListSearchKey.USER_NAME:
|
||||
const uNQuery = new UserNameQuery();
|
||||
uNQuery.setUserName(searchValue);
|
||||
uNQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
|
||||
query.setUserNameQuery(uNQuery);
|
||||
break;
|
||||
case UserListSearchKey.FIRST_NAME:
|
||||
const fNQuery = new FirstNameQuery();
|
||||
fNQuery.setFirstName(searchValue);
|
||||
fNQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
|
||||
query.setFirstNameQuery(fNQuery);
|
||||
break;
|
||||
case UserListSearchKey.FIRST_NAME:
|
||||
const lNQuery = new LastNameQuery();
|
||||
lNQuery.setLastName(searchValue);
|
||||
lNQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
|
||||
query.setLastNameQuery(lNQuery);
|
||||
break;
|
||||
case UserListSearchKey.EMAIL:
|
||||
const eQuery = new EmailQuery();
|
||||
eQuery.setEmailAddress(searchValue);
|
||||
eQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
|
||||
query.setEmailQuery(eQuery);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.userService.SearchUsers(limit, offset, namequery ? [query, namequery] : [query]).then(resp => {
|
||||
this.userResult = resp.toObject();
|
||||
this.dataSource.data = this.userResult.resultList;
|
||||
this.userService.listUsers(limit, offset, [query]).then(resp => {
|
||||
if (resp.details?.totalResult) {
|
||||
this.totalResult = resp.details?.totalResult;
|
||||
}
|
||||
if (resp.details?.viewTimestamp) {
|
||||
this.viewTimestamp = resp.details?.viewTimestamp;
|
||||
}
|
||||
this.dataSource.data = resp.resultList;
|
||||
this.loadingSubject.next(false);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
@@ -144,7 +195,7 @@ export class UserTableComponent implements OnInit {
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize, this.userType);
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize, this.type);
|
||||
}
|
||||
|
||||
public applyFilter(event: Event): void {
|
||||
@@ -154,12 +205,12 @@ export class UserTableComponent implements OnInit {
|
||||
this.getData(
|
||||
this.paginator.pageSize,
|
||||
this.paginator.pageIndex * this.paginator.pageSize,
|
||||
this.userType,
|
||||
this.type,
|
||||
filterValue,
|
||||
);
|
||||
}
|
||||
|
||||
public setFilter(key: UserSearchKey): void {
|
||||
public setFilter(key: UserListSearchKey): void {
|
||||
setTimeout(() => {
|
||||
if (this.filter) {
|
||||
(this.filter as any).nativeElement.focus();
|
||||
@@ -174,7 +225,7 @@ export class UserTableComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public deleteUser(user: UserView.AsObject): void {
|
||||
public deleteUser(user: User.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@@ -187,7 +238,7 @@ export class UserTableComponent implements OnInit {
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.userService.DeleteUser(user.id).then(() => {
|
||||
this.userService.removeUser(user.id).then(() => {
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
|
||||
Reference in New Issue
Block a user