mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-01 11:52:15 +00:00
fix(console): apply labelpolicy if icon is provided, signout page (#4499)
* label policy as observable * signedout policy via state * add caching * disable loading spinner on signedout * cleanup * catch error * update deps * move policy to localstorage * handle labelpolicy for users without org Co-authored-by: Livio Spring <[email protected]> Co-authored-by: Fabi <[email protected]>
This commit is contained in:
co-authored by
Livio Spring
Fabi
parent
bb16852e84
commit
da51b481ee
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
||||
import { BehaviorSubject, from, lastValueFrom, Observable } from 'rxjs';
|
||||
import { GrpcAuthService } from './grpc-auth.service';
|
||||
|
||||
import { StatehandlerService } from './statehandler/statehandler.service';
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
finalize,
|
||||
map,
|
||||
mergeMap,
|
||||
pairwise,
|
||||
switchMap,
|
||||
take,
|
||||
tap,
|
||||
timeout,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators';
|
||||
@@ -103,15 +105,17 @@ import { ChangeQuery } from '../proto/generated/zitadel/change_pb';
|
||||
import { MetadataQuery } from '../proto/generated/zitadel/metadata_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgFieldName, OrgQuery } from '../proto/generated/zitadel/org_pb';
|
||||
import { LabelPolicy } from '../proto/generated/zitadel/policy_pb';
|
||||
import { Gender, MembershipQuery, User, WebAuthNVerification } from '../proto/generated/zitadel/user_pb';
|
||||
import { GrpcService } from './grpc.service';
|
||||
import { StorageKey, StorageLocation, StorageService } from './storage.service';
|
||||
import { ThemeService } from './theme.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class GrpcAuthService {
|
||||
private _activeOrgChanged: Subject<Org.AsObject> = new Subject();
|
||||
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);
|
||||
private triggerPermissionsRefresh: Subject<void> = new Subject();
|
||||
@@ -132,18 +136,47 @@ export class GrpcAuthService {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
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 zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
|
||||
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private cachedOrgs: Org.AsObject[] = [];
|
||||
private cachedLabelPolicies: { [orgId: string]: LabelPolicy.AsObject } = {};
|
||||
|
||||
constructor(
|
||||
private readonly grpcService: GrpcService,
|
||||
private oauthService: OAuthService,
|
||||
private storage: StorageService,
|
||||
themeService: ThemeService,
|
||||
) {
|
||||
this.zitadelPermissions$.subscribe(this.zitadelPermissions);
|
||||
|
||||
this.labelpolicy$ = this.activeOrgChanged.pipe(
|
||||
switchMap((org) => {
|
||||
this.labelPolicyLoading$.next(true);
|
||||
return from(this.getMyLabelPolicy(org ? org.id : ''));
|
||||
}),
|
||||
filter((policy) => !!policy),
|
||||
);
|
||||
|
||||
this.labelpolicy$.subscribe({
|
||||
next: (policy) => {
|
||||
themeService.applyLabelPolicy(policy);
|
||||
this.labelpolicy.next(policy);
|
||||
this.labelPolicyLoading$.next(false);
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(error);
|
||||
this.labelPolicyLoading$.next(false);
|
||||
},
|
||||
});
|
||||
|
||||
this.user = merge(
|
||||
of(this.oauthService.getAccessToken()).pipe(filter((token) => (token ? true : false))),
|
||||
this.oauthService.events.pipe(
|
||||
@@ -225,10 +258,12 @@ export class GrpcAuthService {
|
||||
const org = this.storage.getItem<Org.AsObject>(StorageKey.organization, StorageLocation.local);
|
||||
if (org && orgs.find((tmp) => tmp.id === org.id)) {
|
||||
this.storage.setItem(StorageKey.organization, org, StorageLocation.session);
|
||||
return org;
|
||||
this.setActiveOrg(org);
|
||||
return Promise.resolve(org);
|
||||
}
|
||||
|
||||
if (orgs.length === 0) {
|
||||
this._activeOrgChanged.next(undefined);
|
||||
return Promise.reject(new Error('No organizations found!'));
|
||||
}
|
||||
const orgToSet = orgs.find((element) => element.id !== '0' && element.name !== '');
|
||||
@@ -241,7 +276,7 @@ export class GrpcAuthService {
|
||||
}
|
||||
}
|
||||
|
||||
public get activeOrgChanged(): Observable<Org.AsObject> {
|
||||
public get activeOrgChanged(): Observable<Org.AsObject | undefined> {
|
||||
return this._activeOrgChanged;
|
||||
}
|
||||
|
||||
@@ -605,8 +640,24 @@ export class GrpcAuthService {
|
||||
return this.grpcService.auth.listMyUserChanges(req, null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
||||
public getMyLabelPolicy(): Promise<GetMyLabelPolicyResponse.AsObject> {
|
||||
return this.grpcService.auth.getMyLabelPolicy(new GetMyLabelPolicyRequest(), null).then((resp) => resp.toObject());
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getMyPrivacyPolicy(): Promise<GetMyPrivacyPolicyResponse.AsObject> {
|
||||
|
||||
@@ -13,16 +13,28 @@ export interface Color {
|
||||
contrastColor: string;
|
||||
}
|
||||
|
||||
const DARK_PRIMARY = '#bbbafa';
|
||||
const PRIMARY = '#5469d4';
|
||||
|
||||
const DARK_WARN = '#ff3b5b';
|
||||
const WARN = '#cd3d56';
|
||||
|
||||
const DARK_BACKGROUND = '#111827';
|
||||
const BACKGROUND = '#fafafa';
|
||||
|
||||
const DARK_TEXT = '#ffffff';
|
||||
const TEXT = '#000000';
|
||||
|
||||
@Injectable()
|
||||
export class ThemeService {
|
||||
private _darkTheme: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public isDarkTheme: Observable<boolean> = this._darkTheme.asObservable();
|
||||
|
||||
public loading: boolean = false;
|
||||
private primaryColorPalette: Color[] = [];
|
||||
private warnColorPalette: Color[] = [];
|
||||
private backgroundColorPalette: Color[] = [];
|
||||
|
||||
constructor(private authService: GrpcAuthService) {
|
||||
constructor() {
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme) {
|
||||
if (theme === 'light-theme') {
|
||||
@@ -31,6 +43,7 @@ export class ThemeService {
|
||||
this.setDarkTheme(true);
|
||||
}
|
||||
}
|
||||
this.applyLabelPolicy(); // apply default
|
||||
}
|
||||
|
||||
setDarkTheme(isDarkTheme: boolean): void {
|
||||
@@ -116,105 +129,68 @@ export class ThemeService {
|
||||
}
|
||||
|
||||
public setDefaultColors = () => {
|
||||
const darkPrimary = '#bbbafa';
|
||||
const lightPrimary = '#5469d4';
|
||||
this.savePrimaryColor(DARK_PRIMARY, true);
|
||||
this.savePrimaryColor(PRIMARY, false);
|
||||
|
||||
const darkWarn = '#ff3b5b';
|
||||
const lightWarn = '#cd3d56';
|
||||
this.saveWarnColor(DARK_WARN, true);
|
||||
this.saveWarnColor(WARN, false);
|
||||
|
||||
const darkBackground = '#111827';
|
||||
const lightBackground = '#fafafa';
|
||||
this.saveBackgroundColor(DARK_BACKGROUND, true);
|
||||
this.saveBackgroundColor(BACKGROUND, false);
|
||||
|
||||
const darkText = '#ffffff';
|
||||
const lightText = '#000000';
|
||||
|
||||
this.savePrimaryColor(darkPrimary, true);
|
||||
this.savePrimaryColor(lightPrimary, false);
|
||||
|
||||
this.saveWarnColor(darkWarn, true);
|
||||
this.saveWarnColor(lightWarn, false);
|
||||
|
||||
this.saveBackgroundColor(darkBackground, true);
|
||||
this.saveBackgroundColor(lightBackground, false);
|
||||
|
||||
this.saveTextColor(darkText, true);
|
||||
this.saveTextColor(lightText, false);
|
||||
this.saveTextColor(DARK_TEXT, true);
|
||||
this.saveTextColor(TEXT, false);
|
||||
};
|
||||
|
||||
public loadPrivateLabelling(forceDefault: boolean = false): Promise<LabelPolicy.AsObject | undefined> {
|
||||
if (forceDefault) {
|
||||
this.setDefaultColors();
|
||||
return Promise.resolve(undefined);
|
||||
public applyLabelPolicy(labelpolicy?: LabelPolicy.AsObject) {
|
||||
if (labelpolicy) {
|
||||
const darkPrimary = labelpolicy?.primaryColorDark ?? DARK_PRIMARY;
|
||||
const lightPrimary = labelpolicy?.primaryColor ?? PRIMARY;
|
||||
|
||||
const darkWarn = labelpolicy?.warnColorDark ?? DARK_WARN;
|
||||
const lightWarn = labelpolicy?.warnColor ?? WARN;
|
||||
|
||||
let darkBackground = labelpolicy?.backgroundColorDark ?? DARK_BACKGROUND;
|
||||
let lightBackground = labelpolicy?.backgroundColor ?? BACKGROUND;
|
||||
|
||||
let darkText = labelpolicy?.fontColorDark ?? DARK_TEXT;
|
||||
let lightText = labelpolicy?.fontColor ?? TEXT;
|
||||
|
||||
this.savePrimaryColor(darkPrimary, true);
|
||||
this.savePrimaryColor(lightPrimary, false);
|
||||
|
||||
this.saveWarnColor(darkWarn, true);
|
||||
this.saveWarnColor(lightWarn, false);
|
||||
|
||||
if (darkBackground && !this.isDark(darkBackground)) {
|
||||
console.info(
|
||||
`Background (${darkBackground}) is not dark enough for a dark theme. Falling back to zitadel background`,
|
||||
);
|
||||
darkBackground = '#111827';
|
||||
}
|
||||
this.saveBackgroundColor(darkBackground || '#111827', true);
|
||||
|
||||
if (lightBackground && !this.isLight(lightBackground)) {
|
||||
console.info(
|
||||
`Background (${lightBackground}) is not light enough for a light theme. Falling back to zitadel background`,
|
||||
);
|
||||
lightBackground = '#fafafa';
|
||||
}
|
||||
this.saveBackgroundColor(lightBackground || '#fafafa', false);
|
||||
|
||||
if (darkText && !this.isLight(darkText)) {
|
||||
console.info(`Text color (${darkText}) is not light enough for a dark theme. Falling back to zitadel's text color`);
|
||||
darkText = '#ffffff';
|
||||
}
|
||||
this.saveTextColor(darkText || '#ffffff', true);
|
||||
|
||||
if (lightText && !this.isDark(lightText)) {
|
||||
console.info(`Text color (${lightText}) is not dark enough for a light theme. Falling back to zitadel's text color`);
|
||||
lightText = '#000000';
|
||||
}
|
||||
this.saveTextColor(lightText || '#000000', false);
|
||||
} else {
|
||||
const isDark = (color: string) => this.isDark(color);
|
||||
const isLight = (color: string) => this.isLight(color);
|
||||
|
||||
return this.authService
|
||||
.getMyLabelPolicy()
|
||||
.then((lpresp) => {
|
||||
const labelpolicy = lpresp.policy;
|
||||
|
||||
const darkPrimary = labelpolicy?.primaryColorDark || '#bbbafa';
|
||||
const lightPrimary = labelpolicy?.primaryColor || '#5469d4';
|
||||
|
||||
const darkWarn = labelpolicy?.warnColorDark || '#ff3b5b';
|
||||
const lightWarn = labelpolicy?.warnColor || '#cd3d56';
|
||||
|
||||
let darkBackground = labelpolicy?.backgroundColorDark;
|
||||
let lightBackground = labelpolicy?.backgroundColor;
|
||||
|
||||
let darkText = labelpolicy?.fontColorDark ?? '#ffffff';
|
||||
let lightText = labelpolicy?.fontColor ?? '#000000';
|
||||
|
||||
this.savePrimaryColor(darkPrimary, true);
|
||||
this.savePrimaryColor(lightPrimary, false);
|
||||
|
||||
this.saveWarnColor(darkWarn, true);
|
||||
this.saveWarnColor(lightWarn, false);
|
||||
|
||||
if (darkBackground && !isDark(darkBackground)) {
|
||||
console.info(
|
||||
`Background (${darkBackground}) is not dark enough for a dark theme. Falling back to zitadel background`,
|
||||
);
|
||||
darkBackground = '#111827';
|
||||
}
|
||||
this.saveBackgroundColor(darkBackground || '#111827', true);
|
||||
|
||||
if (lightBackground && !isLight(lightBackground)) {
|
||||
console.info(
|
||||
`Background (${lightBackground}) is not light enough for a light theme. Falling back to zitadel background`,
|
||||
);
|
||||
lightBackground = '#fafafa';
|
||||
}
|
||||
this.saveBackgroundColor(lightBackground || '#fafafa', false);
|
||||
|
||||
if (darkText && !isLight(darkText)) {
|
||||
console.info(
|
||||
`Text color (${darkText}) is not light enough for a dark theme. Falling back to zitadel's text color`,
|
||||
);
|
||||
darkText = '#ffffff';
|
||||
}
|
||||
this.saveTextColor(darkText || '#ffffff', true);
|
||||
|
||||
if (lightText && !isDark(lightText)) {
|
||||
console.info(
|
||||
`Text color (${lightText}) is not dark enough for a light theme. Falling back to zitadel's text color`,
|
||||
);
|
||||
lightText = '#000000';
|
||||
}
|
||||
this.saveTextColor(lightText || '#000000', false);
|
||||
|
||||
if (labelpolicy) {
|
||||
return labelpolicy;
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('could not load private labelling policy!', error);
|
||||
this.setDefaultColors();
|
||||
return Promise.reject();
|
||||
});
|
||||
this.setDefaultColors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user