mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:57:33 +00:00
fix(console): allow management of metadata of users of other organizations again (#9490)
# Which Problems Are Solved
With the recent change in Console to use the User V2 API
(https://github.com/zitadel/zitadel/pull/9312), some functionality still
needs to call the management API, which requires the organization
context. The context was not passed anymore, leading to error in cases
where the calling user (e.g. an IAM_OWNER) was not part of the same
organization.
# How the Problems Are Solved
Added an interceptor to provide the `x-zitadel-orgid` header for the new
management client.
# Additional Changes
None
# Additional Context
- closes #9488
(cherry picked from commit 61c4b1c3fd
)
This commit is contained in:
@@ -14,7 +14,7 @@ import { ExhaustedService } from './exhausted.service';
|
|||||||
import { AuthInterceptor, AuthInterceptorProvider, NewConnectWebAuthInterceptor } from './interceptors/auth.interceptor';
|
import { AuthInterceptor, AuthInterceptorProvider, NewConnectWebAuthInterceptor } from './interceptors/auth.interceptor';
|
||||||
import { ExhaustedGrpcInterceptor } from './interceptors/exhausted.grpc.interceptor';
|
import { ExhaustedGrpcInterceptor } from './interceptors/exhausted.grpc.interceptor';
|
||||||
import { I18nInterceptor } from './interceptors/i18n.interceptor';
|
import { I18nInterceptor } from './interceptors/i18n.interceptor';
|
||||||
import { OrgInterceptor } from './interceptors/org.interceptor';
|
import { NewConnectWebOrgInterceptor, OrgInterceptor, OrgInterceptorProvider } from './interceptors/org.interceptor';
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
import { UserServiceClient } from '../proto/generated/zitadel/user/v2/User_serviceServiceClientPb';
|
import { UserServiceClient } from '../proto/generated/zitadel/user/v2/User_serviceServiceClientPb';
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -46,6 +46,7 @@ export class GrpcService {
|
|||||||
private readonly exhaustedService: ExhaustedService,
|
private readonly exhaustedService: ExhaustedService,
|
||||||
private readonly authInterceptor: AuthInterceptor,
|
private readonly authInterceptor: AuthInterceptor,
|
||||||
private readonly authInterceptorProvider: AuthInterceptorProvider,
|
private readonly authInterceptorProvider: AuthInterceptorProvider,
|
||||||
|
private readonly orgInterceptorProvider: OrgInterceptorProvider,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public loadAppEnvironment(): Promise<any> {
|
public loadAppEnvironment(): Promise<any> {
|
||||||
@@ -62,7 +63,7 @@ export class GrpcService {
|
|||||||
const interceptors = {
|
const interceptors = {
|
||||||
unaryInterceptors: [
|
unaryInterceptors: [
|
||||||
new ExhaustedGrpcInterceptor(this.exhaustedService, this.envService),
|
new ExhaustedGrpcInterceptor(this.exhaustedService, this.envService),
|
||||||
new OrgInterceptor(this.storageService),
|
new OrgInterceptor(this.orgInterceptorProvider),
|
||||||
this.authInterceptor,
|
this.authInterceptor,
|
||||||
new I18nInterceptor(this.translate),
|
new I18nInterceptor(this.translate),
|
||||||
],
|
],
|
||||||
@@ -103,8 +104,15 @@ export class GrpcService {
|
|||||||
baseUrl: env.api,
|
baseUrl: env.api,
|
||||||
interceptors: [NewConnectWebAuthInterceptor(this.authInterceptorProvider)],
|
interceptors: [NewConnectWebAuthInterceptor(this.authInterceptorProvider)],
|
||||||
});
|
});
|
||||||
|
const transportOldAPIs = createGrpcWebTransport({
|
||||||
|
baseUrl: env.api,
|
||||||
|
interceptors: [
|
||||||
|
NewConnectWebAuthInterceptor(this.authInterceptorProvider),
|
||||||
|
NewConnectWebOrgInterceptor(this.orgInterceptorProvider),
|
||||||
|
],
|
||||||
|
});
|
||||||
this.userNew = createUserServiceClient(transport);
|
this.userNew = createUserServiceClient(transport);
|
||||||
this.mgmtNew = createManagementServiceClient(transport);
|
this.mgmtNew = createManagementServiceClient(transportOldAPIs);
|
||||||
this.authNew = createAuthServiceClient(transport);
|
this.authNew = createAuthServiceClient(transport);
|
||||||
|
|
||||||
const authConfig: AuthConfig = {
|
const authConfig: AuthConfig = {
|
||||||
|
@@ -3,32 +3,63 @@ import { Request, RpcError, StatusCode, UnaryInterceptor, UnaryResponse } from '
|
|||||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||||
|
|
||||||
import { StorageKey, StorageLocation, StorageService } from '../storage.service';
|
import { StorageKey, StorageLocation, StorageService } from '../storage.service';
|
||||||
|
import { ConnectError, Interceptor } from '@connectrpc/connect';
|
||||||
|
import { firstValueFrom, identity, Observable, Subject } from 'rxjs';
|
||||||
|
import { debounceTime, filter, map } from 'rxjs/operators';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
const ORG_HEADER_KEY = 'x-zitadel-orgid';
|
const ORG_HEADER_KEY = 'x-zitadel-orgid';
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class OrgInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
export class OrgInterceptor<TReq = unknown, TResp = unknown> implements UnaryInterceptor<TReq, TResp> {
|
||||||
constructor(private readonly storageService: StorageService) {}
|
constructor(private readonly orgInterceptorProvider: OrgInterceptorProvider) {}
|
||||||
|
|
||||||
public async 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 metadata = request.getMetadata();
|
||||||
|
|
||||||
const org: Org.AsObject | null = this.storageService.getItem(StorageKey.organization, StorageLocation.session);
|
const orgId = this.orgInterceptorProvider.getOrgId();
|
||||||
|
if (orgId) {
|
||||||
if (org) {
|
metadata[ORG_HEADER_KEY] = orgId;
|
||||||
metadata[ORG_HEADER_KEY] = `${org.id}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return invoker(request).catch(this.orgInterceptorProvider.handleError);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NewConnectWebOrgInterceptor(orgInterceptorProvider: OrgInterceptorProvider): Interceptor {
|
||||||
|
return (next) => async (req) => {
|
||||||
|
if (!req.header.get(ORG_HEADER_KEY)) {
|
||||||
|
const orgId = orgInterceptorProvider.getOrgId();
|
||||||
|
if (orgId) {
|
||||||
|
req.header.set(ORG_HEADER_KEY, orgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(req).catch(orgInterceptorProvider.handleError);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class OrgInterceptorProvider {
|
||||||
|
constructor(private storageService: StorageService) {}
|
||||||
|
|
||||||
|
getOrgId() {
|
||||||
|
const org: Org.AsObject | null = this.storageService.getItem(StorageKey.organization, StorageLocation.session);
|
||||||
|
return org?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError = (error: any): never => {
|
||||||
|
if (!(error instanceof RpcError) && !(error instanceof ConnectError)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user