mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-05 08:22:04 +00:00
feat: Use V2 API's in Console (#9312)
# Which Problems Are Solved Solves #8976 # Additional Changes I have done some intensive refactorings and we are using the new @zitadel/client package for GRPC access. # Additional Context - Closes #8976 --------- Co-authored-by: Max Peintner <peintnerm@gmail.com>
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { GrpcService } from './grpc.service';
|
||||
|
||||
import {
|
||||
GetInstanceFeaturesRequest,
|
||||
GetInstanceFeaturesResponse,
|
||||
ResetInstanceFeaturesRequest,
|
||||
SetInstanceFeaturesRequest,
|
||||
SetInstanceFeaturesResponse,
|
||||
} from '../proto/generated/zitadel/feature/v2beta/instance_pb';
|
||||
import {
|
||||
GetOrganizationFeaturesRequest,
|
||||
GetOrganizationFeaturesResponse,
|
||||
} from '../proto/generated/zitadel/feature/v2beta/organization_pb';
|
||||
import { GetUserFeaturesRequest, GetUserFeaturesResponse } from '../proto/generated/zitadel/feature/v2beta/user_pb';
|
||||
import { GetSystemFeaturesRequest, GetSystemFeaturesResponse } from '../proto/generated/zitadel/feature/v2beta/system_pb';
|
||||
import {
|
||||
GetInstanceFeaturesRequest,
|
||||
GetInstanceFeaturesResponse,
|
||||
ResetInstanceFeaturesRequest,
|
||||
SetInstanceFeaturesRequest,
|
||||
SetInstanceFeaturesResponse,
|
||||
} from '../proto/generated/zitadel/feature/v2/instance_pb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { SortDirection } from '@angular/material/sort';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { BehaviorSubject, forkJoin, from, Observable, of, Subject } from 'rxjs';
|
||||
import { catchError, distinctUntilChanged, filter, finalize, map, switchMap, timeout, withLatestFrom } from 'rxjs/operators';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatestWith,
|
||||
defer,
|
||||
distinctUntilKeyChanged,
|
||||
EMPTY,
|
||||
forkJoin,
|
||||
mergeWith,
|
||||
NEVER,
|
||||
Observable,
|
||||
of,
|
||||
shareReplay,
|
||||
Subject,
|
||||
TimeoutError,
|
||||
} from 'rxjs';
|
||||
import { catchError, distinctUntilChanged, filter, finalize, map, startWith, switchMap, tap, timeout } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
AddMyAuthFactorOTPEmailRequest,
|
||||
@@ -110,41 +124,17 @@ const ORG_LIMIT = 10;
|
||||
})
|
||||
export class GrpcAuthService {
|
||||
private _activeOrgChanged: Subject<Org.AsObject | undefined> = new Subject();
|
||||
public user!: Observable<User.AsObject | undefined>;
|
||||
public userSubject: BehaviorSubject<User.AsObject | undefined> = new BehaviorSubject<User.AsObject | undefined>(undefined);
|
||||
public user: Observable<User.AsObject | undefined>;
|
||||
private triggerPermissionsRefresh: Subject<void> = new Subject();
|
||||
public zitadelPermissions$: Observable<string[]> = this.triggerPermissionsRefresh.pipe(
|
||||
switchMap(() =>
|
||||
from(this.listMyZitadelPermissions()).pipe(
|
||||
map((rolesResp) => rolesResp.resultList),
|
||||
filter((roles) => !!roles.length),
|
||||
catchError((_) => {
|
||||
return of([]);
|
||||
}),
|
||||
distinctUntilChanged((a, b) => {
|
||||
return JSON.stringify(a.sort()) === JSON.stringify(b.sort());
|
||||
}),
|
||||
finalize(() => {
|
||||
this.fetchedZitadelPermissions.next(true);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
public zitadelPermissions: Observable<string[]>;
|
||||
|
||||
public labelpolicy$!: Observable<LabelPolicy.AsObject>;
|
||||
public labelpolicy: BehaviorSubject<LabelPolicy.AsObject | undefined> = new BehaviorSubject<
|
||||
LabelPolicy.AsObject | undefined
|
||||
>(undefined);
|
||||
labelPolicyLoading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
|
||||
public privacypolicy$!: Observable<PrivacyPolicy.AsObject>;
|
||||
public privacypolicy$: Observable<PrivacyPolicy.AsObject>;
|
||||
public privacypolicy: BehaviorSubject<PrivacyPolicy.AsObject | undefined> = new BehaviorSubject<
|
||||
PrivacyPolicy.AsObject | undefined
|
||||
>(undefined);
|
||||
privacyPolicyLoading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
|
||||
public zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
|
||||
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
public cachedOrgs: BehaviorSubject<Org.AsObject[]> = new BehaviorSubject<Org.AsObject[]>([]);
|
||||
private cachedLabelPolicies: { [orgId: string]: LabelPolicy.AsObject } = {};
|
||||
@@ -155,74 +145,63 @@ export class GrpcAuthService {
|
||||
private oauthService: OAuthService,
|
||||
private storage: StorageService,
|
||||
) {
|
||||
this.zitadelPermissions$.subscribe(this.zitadelPermissions);
|
||||
|
||||
this.labelpolicy$ = this.activeOrgChanged.pipe(
|
||||
switchMap((org) => {
|
||||
this.labelPolicyLoading$.next(true);
|
||||
return from(this.getMyLabelPolicy(org ? org.id : ''));
|
||||
}),
|
||||
tap(() => this.labelPolicyLoading$.next(true)),
|
||||
switchMap((org) => this.getMyLabelPolicy(org ? org.id : '')),
|
||||
tap(() => this.labelPolicyLoading$.next(false)),
|
||||
finalize(() => this.labelPolicyLoading$.next(false)),
|
||||
filter((policy) => !!policy),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.labelpolicy$.subscribe({
|
||||
next: (policy) => {
|
||||
this.labelpolicy.next(policy);
|
||||
this.labelPolicyLoading$.next(false);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
this.labelPolicyLoading$.next(false);
|
||||
},
|
||||
});
|
||||
|
||||
this.privacypolicy$ = this.activeOrgChanged.pipe(
|
||||
switchMap((org) => {
|
||||
this.privacyPolicyLoading$.next(true);
|
||||
return from(this.getMyPrivacyPolicy(org ? org.id : ''));
|
||||
}),
|
||||
switchMap((org) => this.getMyPrivacyPolicy(org ? org.id : '')),
|
||||
filter((policy) => !!policy),
|
||||
catchError((err) => {
|
||||
console.error(err);
|
||||
return EMPTY;
|
||||
}),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.privacypolicy$.subscribe({
|
||||
next: (policy) => {
|
||||
this.privacypolicy.next(policy);
|
||||
this.privacyPolicyLoading$.next(false);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
this.privacyPolicyLoading$.next(false);
|
||||
},
|
||||
});
|
||||
|
||||
this.user = forkJoin([
|
||||
of(this.oauthService.getAccessToken()),
|
||||
defer(() => of(this.oauthService.getAccessToken())),
|
||||
this.oauthService.events.pipe(
|
||||
filter((e) => e.type === 'token_received'),
|
||||
timeout(this.oauthService.waitForTokenInMsec || 0),
|
||||
catchError((_) => of(null)), // timeout is not an error
|
||||
timeout(this.oauthService.waitForTokenInMsec ?? 0),
|
||||
catchError((err) => {
|
||||
if (err instanceof TimeoutError) {
|
||||
return of(null);
|
||||
}
|
||||
throw err;
|
||||
}), // timeout is not an error
|
||||
map((_) => this.oauthService.getAccessToken()),
|
||||
),
|
||||
]).pipe(
|
||||
filter((token) => (token ? true : false)),
|
||||
distinctUntilChanged(),
|
||||
switchMap(() => {
|
||||
return from(
|
||||
this.getMyUser().then((resp) => {
|
||||
return resp.user;
|
||||
}),
|
||||
);
|
||||
}),
|
||||
finalize(() => {
|
||||
this.loadPermissions();
|
||||
}),
|
||||
filter(([_, token]) => !!token),
|
||||
distinctUntilKeyChanged(1),
|
||||
switchMap(() => this.getMyUser().then((resp) => resp.user)),
|
||||
startWith(undefined),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.user.subscribe(this.userSubject);
|
||||
|
||||
this.activeOrgChanged.subscribe(() => {
|
||||
this.loadPermissions();
|
||||
});
|
||||
this.zitadelPermissions = this.user.pipe(
|
||||
combineLatestWith(this.activeOrgChanged),
|
||||
// ignore errors from observables
|
||||
catchError(() => of(true)),
|
||||
// make sure observable never completes
|
||||
mergeWith(NEVER),
|
||||
switchMap(() =>
|
||||
this.listMyZitadelPermissions()
|
||||
.then((resp) => resp.resultList)
|
||||
.catch(() => <string[]>[]),
|
||||
),
|
||||
filter((roles) => !!roles.length),
|
||||
distinctUntilChanged((a, b) => {
|
||||
return JSON.stringify(a.sort()) === JSON.stringify(b.sort());
|
||||
}),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
}
|
||||
|
||||
public listMyMetadata(
|
||||
@@ -309,7 +288,7 @@ export class GrpcAuthService {
|
||||
}
|
||||
|
||||
public get activeOrgChanged(): Observable<Org.AsObject | undefined> {
|
||||
return this._activeOrgChanged;
|
||||
return this._activeOrgChanged.asObservable();
|
||||
}
|
||||
|
||||
public setActiveOrg(org: Org.AsObject): void {
|
||||
@@ -328,18 +307,15 @@ export class GrpcAuthService {
|
||||
* @param roles roles of the user
|
||||
*/
|
||||
public isAllowed(roles: string[] | RegExp[], requiresAll: boolean = false): Observable<boolean> {
|
||||
if (roles && roles.length > 0) {
|
||||
return this.fetchedZitadelPermissions.pipe(
|
||||
withLatestFrom(this.zitadelPermissions),
|
||||
filter(([hL, p]) => {
|
||||
return hL === true && !!p.length;
|
||||
}),
|
||||
map(([_, zroles]) => this.hasRoles(zroles, roles, requiresAll)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
} else {
|
||||
if (!roles?.length) {
|
||||
return of(false);
|
||||
}
|
||||
|
||||
return this.zitadelPermissions.pipe(
|
||||
filter((permissions) => !!permissions.length),
|
||||
map((permissions) => this.hasRoles(permissions, roles, requiresAll)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,17 +329,14 @@ export class GrpcAuthService {
|
||||
mapper: (attr: any) => string[] | RegExp[],
|
||||
requiresAll: boolean = false,
|
||||
): Observable<T[]> {
|
||||
return this.fetchedZitadelPermissions.pipe(
|
||||
withLatestFrom(this.zitadelPermissions),
|
||||
filter(([hL, p]) => {
|
||||
return hL === true && !!p.length;
|
||||
}),
|
||||
map(([_, zroles]) => {
|
||||
return objects.filter((obj) => {
|
||||
return this.zitadelPermissions.pipe(
|
||||
filter((permissions) => !!permissions.length),
|
||||
map((permissions) =>
|
||||
objects.filter((obj) => {
|
||||
const roles = mapper(obj);
|
||||
return this.hasRoles(zroles, roles, requiresAll);
|
||||
});
|
||||
}),
|
||||
return this.hasRoles(permissions, roles, requiresAll);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -395,19 +368,6 @@ export class GrpcAuthService {
|
||||
.then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public loadMyUser(): void {
|
||||
from(this.getMyUser())
|
||||
.pipe(
|
||||
map((resp) => resp.user),
|
||||
catchError((_) => {
|
||||
return of(undefined);
|
||||
}),
|
||||
)
|
||||
.subscribe((user) => {
|
||||
this.userSubject.next(user);
|
||||
});
|
||||
}
|
||||
|
||||
public getMyUser(): Promise<GetMyUserResponse.AsObject> {
|
||||
return this.grpcService.auth.getMyUser(new GetMyUserRequest(), null).then((resp) => resp.toObject());
|
||||
}
|
||||
@@ -728,40 +688,39 @@ export class GrpcAuthService {
|
||||
public getMyLabelPolicy(orgIdForCache?: string): Promise<LabelPolicy.AsObject> {
|
||||
if (orgIdForCache && this.cachedLabelPolicies[orgIdForCache]) {
|
||||
return Promise.resolve(this.cachedLabelPolicies[orgIdForCache]);
|
||||
} else {
|
||||
return this.grpcService.auth
|
||||
.getMyLabelPolicy(new GetMyLabelPolicyRequest(), null)
|
||||
.then((resp) => resp.toObject())
|
||||
.then((resp) => {
|
||||
if (resp.policy) {
|
||||
if (orgIdForCache) {
|
||||
this.cachedLabelPolicies[orgIdForCache] = resp.policy;
|
||||
}
|
||||
return Promise.resolve(resp.policy);
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.grpcService.auth
|
||||
.getMyLabelPolicy(new GetMyLabelPolicyRequest(), null)
|
||||
.then((resp) => resp.toObject())
|
||||
.then((resp) => {
|
||||
if (!resp.policy) {
|
||||
return Promise.reject();
|
||||
}
|
||||
if (orgIdForCache) {
|
||||
this.cachedLabelPolicies[orgIdForCache] = resp.policy;
|
||||
}
|
||||
return resp.policy;
|
||||
});
|
||||
}
|
||||
|
||||
public getMyPrivacyPolicy(orgIdForCache?: string): Promise<PrivacyPolicy.AsObject> {
|
||||
if (orgIdForCache && this.cachedPrivacyPolicies[orgIdForCache]) {
|
||||
return Promise.resolve(this.cachedPrivacyPolicies[orgIdForCache]);
|
||||
} else {
|
||||
return this.grpcService.auth
|
||||
.getMyPrivacyPolicy(new GetMyPrivacyPolicyRequest(), null)
|
||||
.then((resp) => resp.toObject())
|
||||
.then((resp) => {
|
||||
if (resp.policy) {
|
||||
if (orgIdForCache) {
|
||||
this.cachedPrivacyPolicies[orgIdForCache] = resp.policy;
|
||||
}
|
||||
return Promise.resolve(resp.policy);
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.grpcService.auth
|
||||
.getMyPrivacyPolicy(new GetMyPrivacyPolicyRequest(), null)
|
||||
.then((resp) => resp.toObject())
|
||||
.then((resp) => {
|
||||
if (!resp.policy) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
if (orgIdForCache) {
|
||||
this.cachedPrivacyPolicies[orgIdForCache] = resp.policy;
|
||||
}
|
||||
return resp.policy;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { PlatformLocation } from '@angular/common';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { catchError, switchMap, tap, throwError } from 'rxjs';
|
||||
import { catchError, firstValueFrom, switchMap, tap } from 'rxjs';
|
||||
|
||||
import { AdminServiceClient } from '../proto/generated/zitadel/AdminServiceClientPb';
|
||||
import { AuthServiceClient } from '../proto/generated/zitadel/AuthServiceClientPb';
|
||||
@@ -12,13 +11,18 @@ import { fallbackLanguage, supportedLanguagesRegexp } from '../utils/language';
|
||||
import { AuthenticationService } from './authentication.service';
|
||||
import { EnvironmentService } from './environment.service';
|
||||
import { ExhaustedService } from './exhausted.service';
|
||||
import { AuthInterceptor } from './interceptors/auth.interceptor';
|
||||
import { AuthInterceptor, AuthInterceptorProvider, NewConnectWebAuthInterceptor } from './interceptors/auth.interceptor';
|
||||
import { ExhaustedGrpcInterceptor } from './interceptors/exhausted.grpc.interceptor';
|
||||
import { I18nInterceptor } from './interceptors/i18n.interceptor';
|
||||
import { OrgInterceptor } from './interceptors/org.interceptor';
|
||||
import { StorageService } from './storage.service';
|
||||
import { FeatureServiceClient } from '../proto/generated/zitadel/feature/v2beta/Feature_serviceServiceClientPb';
|
||||
import { GrpcAuthService } from './grpc-auth.service';
|
||||
import { UserServiceClient } from '../proto/generated/zitadel/user/v2/User_serviceServiceClientPb';
|
||||
//@ts-ignore
|
||||
import { createUserServiceClient } from '@zitadel/client/v2';
|
||||
//@ts-ignore
|
||||
import { createAuthServiceClient, createManagementServiceClient } from '@zitadel/client/v1';
|
||||
import { createGrpcWebTransport } from '@connectrpc/connect-web';
|
||||
import { FeatureServiceClient } from '../proto/generated/zitadel/feature/v2/Feature_serviceServiceClientPb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -28,15 +32,20 @@ export class GrpcService {
|
||||
public mgmt!: ManagementServiceClient;
|
||||
public admin!: AdminServiceClient;
|
||||
public feature!: FeatureServiceClient;
|
||||
public user!: UserServiceClient;
|
||||
public userNew!: ReturnType<typeof createUserServiceClient>;
|
||||
public mgmtNew!: ReturnType<typeof createManagementServiceClient>;
|
||||
public authNew!: ReturnType<typeof createAuthServiceClient>;
|
||||
|
||||
constructor(
|
||||
private envService: EnvironmentService,
|
||||
private platformLocation: PlatformLocation,
|
||||
private authenticationService: AuthenticationService,
|
||||
private storageService: StorageService,
|
||||
private dialog: MatDialog,
|
||||
private translate: TranslateService,
|
||||
private exhaustedService: ExhaustedService,
|
||||
private readonly envService: EnvironmentService,
|
||||
private readonly platformLocation: PlatformLocation,
|
||||
private readonly authenticationService: AuthenticationService,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly translate: TranslateService,
|
||||
private readonly exhaustedService: ExhaustedService,
|
||||
private readonly authInterceptor: AuthInterceptor,
|
||||
private readonly authInterceptorProvider: AuthInterceptorProvider,
|
||||
) {}
|
||||
|
||||
public loadAppEnvironment(): Promise<any> {
|
||||
@@ -44,66 +53,79 @@ export class GrpcService {
|
||||
|
||||
const browserLanguage = this.translate.getBrowserLang();
|
||||
const language = browserLanguage?.match(supportedLanguagesRegexp) ? browserLanguage : fallbackLanguage;
|
||||
return this.translate
|
||||
.use(language || this.translate.defaultLang)
|
||||
.pipe(
|
||||
switchMap(() => this.envService.env),
|
||||
tap((env) => {
|
||||
if (!env?.api || !env?.issuer) {
|
||||
return;
|
||||
}
|
||||
const interceptors = {
|
||||
unaryInterceptors: [
|
||||
new ExhaustedGrpcInterceptor(this.exhaustedService, this.envService),
|
||||
new OrgInterceptor(this.storageService),
|
||||
new AuthInterceptor(this.authenticationService, this.storageService, this.dialog),
|
||||
new I18nInterceptor(this.translate),
|
||||
],
|
||||
};
|
||||
const init = this.translate.use(language || this.translate.defaultLang).pipe(
|
||||
switchMap(() => this.envService.env),
|
||||
tap((env) => {
|
||||
if (!env?.api || !env?.issuer) {
|
||||
return;
|
||||
}
|
||||
const interceptors = {
|
||||
unaryInterceptors: [
|
||||
new ExhaustedGrpcInterceptor(this.exhaustedService, this.envService),
|
||||
new OrgInterceptor(this.storageService),
|
||||
this.authInterceptor,
|
||||
new I18nInterceptor(this.translate),
|
||||
],
|
||||
};
|
||||
|
||||
this.auth = new AuthServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.mgmt = new ManagementServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.admin = new AdminServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.feature = new FeatureServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.auth = new AuthServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.mgmt = new ManagementServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.admin = new AdminServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.feature = new FeatureServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
this.user = new UserServiceClient(
|
||||
env.api,
|
||||
null,
|
||||
// @ts-ignore
|
||||
interceptors,
|
||||
);
|
||||
|
||||
const authConfig: AuthConfig = {
|
||||
scope: 'openid profile email',
|
||||
responseType: 'code',
|
||||
oidc: true,
|
||||
clientId: env.clientid,
|
||||
issuer: env.issuer,
|
||||
redirectUri: window.location.origin + this.platformLocation.getBaseHrefFromDOM() + 'auth/callback',
|
||||
postLogoutRedirectUri: window.location.origin + this.platformLocation.getBaseHrefFromDOM() + 'signedout',
|
||||
requireHttps: false,
|
||||
};
|
||||
const transport = createGrpcWebTransport({
|
||||
baseUrl: env.api,
|
||||
interceptors: [NewConnectWebAuthInterceptor(this.authInterceptorProvider)],
|
||||
});
|
||||
this.userNew = createUserServiceClient(transport);
|
||||
this.mgmtNew = createManagementServiceClient(transport);
|
||||
this.authNew = createAuthServiceClient(transport);
|
||||
|
||||
this.authenticationService.initConfig(authConfig);
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('Failed to load environment from assets', err);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
)
|
||||
.toPromise();
|
||||
const authConfig: AuthConfig = {
|
||||
scope: 'openid profile email',
|
||||
responseType: 'code',
|
||||
oidc: true,
|
||||
clientId: env.clientid,
|
||||
issuer: env.issuer,
|
||||
redirectUri: window.location.origin + this.platformLocation.getBaseHrefFromDOM() + 'auth/callback',
|
||||
postLogoutRedirectUri: window.location.origin + this.platformLocation.getBaseHrefFromDOM() + 'signedout',
|
||||
requireHttps: false,
|
||||
};
|
||||
|
||||
this.authenticationService.initConfig(authConfig);
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('Failed to load environment from assets', err);
|
||||
throw err;
|
||||
}),
|
||||
);
|
||||
|
||||
return firstValueFrom(init);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,53 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DestroyRef, Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Request, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { Subject } from 'rxjs';
|
||||
import { debounceTime, filter, first, map, take, tap } from 'rxjs/operators';
|
||||
import { Request, RpcError, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { firstValueFrom, identity, lastValueFrom, Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, map } from 'rxjs/operators';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
|
||||
import { AuthenticationService } from '../authentication.service';
|
||||
import { StorageService } from '../storage.service';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { GrpcAuthService } from '../grpc-auth.service';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ConnectError, Interceptor } from '@connectrpc/connect';
|
||||
|
||||
const authorizationKey = 'Authorization';
|
||||
const bearerPrefix = 'Bearer';
|
||||
const accessTokenStorageKey = 'access_token';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
/**
|
||||
* Set the authentication token
|
||||
*/
|
||||
export class AuthInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
||||
export class AuthInterceptorProvider {
|
||||
public triggerDialog: Subject<boolean> = new Subject();
|
||||
|
||||
constructor(
|
||||
private authenticationService: AuthenticationService,
|
||||
private storageService: StorageService,
|
||||
private dialog: MatDialog,
|
||||
private destroyRef: DestroyRef,
|
||||
) {
|
||||
this.triggerDialog.pipe(debounceTime(1000)).subscribe(() => {
|
||||
this.openDialog();
|
||||
});
|
||||
this.triggerDialog.pipe(debounceTime(1000), takeUntilDestroyed(this.destroyRef)).subscribe(() => this.openDialog());
|
||||
}
|
||||
|
||||
public async intercept(request: Request<TReq, TResp>, invoker: any): Promise<UnaryResponse<TReq, TResp>> {
|
||||
await this.authenticationService.authenticationChanged
|
||||
.pipe(
|
||||
filter((authed) => !!authed),
|
||||
first(),
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
const metadata = request.getMetadata();
|
||||
const accessToken = this.storageService.getItem(accessTokenStorageKey);
|
||||
metadata[authorizationKey] = `${bearerPrefix} ${accessToken}`;
|
||||
|
||||
return invoker(request)
|
||||
.then((response: any) => {
|
||||
return response;
|
||||
})
|
||||
.catch(async (error: any) => {
|
||||
if (error.code === 16 || (error.code === 7 && error.message === 'mfa required (AUTHZ-Kl3p0)')) {
|
||||
this.triggerDialog.next(true);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
getToken(): Observable<string> {
|
||||
return this.authenticationService.authenticationChanged.pipe(
|
||||
filter(identity),
|
||||
map(() => this.storageService.getItem(accessTokenStorageKey)),
|
||||
map((token) => `${bearerPrefix} ${token}`),
|
||||
);
|
||||
}
|
||||
|
||||
private openDialog(): void {
|
||||
handleError = (error: any): never => {
|
||||
if (!(error instanceof RpcError) && !(error instanceof ConnectError)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error.code === 16 || (error.code === 7 && error.message === 'mfa required (AUTHZ-Kl3p0)')) {
|
||||
this.triggerDialog.next(true);
|
||||
}
|
||||
throw error;
|
||||
};
|
||||
|
||||
private async openDialog(): Promise<void> {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.LOGIN',
|
||||
@@ -64,19 +58,47 @@ export class AuthInterceptor<TReq = unknown, TResp = unknown> implements UnaryIn
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe((resp) => {
|
||||
if (resp) {
|
||||
const idToken = this.authenticationService.getIdToken();
|
||||
const configWithPrompt: Partial<AuthConfig> = {
|
||||
customQueryParams: {
|
||||
id_token_hint: idToken,
|
||||
},
|
||||
};
|
||||
this.authenticationService.authenticate(configWithPrompt, true);
|
||||
}
|
||||
});
|
||||
const resp = await lastValueFrom(dialogRef.afterClosed());
|
||||
if (!resp) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idToken = this.authenticationService.getIdToken();
|
||||
const configWithPrompt: Partial<AuthConfig> = {
|
||||
customQueryParams: {
|
||||
id_token_hint: idToken,
|
||||
},
|
||||
};
|
||||
|
||||
await this.authenticationService.authenticate(configWithPrompt, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
/**
|
||||
* Set the authentication token
|
||||
*/
|
||||
export class AuthInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
||||
constructor(private readonly authInterceptorProvider: AuthInterceptorProvider) {}
|
||||
|
||||
public async intercept(
|
||||
request: Request<TReq, TResp>,
|
||||
invoker: (request: Request<TReq, TResp>) => Promise<UnaryResponse<TReq, TResp>>,
|
||||
): Promise<UnaryResponse<TReq, TResp>> {
|
||||
const metadata = request.getMetadata();
|
||||
metadata[authorizationKey] = await firstValueFrom(this.authInterceptorProvider.getToken());
|
||||
|
||||
return invoker(request).catch(this.authInterceptorProvider.handleError);
|
||||
}
|
||||
}
|
||||
|
||||
export function NewConnectWebAuthInterceptor(authInterceptorProvider: AuthInterceptorProvider): Interceptor {
|
||||
return (next) => async (req) => {
|
||||
if (!req.header.get('Authorization')) {
|
||||
const token = await firstValueFrom(authInterceptorProvider.getToken());
|
||||
req.header.set('Authorization', token);
|
||||
}
|
||||
|
||||
return next(req).catch(authInterceptorProvider.handleError);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Request, StatusCode, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { Request, RpcError, StatusCode, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { EnvironmentService } from '../environment.service';
|
||||
import { ExhaustedService } from '../exhausted.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
/**
|
||||
* ExhaustedGrpcInterceptor shows the exhausted dialog after receiving a gRPC response status 8.
|
||||
@@ -17,16 +18,13 @@ export class ExhaustedGrpcInterceptor<TReq = unknown, TResp = unknown> implement
|
||||
request: Request<TReq, TResp>,
|
||||
invoker: (request: Request<TReq, TResp>) => Promise<UnaryResponse<TReq, TResp>>,
|
||||
): Promise<UnaryResponse<TReq, TResp>> {
|
||||
return invoker(request).catch((error: any) => {
|
||||
if (error.code === StatusCode.RESOURCE_EXHAUSTED) {
|
||||
return this.exhaustedSvc
|
||||
.showExhaustedDialog(this.envSvc.env)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
throw error;
|
||||
});
|
||||
try {
|
||||
return await invoker(request);
|
||||
} catch (error: any) {
|
||||
if (error instanceof RpcError && error.code === StatusCode.RESOURCE_EXHAUSTED) {
|
||||
await lastValueFrom(this.exhaustedSvc.showExhaustedDialog(this.envSvc.env));
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const i18nHeader = 'Accept-Language';
|
||||
export class I18nInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
||||
constructor(private translate: TranslateService) {}
|
||||
|
||||
public async intercept(request: Request<TReq, TResp>, invoker: any): Promise<UnaryResponse<TReq, TResp>> {
|
||||
public intercept(request: Request<TReq, TResp>, invoker: any): Promise<UnaryResponse<TReq, TResp>> {
|
||||
const metadata = request.getMetadata();
|
||||
|
||||
const navLang = this.translate.currentLang ?? navigator.language;
|
||||
@@ -18,12 +18,6 @@ export class I18nInterceptor<TReq = unknown, TResp = unknown> implements UnaryIn
|
||||
metadata[i18nHeader] = navLang;
|
||||
}
|
||||
|
||||
return invoker(request)
|
||||
.then((response: any) => {
|
||||
return response;
|
||||
})
|
||||
.catch((error: any) => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
return invoker(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Request, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { Request, RpcError, StatusCode, UnaryInterceptor, UnaryResponse } from 'grpc-web';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
|
||||
import { StorageKey, StorageLocation, StorageService } from '../storage.service';
|
||||
@@ -9,7 +9,7 @@ const ORG_HEADER_KEY = 'x-zitadel-orgid';
|
||||
export class OrgInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
||||
constructor(private readonly storageService: StorageService) {}
|
||||
|
||||
public intercept(request: Request<TReq, TResp>, invoker: any): Promise<UnaryResponse<TReq, TResp>> {
|
||||
public async intercept(request: Request<TReq, TResp>, invoker: any): Promise<UnaryResponse<TReq, TResp>> {
|
||||
const metadata = request.getMetadata();
|
||||
|
||||
const org: Org.AsObject | null = this.storageService.getItem(StorageKey.organization, StorageLocation.session);
|
||||
@@ -18,15 +18,17 @@ export class OrgInterceptor<TReq = unknown, TResp = unknown> implements UnaryInt
|
||||
metadata[ORG_HEADER_KEY] = `${org.id}`;
|
||||
}
|
||||
|
||||
return invoker(request)
|
||||
.then((response: any) => {
|
||||
return response;
|
||||
})
|
||||
.catch((error: any) => {
|
||||
if (error.code === 7 && error.message.startsWith("Organisation doesn't exist")) {
|
||||
this.storageService.removeItem(StorageKey.organization, StorageLocation.session);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
try {
|
||||
return await invoker(request);
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error instanceof RpcError &&
|
||||
error.code === StatusCode.PERMISSION_DENIED &&
|
||||
error.message.startsWith("Organisation doesn't exist")
|
||||
) {
|
||||
this.storageService.removeItem(StorageKey.organization, StorageLocation.session);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
console/src/app/services/new-auth.service.ts
Normal file
30
console/src/app/services/new-auth.service.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import { create } from '@bufbuild/protobuf';
|
||||
import {
|
||||
AddMyAuthFactorOTPSMSRequestSchema,
|
||||
AddMyAuthFactorOTPSMSResponse,
|
||||
GetMyUserRequestSchema,
|
||||
GetMyUserResponse,
|
||||
VerifyMyPhoneRequestSchema,
|
||||
VerifyMyPhoneResponse,
|
||||
} from '@zitadel/proto/zitadel/auth_pb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NewAuthService {
|
||||
constructor(private readonly grpcService: GrpcService) {}
|
||||
|
||||
public getMyUser(): Promise<GetMyUserResponse> {
|
||||
return this.grpcService.authNew.getMyUser(create(GetMyUserRequestSchema));
|
||||
}
|
||||
|
||||
public verifyMyPhone(code: string): Promise<VerifyMyPhoneResponse> {
|
||||
return this.grpcService.authNew.verifyMyPhone(create(VerifyMyPhoneRequestSchema, { code }));
|
||||
}
|
||||
|
||||
public addMyAuthFactorOTPSMS(): Promise<AddMyAuthFactorOTPSMSResponse> {
|
||||
return this.grpcService.authNew.addMyAuthFactorOTPSMS(create(AddMyAuthFactorOTPSMSRequestSchema));
|
||||
}
|
||||
}
|
||||
92
console/src/app/services/new-mgmt.service.ts
Normal file
92
console/src/app/services/new-mgmt.service.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import {
|
||||
GenerateMachineSecretRequestSchema,
|
||||
GenerateMachineSecretResponse,
|
||||
GetLoginPolicyRequestSchema,
|
||||
GetLoginPolicyResponse,
|
||||
ListUserMetadataRequestSchema,
|
||||
ListUserMetadataResponse,
|
||||
RemoveMachineSecretRequestSchema,
|
||||
RemoveMachineSecretResponse,
|
||||
RemoveUserMetadataRequestSchema,
|
||||
RemoveUserMetadataResponse,
|
||||
ResendHumanEmailVerificationRequestSchema,
|
||||
ResendHumanEmailVerificationResponse,
|
||||
ResendHumanInitializationRequestSchema,
|
||||
ResendHumanInitializationResponse,
|
||||
ResendHumanPhoneVerificationRequestSchema,
|
||||
ResendHumanPhoneVerificationResponse,
|
||||
SendHumanResetPasswordNotificationRequest_Type,
|
||||
SendHumanResetPasswordNotificationRequestSchema,
|
||||
SendHumanResetPasswordNotificationResponse,
|
||||
SetUserMetadataRequestSchema,
|
||||
SetUserMetadataResponse,
|
||||
UpdateMachineRequestSchema,
|
||||
UpdateMachineResponse,
|
||||
} from '@zitadel/proto/zitadel/management_pb';
|
||||
import { MessageInitShape, create } from '@bufbuild/protobuf';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NewMgmtService {
|
||||
constructor(private readonly grpcService: GrpcService) {}
|
||||
|
||||
public getLoginPolicy(): Promise<GetLoginPolicyResponse> {
|
||||
return this.grpcService.mgmtNew.getLoginPolicy(create(GetLoginPolicyRequestSchema));
|
||||
}
|
||||
|
||||
public generateMachineSecret(userId: string): Promise<GenerateMachineSecretResponse> {
|
||||
return this.grpcService.mgmtNew.generateMachineSecret(create(GenerateMachineSecretRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public removeMachineSecret(userId: string): Promise<RemoveMachineSecretResponse> {
|
||||
return this.grpcService.mgmtNew.removeMachineSecret(create(RemoveMachineSecretRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public updateMachine(req: MessageInitShape<typeof UpdateMachineRequestSchema>): Promise<UpdateMachineResponse> {
|
||||
return this.grpcService.mgmtNew.updateMachine(create(UpdateMachineRequestSchema, req));
|
||||
}
|
||||
|
||||
public resendHumanEmailVerification(userId: string): Promise<ResendHumanEmailVerificationResponse> {
|
||||
return this.grpcService.mgmtNew.resendHumanEmailVerification(
|
||||
create(ResendHumanEmailVerificationRequestSchema, { userId }),
|
||||
);
|
||||
}
|
||||
|
||||
public resendHumanPhoneVerification(userId: string): Promise<ResendHumanPhoneVerificationResponse> {
|
||||
return this.grpcService.mgmtNew.resendHumanPhoneVerification(
|
||||
create(ResendHumanPhoneVerificationRequestSchema, { userId }),
|
||||
);
|
||||
}
|
||||
|
||||
public sendHumanResetPasswordNotification(
|
||||
userId: string,
|
||||
type: SendHumanResetPasswordNotificationRequest_Type,
|
||||
): Promise<SendHumanResetPasswordNotificationResponse> {
|
||||
return this.grpcService.mgmtNew.sendHumanResetPasswordNotification(
|
||||
create(SendHumanResetPasswordNotificationRequestSchema, { userId, type }),
|
||||
);
|
||||
}
|
||||
|
||||
public resendHumanInitialization(userId: string, email: string = ''): Promise<ResendHumanInitializationResponse> {
|
||||
return this.grpcService.mgmtNew.resendHumanInitialization(
|
||||
create(ResendHumanInitializationRequestSchema, { userId, email }),
|
||||
);
|
||||
}
|
||||
|
||||
public listUserMetadata(id: string): Promise<ListUserMetadataResponse> {
|
||||
return this.grpcService.mgmtNew.listUserMetadata(create(ListUserMetadataRequestSchema, { id }));
|
||||
}
|
||||
|
||||
public setUserMetadata(req: MessageInitShape<typeof SetUserMetadataRequestSchema>): Promise<SetUserMetadataResponse> {
|
||||
return this.grpcService.mgmtNew.setUserMetadata(create(SetUserMetadataRequestSchema, req));
|
||||
}
|
||||
|
||||
public removeUserMetadata(
|
||||
req: MessageInitShape<typeof RemoveUserMetadataRequestSchema>,
|
||||
): Promise<RemoveUserMetadataResponse> {
|
||||
return this.grpcService.mgmtNew.removeUserMetadata(create(RemoveUserMetadataRequestSchema, req));
|
||||
}
|
||||
}
|
||||
302
console/src/app/services/user.service.ts
Normal file
302
console/src/app/services/user.service.ts
Normal file
@@ -0,0 +1,302 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import {
|
||||
AddHumanUserRequestSchema,
|
||||
AddHumanUserResponse,
|
||||
CreateInviteCodeRequestSchema,
|
||||
CreateInviteCodeResponse,
|
||||
CreatePasskeyRegistrationLinkRequestSchema,
|
||||
CreatePasskeyRegistrationLinkResponse,
|
||||
DeactivateUserRequestSchema,
|
||||
DeactivateUserResponse,
|
||||
DeleteUserRequestSchema,
|
||||
DeleteUserResponse,
|
||||
GetUserByIDRequestSchema,
|
||||
GetUserByIDResponse,
|
||||
ListAuthenticationFactorsRequestSchema,
|
||||
ListAuthenticationFactorsResponse,
|
||||
ListPasskeysRequestSchema,
|
||||
ListPasskeysResponse,
|
||||
ListUsersRequestSchema,
|
||||
ListUsersResponse,
|
||||
LockUserRequestSchema,
|
||||
LockUserResponse,
|
||||
PasswordResetRequestSchema,
|
||||
ReactivateUserRequestSchema,
|
||||
ReactivateUserResponse,
|
||||
RemoveOTPEmailRequestSchema,
|
||||
RemoveOTPEmailResponse,
|
||||
RemoveOTPSMSRequestSchema,
|
||||
RemoveOTPSMSResponse,
|
||||
RemovePasskeyRequestSchema,
|
||||
RemovePasskeyResponse,
|
||||
RemovePhoneRequestSchema,
|
||||
RemovePhoneResponse,
|
||||
RemoveTOTPRequestSchema,
|
||||
RemoveTOTPResponse,
|
||||
RemoveU2FRequestSchema,
|
||||
RemoveU2FResponse,
|
||||
ResendInviteCodeRequestSchema,
|
||||
ResendInviteCodeResponse,
|
||||
SetEmailRequestSchema,
|
||||
SetEmailResponse,
|
||||
SetPasswordRequestSchema,
|
||||
SetPasswordResponse,
|
||||
SetPhoneRequestSchema,
|
||||
SetPhoneResponse,
|
||||
UnlockUserRequestSchema,
|
||||
UnlockUserResponse,
|
||||
UpdateHumanUserRequestSchema,
|
||||
UpdateHumanUserResponse,
|
||||
} from '@zitadel/proto/zitadel/user/v2/user_service_pb';
|
||||
import type { MessageInitShape } from '@bufbuild/protobuf';
|
||||
import {
|
||||
AccessTokenType,
|
||||
Gender,
|
||||
HumanProfile,
|
||||
HumanProfileSchema,
|
||||
HumanUser,
|
||||
HumanUserSchema,
|
||||
MachineUser,
|
||||
MachineUserSchema,
|
||||
User as UserV2,
|
||||
UserSchema,
|
||||
UserState,
|
||||
} from '@zitadel/proto/zitadel/user/v2/user_pb';
|
||||
import { create } from '@bufbuild/protobuf';
|
||||
import { Timestamp as TimestampV2, TimestampSchema } from '@bufbuild/protobuf/wkt';
|
||||
import { Details, DetailsSchema, ListQuerySchema } from '@zitadel/proto/zitadel/object/v2/object_pb';
|
||||
import { SearchQuery, UserFieldName } from '@zitadel/proto/zitadel/user/v2/query_pb';
|
||||
import { SortDirection } from '@angular/material/sort';
|
||||
import { Human, Machine, Phone, Profile, User } from '../proto/generated/zitadel/user_pb';
|
||||
import { ObjectDetails } from '../proto/generated/zitadel/object_pb';
|
||||
import { Timestamp } from '../proto/generated/google/protobuf/timestamp_pb';
|
||||
import { HumanPhone, HumanPhoneSchema } from '@zitadel/proto/zitadel/user/v2/phone_pb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
constructor(private readonly grpcService: GrpcService) {}
|
||||
|
||||
public addHumanUser(req: MessageInitShape<typeof AddHumanUserRequestSchema>): Promise<AddHumanUserResponse> {
|
||||
return this.grpcService.userNew.addHumanUser(create(AddHumanUserRequestSchema, req));
|
||||
}
|
||||
|
||||
public listUsers(
|
||||
limit: number,
|
||||
offset: number,
|
||||
queriesList?: SearchQuery[],
|
||||
sortingColumn?: UserFieldName,
|
||||
sortingDirection?: SortDirection,
|
||||
): Promise<ListUsersResponse> {
|
||||
const query = create(ListQuerySchema);
|
||||
|
||||
if (limit) {
|
||||
query.limit = limit;
|
||||
}
|
||||
if (offset) {
|
||||
query.offset = BigInt(offset);
|
||||
}
|
||||
if (sortingDirection) {
|
||||
query.asc = sortingDirection === 'asc';
|
||||
}
|
||||
|
||||
const req = create(ListUsersRequestSchema, {
|
||||
query,
|
||||
});
|
||||
|
||||
if (sortingColumn) {
|
||||
req.sortingColumn = sortingColumn;
|
||||
}
|
||||
|
||||
if (queriesList) {
|
||||
req.queries = queriesList;
|
||||
}
|
||||
|
||||
return this.grpcService.userNew.listUsers(req);
|
||||
}
|
||||
|
||||
public getUserById(userId: string): Promise<GetUserByIDResponse> {
|
||||
return this.grpcService.userNew.getUserByID(create(GetUserByIDRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public deactivateUser(userId: string): Promise<DeactivateUserResponse> {
|
||||
return this.grpcService.userNew.deactivateUser(create(DeactivateUserRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public reactivateUser(userId: string): Promise<ReactivateUserResponse> {
|
||||
return this.grpcService.userNew.reactivateUser(create(ReactivateUserRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public deleteUser(userId: string): Promise<DeleteUserResponse> {
|
||||
return this.grpcService.userNew.deleteUser(create(DeleteUserRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public updateUser(req: MessageInitShape<typeof UpdateHumanUserRequestSchema>): Promise<UpdateHumanUserResponse> {
|
||||
return this.grpcService.userNew.updateHumanUser(create(UpdateHumanUserRequestSchema, req));
|
||||
}
|
||||
|
||||
public lockUser(userId: string): Promise<LockUserResponse> {
|
||||
return this.grpcService.userNew.lockUser(create(LockUserRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public unlockUser(userId: string): Promise<UnlockUserResponse> {
|
||||
return this.grpcService.userNew.unlockUser(create(UnlockUserRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public listAuthenticationFactors(
|
||||
req: MessageInitShape<typeof ListAuthenticationFactorsRequestSchema>,
|
||||
): Promise<ListAuthenticationFactorsResponse> {
|
||||
return this.grpcService.userNew.listAuthenticationFactors(create(ListAuthenticationFactorsRequestSchema, req));
|
||||
}
|
||||
|
||||
public listPasskeys(req: MessageInitShape<typeof ListPasskeysRequestSchema>): Promise<ListPasskeysResponse> {
|
||||
return this.grpcService.userNew.listPasskeys(create(ListPasskeysRequestSchema, req));
|
||||
}
|
||||
|
||||
public removePasskeys(req: MessageInitShape<typeof RemovePasskeyRequestSchema>): Promise<RemovePasskeyResponse> {
|
||||
return this.grpcService.userNew.removePasskey(create(RemovePasskeyRequestSchema, req));
|
||||
}
|
||||
|
||||
public createPasskeyRegistrationLink(
|
||||
req: MessageInitShape<typeof CreatePasskeyRegistrationLinkRequestSchema>,
|
||||
): Promise<CreatePasskeyRegistrationLinkResponse> {
|
||||
return this.grpcService.userNew.createPasskeyRegistrationLink(create(CreatePasskeyRegistrationLinkRequestSchema, req));
|
||||
}
|
||||
|
||||
public removePhone(userId: string): Promise<RemovePhoneResponse> {
|
||||
return this.grpcService.userNew.removePhone(create(RemovePhoneRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public setPhone(req: MessageInitShape<typeof SetPhoneRequestSchema>): Promise<SetPhoneResponse> {
|
||||
return this.grpcService.userNew.setPhone(create(SetPhoneRequestSchema, req));
|
||||
}
|
||||
|
||||
public setEmail(req: MessageInitShape<typeof SetEmailRequestSchema>): Promise<SetEmailResponse> {
|
||||
return this.grpcService.userNew.setEmail(create(SetEmailRequestSchema, req));
|
||||
}
|
||||
|
||||
public removeTOTP(userId: string): Promise<RemoveTOTPResponse> {
|
||||
return this.grpcService.userNew.removeTOTP(create(RemoveTOTPRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public removeU2F(userId: string, u2fId: string): Promise<RemoveU2FResponse> {
|
||||
return this.grpcService.userNew.removeU2F(create(RemoveU2FRequestSchema, { userId, u2fId }));
|
||||
}
|
||||
|
||||
public removeOTPSMS(userId: string): Promise<RemoveOTPSMSResponse> {
|
||||
return this.grpcService.userNew.removeOTPSMS(create(RemoveOTPSMSRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public removeOTPEmail(userId: string): Promise<RemoveOTPEmailResponse> {
|
||||
return this.grpcService.userNew.removeOTPEmail(create(RemoveOTPEmailRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public resendInviteCode(userId: string): Promise<ResendInviteCodeResponse> {
|
||||
return this.grpcService.userNew.resendInviteCode(create(ResendInviteCodeRequestSchema, { userId }));
|
||||
}
|
||||
|
||||
public createInviteCode(req: MessageInitShape<typeof CreateInviteCodeRequestSchema>): Promise<CreateInviteCodeResponse> {
|
||||
return this.grpcService.userNew.createInviteCode(create(CreateInviteCodeRequestSchema, req));
|
||||
}
|
||||
|
||||
public passwordReset(req: MessageInitShape<typeof PasswordResetRequestSchema>) {
|
||||
return this.grpcService.userNew.passwordReset(create(PasswordResetRequestSchema, req));
|
||||
}
|
||||
|
||||
public setPassword(req: MessageInitShape<typeof SetPasswordRequestSchema>): Promise<SetPasswordResponse> {
|
||||
return this.grpcService.userNew.setPassword(create(SetPasswordRequestSchema, req));
|
||||
}
|
||||
}
|
||||
|
||||
function userToV2(user: User): UserV2 {
|
||||
const details = user.getDetails();
|
||||
return create(UserSchema, {
|
||||
userId: user.getId(),
|
||||
details: details && detailsToV2(details),
|
||||
state: user.getState() as number as UserState,
|
||||
username: user.getUserName(),
|
||||
loginNames: user.getLoginNamesList(),
|
||||
preferredLoginName: user.getPreferredLoginName(),
|
||||
type: typeToV2(user),
|
||||
});
|
||||
}
|
||||
|
||||
function detailsToV2(details: ObjectDetails): Details {
|
||||
const changeDate = details.getChangeDate();
|
||||
return create(DetailsSchema, {
|
||||
sequence: BigInt(details.getSequence()),
|
||||
changeDate: changeDate && timestampToV2(changeDate),
|
||||
resourceOwner: details.getResourceOwner(),
|
||||
});
|
||||
}
|
||||
|
||||
function timestampToV2(timestamp: Timestamp): TimestampV2 {
|
||||
return create(TimestampSchema, {
|
||||
seconds: BigInt(timestamp.getSeconds()),
|
||||
nanos: timestamp.getNanos(),
|
||||
});
|
||||
}
|
||||
|
||||
function typeToV2(user: User): UserV2['type'] {
|
||||
const human = user.getHuman();
|
||||
if (human) {
|
||||
return { case: 'human', value: humanToV2(user, human) };
|
||||
}
|
||||
|
||||
const machine = user.getMachine();
|
||||
if (machine) {
|
||||
return { case: 'machine', value: machineToV2(machine) };
|
||||
}
|
||||
|
||||
return { case: undefined };
|
||||
}
|
||||
|
||||
function humanToV2(user: User, human: Human): HumanUser {
|
||||
const profile = human.getProfile();
|
||||
const email = human.getEmail()?.getEmail();
|
||||
const phone = human.getPhone();
|
||||
const passwordChanged = human.getPasswordChanged();
|
||||
|
||||
return create(HumanUserSchema, {
|
||||
userId: user.getId(),
|
||||
state: user.getState() as number as UserState,
|
||||
username: user.getUserName(),
|
||||
loginNames: user.getLoginNamesList(),
|
||||
preferredLoginName: user.getPreferredLoginName(),
|
||||
profile: profile && humanProfileToV2(profile),
|
||||
email: { email },
|
||||
phone: phone && humanPhoneToV2(phone),
|
||||
passwordChangeRequired: false,
|
||||
passwordChanged: passwordChanged && timestampToV2(passwordChanged),
|
||||
});
|
||||
}
|
||||
|
||||
function humanProfileToV2(profile: Profile): HumanProfile {
|
||||
return create(HumanProfileSchema, {
|
||||
givenName: profile.getFirstName(),
|
||||
familyName: profile.getLastName(),
|
||||
nickName: profile.getNickName(),
|
||||
displayName: profile.getDisplayName(),
|
||||
preferredLanguage: profile.getPreferredLanguage(),
|
||||
gender: profile.getGender() as number as Gender,
|
||||
avatarUrl: profile.getAvatarUrl(),
|
||||
});
|
||||
}
|
||||
|
||||
function humanPhoneToV2(phone: Phone): HumanPhone {
|
||||
return create(HumanPhoneSchema, {
|
||||
phone: phone.getPhone(),
|
||||
isVerified: phone.getIsPhoneVerified(),
|
||||
});
|
||||
}
|
||||
|
||||
function machineToV2(machine: Machine): MachineUser {
|
||||
return create(MachineUserSchema, {
|
||||
name: machine.getName(),
|
||||
description: machine.getDescription(),
|
||||
hasSecret: machine.getHasSecret(),
|
||||
accessTokenType: machine.getAccessTokenType() as number as AccessTokenType,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user