mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 02:37:31 +00:00
feat: add attribute to only enable specific themes (#6798)
* feat: enable only specific themes in label policy * feat: enable only specific themes in label policy * feat: enable only specific themes in label policy * feat: enable only specific themes in label policy * add management in console * pass enabledTheme * render login ui based on enabled theme * add in branding / settings service and name consistently * update console to latest proto state * fix console linting * fix linting * cleanup * add translations --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package admin
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||
)
|
||||
|
||||
func updateLabelPolicyToDomain(policy *admin_pb.UpdateLabelPolicyRequest) *domain.LabelPolicy {
|
||||
@@ -17,5 +18,21 @@ func updateLabelPolicyToDomain(policy *admin_pb.UpdateLabelPolicyRequest) *domai
|
||||
FontColorDark: policy.FontColorDark,
|
||||
HideLoginNameSuffix: policy.HideLoginNameSuffix,
|
||||
DisableWatermark: policy.DisableWatermark,
|
||||
ThemeMode: themeModeToDomain(policy.ThemeMode),
|
||||
}
|
||||
}
|
||||
|
||||
func themeModeToDomain(theme policy_pb.ThemeMode) domain.LabelPolicyThemeMode {
|
||||
switch theme {
|
||||
case policy_pb.ThemeMode_THEME_MODE_AUTO:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
case policy_pb.ThemeMode_THEME_MODE_DARK:
|
||||
return domain.LabelPolicyThemeDark
|
||||
case policy_pb.ThemeMode_THEME_MODE_LIGHT:
|
||||
return domain.LabelPolicyThemeLight
|
||||
case policy_pb.ThemeMode_THEME_MODE_UNSPECIFIED:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
default:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package management
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
policy_pb "github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||
)
|
||||
|
||||
func AddLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.LabelPolicy {
|
||||
@@ -17,6 +18,22 @@ func AddLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.Labe
|
||||
FontColorDark: p.FontColorDark,
|
||||
HideLoginNameSuffix: p.HideLoginNameSuffix,
|
||||
DisableWatermark: p.DisableWatermark,
|
||||
ThemeMode: themeModeToDomain(p.ThemeMode),
|
||||
}
|
||||
}
|
||||
|
||||
func themeModeToDomain(theme policy_pb.ThemeMode) domain.LabelPolicyThemeMode {
|
||||
switch theme {
|
||||
case policy_pb.ThemeMode_THEME_MODE_AUTO:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
case policy_pb.ThemeMode_THEME_MODE_DARK:
|
||||
return domain.LabelPolicyThemeDark
|
||||
case policy_pb.ThemeMode_THEME_MODE_LIGHT:
|
||||
return domain.LabelPolicyThemeLight
|
||||
case policy_pb.ThemeMode_THEME_MODE_UNSPECIFIED:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
default:
|
||||
return domain.LabelPolicyThemeAuto
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +49,6 @@ func updateLabelPolicyToDomain(p *mgmt_pb.UpdateCustomLabelPolicyRequest) *domai
|
||||
FontColorDark: p.FontColorDark,
|
||||
HideLoginNameSuffix: p.HideLoginNameSuffix,
|
||||
DisableWatermark: p.DisableWatermark,
|
||||
ThemeMode: themeModeToDomain(p.ThemeMode),
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ func ModelLabelPolicyToPb(policy *query.LabelPolicy, assetPrefix string) *policy
|
||||
|
||||
DisableWatermark: policy.WatermarkDisabled,
|
||||
HideLoginNameSuffix: policy.HideLoginNameSuffix,
|
||||
ThemeMode: themeModeToPb(policy.ThemeMode),
|
||||
Details: object.ToViewDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.CreationDate,
|
||||
@@ -34,3 +35,16 @@ func ModelLabelPolicyToPb(policy *query.LabelPolicy, assetPrefix string) *policy
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func themeModeToPb(theme domain.LabelPolicyThemeMode) policy_pb.ThemeMode {
|
||||
switch theme {
|
||||
case domain.LabelPolicyThemeAuto:
|
||||
return policy_pb.ThemeMode_THEME_MODE_AUTO
|
||||
case domain.LabelPolicyThemeDark:
|
||||
return policy_pb.ThemeMode_THEME_MODE_DARK
|
||||
case domain.LabelPolicyThemeLight:
|
||||
return policy_pb.ThemeMode_THEME_MODE_LIGHT
|
||||
default:
|
||||
return policy_pb.ThemeMode_THEME_MODE_AUTO
|
||||
}
|
||||
}
|
||||
|
@@ -107,6 +107,20 @@ func brandingSettingsToPb(current *query.LabelPolicy, assetPrefix string) *setti
|
||||
DisableWatermark: current.WatermarkDisabled,
|
||||
HideLoginNameSuffix: current.HideLoginNameSuffix,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
ThemeMode: themeModeToPb(current.ThemeMode),
|
||||
}
|
||||
}
|
||||
|
||||
func themeModeToPb(themeMode domain.LabelPolicyThemeMode) settings.ThemeMode {
|
||||
switch themeMode {
|
||||
case domain.LabelPolicyThemeAuto:
|
||||
return settings.ThemeMode_THEME_MODE_AUTO
|
||||
case domain.LabelPolicyThemeLight:
|
||||
return settings.ThemeMode_THEME_MODE_LIGHT
|
||||
case domain.LabelPolicyThemeDark:
|
||||
return settings.ThemeMode_THEME_MODE_DARK
|
||||
default:
|
||||
return settings.ThemeMode_THEME_MODE_AUTO
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -258,6 +258,7 @@ func Test_brandingSettingsToPb(t *testing.T) {
|
||||
FontURL: "fonts",
|
||||
WatermarkDisabled: true,
|
||||
HideLoginNameSuffix: true,
|
||||
ThemeMode: domain.LabelPolicyThemeDark,
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.BrandingSettings{
|
||||
@@ -281,6 +282,7 @@ func Test_brandingSettingsToPb(t *testing.T) {
|
||||
DisableWatermark: true,
|
||||
HideLoginNameSuffix: true,
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
ThemeMode: settings.ThemeMode_THEME_MODE_DARK,
|
||||
}
|
||||
|
||||
got := brandingSettingsToPb(arg, "http://example.com")
|
||||
|
@@ -381,8 +381,6 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, titleI
|
||||
Title: title,
|
||||
Description: description,
|
||||
Theme: l.getTheme(r),
|
||||
ThemeMode: l.getThemeMode(r),
|
||||
DarkMode: l.isDarkMode(r),
|
||||
PrivateLabelingOrgID: l.getPrivateLabelingID(r, authReq),
|
||||
OrgID: l.getOrgID(r, authReq),
|
||||
OrgName: l.getOrgName(authReq),
|
||||
@@ -412,6 +410,9 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, titleI
|
||||
}
|
||||
privacyPolicy = policy.ToDomain()
|
||||
}
|
||||
baseData.ThemeMode = l.getThemeMode(baseData.LabelPolicy)
|
||||
baseData.ThemeClass = l.getThemeClass(r, baseData.LabelPolicy)
|
||||
baseData.DarkMode = l.isDarkMode(r, baseData.LabelPolicy)
|
||||
baseData = l.setLinksOnBaseData(baseData, privacyPolicy)
|
||||
return baseData
|
||||
}
|
||||
@@ -480,14 +481,22 @@ func (l *Login) getTheme(r *http.Request) string {
|
||||
return "zitadel"
|
||||
}
|
||||
|
||||
func (l *Login) getThemeMode(r *http.Request) string {
|
||||
if l.isDarkMode(r) {
|
||||
// getThemeClass returns the css class for the login html.
|
||||
// Possible values are `lgn-light-theme` and `lgn-dark-theme` and are based on the policy first
|
||||
// and if it's set to auto the cookie is checked.
|
||||
func (l *Login) getThemeClass(r *http.Request, policy *domain.LabelPolicy) string {
|
||||
if l.isDarkMode(r, policy) {
|
||||
return "lgn-dark-theme"
|
||||
}
|
||||
return "lgn-light-theme"
|
||||
}
|
||||
|
||||
func (l *Login) isDarkMode(r *http.Request) bool {
|
||||
// isDarkMode checks policy first and if not set to specifically use dark or light only,
|
||||
// it will also check the cookie.
|
||||
func (l *Login) isDarkMode(r *http.Request, policy *domain.LabelPolicy) bool {
|
||||
if mode := l.getThemeMode(policy); mode != domain.LabelPolicyThemeAuto {
|
||||
return mode == domain.LabelPolicyThemeDark
|
||||
}
|
||||
cookie, err := r.Cookie("mode")
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -495,6 +504,13 @@ func (l *Login) isDarkMode(r *http.Request) bool {
|
||||
return strings.HasSuffix(cookie.Value, "dark")
|
||||
}
|
||||
|
||||
func (l *Login) getThemeMode(policy *domain.LabelPolicy) domain.LabelPolicyThemeMode {
|
||||
if policy != nil {
|
||||
return policy.ThemeMode
|
||||
}
|
||||
return domain.LabelPolicyThemeAuto
|
||||
}
|
||||
|
||||
func (l *Login) getOrgID(r *http.Request, authReq *domain.AuthRequest) string {
|
||||
if authReq == nil {
|
||||
return r.FormValue(queryOrgID)
|
||||
@@ -607,7 +623,8 @@ type baseData struct {
|
||||
Title string
|
||||
Description string
|
||||
Theme string
|
||||
ThemeMode string
|
||||
ThemeMode domain.LabelPolicyThemeMode
|
||||
ThemeClass string
|
||||
DarkMode bool
|
||||
PrivateLabelingOrgID string
|
||||
OrgID string
|
||||
|
@@ -1,10 +1,16 @@
|
||||
const usesDarkTheme = hasDarkModeOverwriteCookie() || (!hasLightModeOverwriteCookie() && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
if (usesDarkTheme) {
|
||||
document.documentElement.classList.replace('lgn-light-theme', 'lgn-dark-theme');
|
||||
writeModeCookie('dark');
|
||||
} else {
|
||||
document.documentElement.classList.replace('lgn-dark-theme', 'lgn-light-theme');
|
||||
writeModeCookie('light');
|
||||
if (isAutoMode()) {
|
||||
const usesDarkTheme = hasDarkModeOverwriteCookie() || (!hasLightModeOverwriteCookie() && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
if (usesDarkTheme) {
|
||||
document.documentElement.classList.replace('lgn-light-theme', 'lgn-dark-theme');
|
||||
writeModeCookie('dark');
|
||||
} else {
|
||||
document.documentElement.classList.replace('lgn-dark-theme', 'lgn-light-theme');
|
||||
writeModeCookie('light');
|
||||
}
|
||||
}
|
||||
|
||||
function isAutoMode() {
|
||||
return document.documentElement.dataset["themeMode"] === "0"
|
||||
}
|
||||
|
||||
function hasDarkModeOverwriteCookie() {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{{define "main-top"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Lang }}" class="{{.ThemeMode}}">
|
||||
<html lang="{{ .Lang }}" class="{{.ThemeClass}}" data-theme-mode="{{.ThemeMode}}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
Reference in New Issue
Block a user