mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 19:37:24 +00:00
feat: label policy (#1708)
* feat: label policy proto extension * feat: label policy and activate event * feat: label policy asset events * feat: label policy asset commands * feat: add storage key * feat: storage key validation * feat: label policy asset tests * feat: label policy query side * feat: avatar * feat: avatar event * feat: human avatar * feat: avatar read side * feat: font on iam label policy * feat: label policy font * feat: possiblity to create bucket on put file * uplaoder * login policy logo * set bucket prefix * feat: avatar upload * feat: avatar upload * feat: use assets on command side * feat: fix human avatar removed event * feat: remove human avatar * feat: mock asset storage * feat: remove human avatar * fix(operator): add configuration of asset storage to zitadel operator * feat(console): private labeling policy (#1697) * private labeling component, routing, preview * font, colors, upload, i18n * show logo * fix: uniqueness (#1710) * fix: uniqueconstraint to lower * feat: change org * feat: org change test * feat: change org * fix: tests * fix: handle domain claims correctly * feat: update org Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> * fix: handle domain claimed event correctly for service users (#1711) * fix: handle domain claimed event correctly on user view * fix: ignore domain claimed events for email notifications * fix: change org * handle org changed in read models correctly * fix: change org in user grant handler Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> * fix: correct value (#1695) * docs(api): correct link (#1712) * upload service Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> Co-authored-by: Florian Forster <florian@caos.ch> * feat: fix tests, * feat: remove assets from label policy * fix npm, set environment * lint ts * remove stylelinting * fix(operator): add mapping for console with changed unit tests * fix(operator): add secrets as env variables to pod * feat: remove human avatar * fix(operator): add secrets as env variables to pod * feat: map label policy * feat: labelpolicy, admin, mgmt, adv settings (#1715) * fetch label policy, mgmt, admin service * feat: advanced beh, links, add, update * lint ts * feat: watermark * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: remove human avatar * feat: custom css * css * css * css * css * css * getobject * feat: dynamic handler * feat: varibale css * content info * css overwrite * feat: variablen css * feat: generate css file * feat: dark mode * feat: dark mode * fix logo css * feat: upload logos * dark mode with cookie * feat: handle images in login * avatar css and begin font * feat: avatar * feat: user avatar * caching of static assets in login * add avatar.js to main.html * feat: header dont show logo if no url * feat: label policy colors * feat: mock asset storage * feat: mock asset storage * feat: fix tests * feat: user avatar * feat: header logo * avatar * avatar * make it compatible with go 1.15 * feat: remove unused logos * fix handler * fix: styling error handling * fonts * fix: download func * switch to mux * fix: change upload api to assets * fix build * fix: download avatar * fix: download logos * fix: my avatar * font * fix: remove error msg popup possibility * fix: docs * fix: svalidate colors * rem msg popup from frontend * fix: email with private labeling * fix: tests * fix: email templates * fix: change migration version * fix: fix duplicate imports * fix(console): assets, service url, upload, policy current and preview (#1781) * upload endpoint, layout * fetch current, preview, fix upload * cleanup private labeling * fix linting * begin generated asset handler * generate asset api in dockerfile * features for label policy * features for label policy * features * flag for asset generator * change asset generator flag * fix label policy view in grpc * fix: layout, activate policy (#1786) * theme switcher up on top * change layout * activate policy * feat(console): label policy back color, layout (#1788) * theme switcher up on top * change layout * activate policy * fix overwrite value fc * reset policy, reset service * autosave policy, preview desc, layout impv * layout, i18n * background colors, inject material styles * load images * clean, lint * fix layout * set custom hex * fix content size conversion * remove font format in generated css * fix features for assets * fix(console): label policy colors, image downloads, preview (#1804) * load images * colors, images binding * lint * refresh emitter * lint * propagate font colors * upload error handling * label policy feature check * add blob in csp for console * log * fix: feature edits for label policy, refresh state on upload (#1807) * show error on load image, stop spinner * fix merge * fix migration versions * fix assets * fix csp * fix background color * scss * fix build * lint scss * fix statik for console * fix features check for label policy * cleanup * lint * public links * fix notifications * public links * feat: merge main * feat: fix translation files * fix migration * set api domain * fix logo in email * font face in email * font face in email * validate assets on upload * cleanup * add missing translations * add missing translations Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Stefan Benz <stefan@caos.ch> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
parent
c0d9d86b09
commit
73d37459bb
@ -134,9 +134,13 @@ COPY --from=base /usr/local/bin /usr/local/bin/.
|
||||
COPY build/zitadel/generate-grpc.sh build/zitadel/generate-grpc.sh
|
||||
COPY internal/protoc internal/protoc
|
||||
COPY openapi/statik openapi/statik
|
||||
COPY internal/api/assets/generator internal/api/assets/generator
|
||||
COPY internal/config internal/config
|
||||
COPY internal/errors internal/errors
|
||||
|
||||
RUN build/zitadel/generate-grpc.sh \
|
||||
&& go generate openapi/statik/generate.go
|
||||
&& go generate openapi/statik/generate.go \
|
||||
&& go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/
|
||||
|
||||
|
||||
#######################
|
||||
@ -163,6 +167,9 @@ COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-a
|
||||
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/authoption/options.pb.go internal/protoc/protoc-gen-authoption/authoption/options.pb.go
|
||||
COPY --from=go-stub /go/src/github.com/caos/zitadel/docs/apis/proto docs/docs/apis/proto
|
||||
|
||||
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/api/assets/authz.go ./internal/api/assets/authz.go
|
||||
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/api/assets/router.go ./internal/api/assets/router.go
|
||||
|
||||
|
||||
#######################
|
||||
## Go test
|
||||
|
@ -45,6 +45,7 @@ ZITADEL_AUTHORIZE=http://localhost:50002/oauth/v2
|
||||
ZITADEL_OAUTH=http://localhost:50002/oauth/v2
|
||||
ZITADEL_CONSOLE=http://localhost:4200
|
||||
ZITADEL_COOKIE_DOMAIN=localhost
|
||||
ZITADEL_API_DOMAIN=http://localhost:50002
|
||||
|
||||
#caching is used in UI's and API's
|
||||
ZITADEL_CACHE_MAXAGE=12h
|
||||
|
@ -5,8 +5,10 @@ import (
|
||||
"flag"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
admin_es "github.com/caos/zitadel/internal/admin/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/api"
|
||||
"github.com/caos/zitadel/internal/api/assets"
|
||||
internal_authz "github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/admin"
|
||||
"github.com/caos/zitadel/internal/api/grpc/auth"
|
||||
@ -20,11 +22,13 @@ import (
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
mgmt_es "github.com/caos/zitadel/internal/management/repository/eventsourcing"
|
||||
"github.com/caos/zitadel/internal/notification"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/setup"
|
||||
"github.com/caos/zitadel/internal/static/s3"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
static_config "github.com/caos/zitadel/internal/static/config"
|
||||
metrics "github.com/caos/zitadel/internal/telemetry/metrics/config"
|
||||
tracing "github.com/caos/zitadel/internal/telemetry/tracing/config"
|
||||
"github.com/caos/zitadel/internal/ui"
|
||||
@ -37,7 +41,7 @@ type Config struct {
|
||||
Log logging.Config
|
||||
Tracing tracing.TracingConfig
|
||||
Metrics metrics.MetricsConfig
|
||||
AssetStorage s3.AssetStorage
|
||||
AssetStorage static_config.AssetStorageConfig
|
||||
InternalAuthZ internal_authz.Config
|
||||
SystemDefaults sd.SystemDefaults
|
||||
|
||||
@ -72,6 +76,7 @@ var (
|
||||
managementEnabled = flag.Bool("management", true, "enable management api")
|
||||
authEnabled = flag.Bool("auth", true, "enable auth api")
|
||||
oidcEnabled = flag.Bool("oidc", true, "enable oidc api")
|
||||
assetsEnabled = flag.Bool("assets", true, "enable assets api")
|
||||
loginEnabled = flag.Bool("login", true, "enable login ui")
|
||||
consoleEnabled = flag.Bool("console", true, "enable console ui")
|
||||
notificationEnabled = flag.Bool("notification", true, "enable notification handler")
|
||||
@ -114,11 +119,15 @@ func startZitadel(configPaths []string) {
|
||||
}
|
||||
authZRepo, err := authz.Start(ctx, conf.AuthZ, conf.InternalAuthZ, conf.SystemDefaults, queries)
|
||||
logging.Log("MAIN-s9KOw").OnError(err).Fatal("error starting authz repo")
|
||||
verifier := internal_authz.Start(authZRepo)
|
||||
esCommands, err := eventstore.StartWithUser(conf.EventstoreBase, conf.Commands.Eventstore)
|
||||
if err != nil {
|
||||
logging.Log("ZITAD-iRCMm").OnError(err).Fatal("cannot start eventstore for commands")
|
||||
}
|
||||
commands, err := command.StartCommands(esCommands, conf.SystemDefaults, conf.InternalAuthZ, authZRepo)
|
||||
store, err := conf.AssetStorage.Config.NewStorage()
|
||||
logging.Log("ZITAD-Bfhe2").OnError(err).Fatal("Unable to start asset storage")
|
||||
|
||||
commands, err := command.StartCommands(esCommands, conf.SystemDefaults, conf.InternalAuthZ, store, authZRepo)
|
||||
if err != nil {
|
||||
logging.Log("ZITAD-bmNiJ").OnError(err).Fatal("cannot start commands")
|
||||
}
|
||||
@ -128,21 +137,21 @@ func startZitadel(configPaths []string) {
|
||||
logging.Log("MAIN-9oRw6").OnError(err).Fatal("error starting auth repo")
|
||||
}
|
||||
|
||||
startAPI(ctx, conf, authZRepo, authRepo, commands, queries)
|
||||
startUI(ctx, conf, authRepo, commands, queries)
|
||||
startAPI(ctx, conf, verifier, authZRepo, authRepo, commands, queries, store)
|
||||
startUI(ctx, conf, authRepo, commands, queries, store)
|
||||
|
||||
if *notificationEnabled {
|
||||
notification.Start(ctx, conf.Notification, conf.SystemDefaults, commands)
|
||||
notification.Start(ctx, conf.Notification, conf.SystemDefaults, commands, store != nil)
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
logging.Log("MAIN-s8d2h").Info("stopping zitadel")
|
||||
}
|
||||
|
||||
func startUI(ctx context.Context, conf *Config, authRepo *auth_es.EsRepository, command *command.Commands, query *query.Queries) {
|
||||
func startUI(ctx context.Context, conf *Config, authRepo *auth_es.EsRepository, command *command.Commands, query *query.Queries, staticStorage static.Storage) {
|
||||
uis := ui.Create(conf.UI)
|
||||
if *loginEnabled {
|
||||
login, prefix := login.Start(conf.UI.Login, command, query, authRepo, conf.SystemDefaults, *localDevMode)
|
||||
login, prefix := login.Start(conf.UI.Login, command, query, authRepo, staticStorage, conf.SystemDefaults, *localDevMode)
|
||||
uis.RegisterHandler(prefix, login.Handler())
|
||||
}
|
||||
if *consoleEnabled {
|
||||
@ -153,12 +162,12 @@ func startUI(ctx context.Context, conf *Config, authRepo *auth_es.EsRepository,
|
||||
uis.Start(ctx)
|
||||
}
|
||||
|
||||
func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsRepository, authRepo *auth_es.EsRepository, command *command.Commands, query *query.Queries) {
|
||||
func startAPI(ctx context.Context, conf *Config, verifier *internal_authz.TokenVerifier, authZRepo *authz_repo.EsRepository, authRepo *auth_es.EsRepository, command *command.Commands, query *query.Queries, static static.Storage) {
|
||||
roles := make([]string, len(conf.InternalAuthZ.RolePermissionMappings))
|
||||
for i, role := range conf.InternalAuthZ.RolePermissionMappings {
|
||||
roles[i] = role.Role
|
||||
}
|
||||
repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, roles)
|
||||
repo, err := admin_es.Start(ctx, conf.Admin, conf.SystemDefaults, static, roles, *localDevMode)
|
||||
logging.Log("API-D42tq").OnError(err).Fatal("error starting auth repo")
|
||||
|
||||
apis := api.Create(conf.API, conf.InternalAuthZ, authZRepo, authRepo, repo, conf.SystemDefaults)
|
||||
@ -166,9 +175,9 @@ func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsReposit
|
||||
if *adminEnabled {
|
||||
apis.RegisterServer(ctx, admin.CreateServer(command, query, repo, conf.SystemDefaults.Domain))
|
||||
}
|
||||
managementRepo, err := mgmt_es.Start(conf.Mgmt, conf.SystemDefaults, roles, query, static)
|
||||
logging.Log("API-Gd2qq").OnError(err).Fatal("error starting management repo")
|
||||
if *managementEnabled {
|
||||
managementRepo, err := mgmt_es.Start(conf.Mgmt, conf.SystemDefaults, roles, query)
|
||||
logging.Log("API-Gd2qq").OnError(err).Fatal("error starting management repo")
|
||||
apis.RegisterServer(ctx, management.CreateServer(command, query, managementRepo, conf.SystemDefaults))
|
||||
}
|
||||
if *authEnabled {
|
||||
@ -178,6 +187,10 @@ func startAPI(ctx context.Context, conf *Config, authZRepo *authz_repo.EsReposit
|
||||
op := oidc.NewProvider(ctx, conf.API.OIDC, command, query, authRepo, conf.SystemDefaults.KeyConfig.EncryptionConfig, *localDevMode)
|
||||
apis.RegisterHandler("/oauth/v2", op.HttpHandler())
|
||||
}
|
||||
if *assetsEnabled {
|
||||
assetsHandler := assets.NewHandler(command, verifier, conf.InternalAuthZ, id.SonyFlakeGenerator, static, managementRepo)
|
||||
apis.RegisterHandler("/assets/v1", assetsHandler)
|
||||
}
|
||||
|
||||
openAPIHandler, err := openapi.Start()
|
||||
logging.Log("ZITAD-8pRk1").OnError(err).Fatal("Unable to start openapi handler")
|
||||
@ -196,7 +209,7 @@ func startSetup(configPaths []string) {
|
||||
es, err := eventstore.Start(conf.Eventstore)
|
||||
logging.Log("MAIN-Ddt3").OnError(err).Fatal("cannot start eventstore")
|
||||
|
||||
commands, err := command.StartCommands(es, conf.SystemDefaults, conf.InternalAuthZ, nil)
|
||||
commands, err := command.StartCommands(es, conf.SystemDefaults, conf.InternalAuthZ, nil, nil)
|
||||
logging.Log("MAIN-dsjrr").OnError(err).Fatal("cannot start command side")
|
||||
|
||||
err = setup.Execute(ctx, conf.SetUp, conf.SystemDefaults.IamID, commands)
|
||||
|
File diff suppressed because one or more lines are too long
@ -19,6 +19,7 @@ AssetStorage:
|
||||
SecretAccessKey: $ZITADEL_ASSET_STORAGE_SECRET_ACCESS_KEY
|
||||
SSL: $ZITADEL_ASSET_STORAGE_SSL
|
||||
Location: $ZITADEL_ASSET_STORAGE_LOCATION
|
||||
BucketPrefix: $ZITADEL_ASSET_STORAGE_BUCKET_PREFIX
|
||||
|
||||
Metrics:
|
||||
Type: 'otel'
|
||||
@ -279,6 +280,10 @@ UI:
|
||||
Cache:
|
||||
MaxAge: $ZITADEL_CACHE_MAXAGE
|
||||
SharedMaxAge: $ZITADEL_CACHE_SHARED_MAXAGE
|
||||
StaticCache:
|
||||
Type: bigcache
|
||||
Config:
|
||||
MaxCacheSizeInByte: 52428800 #50MB
|
||||
Console:
|
||||
EnvOverwriteDir: $ZITADEL_CONSOLE_ENV_DIR
|
||||
ShortCache:
|
||||
@ -290,6 +295,7 @@ UI:
|
||||
CSPDomain: $ZITADEL_DEFAULT_DOMAIN
|
||||
|
||||
Notification:
|
||||
APIDomain: $ZITADEL_API_DOMAIN
|
||||
Repository:
|
||||
DefaultLanguage: 'de'
|
||||
Domain: $ZITADEL_DEFAULT_DOMAIN
|
||||
|
@ -26,6 +26,7 @@ import { from, Observable } from 'rxjs';
|
||||
import { OnboardingModule } from 'src/app/modules/onboarding/onboarding.module';
|
||||
import { RegExpPipeModule } from 'src/app/pipes/regexp-pipe/regexp-pipe.module';
|
||||
import { SubscriptionService } from 'src/app/services/subscription.service';
|
||||
import { UploadService } from 'src/app/services/upload.service';
|
||||
|
||||
import { environment } from '../environments/environment';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
@ -180,6 +181,7 @@ const authConfig: AuthConfig = {
|
||||
AuthenticationService,
|
||||
GrpcAuthService,
|
||||
SubscriptionService,
|
||||
UploadService,
|
||||
{ provide: 'windowObject', useValue: window },
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
|
28
console/src/app/directives/dropzone/dropzone.directive.ts
Normal file
28
console/src/app/directives/dropzone/dropzone.directive.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[cnslDropzone]',
|
||||
})
|
||||
export class DropzoneDirective {
|
||||
@Output() dropped: EventEmitter<FileList> = new EventEmitter<FileList>();
|
||||
@Output() hovered: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop($event: DragEvent): void {
|
||||
$event.preventDefault();
|
||||
this.dropped.emit($event.dataTransfer?.files);
|
||||
this.hovered.emit(false);
|
||||
}
|
||||
|
||||
@HostListener('dragover', ['$event'])
|
||||
onDragOver($event: any): void {
|
||||
$event.preventDefault();
|
||||
this.hovered.emit(true);
|
||||
}
|
||||
|
||||
@HostListener('dragleave', ['$event'])
|
||||
onDragLeave($event: any): void {
|
||||
$event.preventDefault();
|
||||
this.hovered.emit(false);
|
||||
}
|
||||
}
|
19
console/src/app/directives/dropzone/dropzone.module.ts
Normal file
19
console/src/app/directives/dropzone/dropzone.module.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { DropzoneDirective } from './dropzone.directive';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DropzoneDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
DropzoneDirective,
|
||||
],
|
||||
})
|
||||
export class DropzoneModule { }
|
@ -120,9 +120,17 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICY' | translate}}</span>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicy"
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyPrivateLabel"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="features.labelPolicyWatermark"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) == false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
@ -158,7 +158,8 @@ export class FeaturesComponent implements OnDestroy {
|
||||
req.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
req.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
req.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
req.setLabelPolicy(this.features.labelPolicy);
|
||||
req.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
req.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
req.setCustomDomain(this.features.customDomain);
|
||||
|
||||
this.adminService.setOrgFeatures(req).then(() => {
|
||||
@ -177,7 +178,8 @@ export class FeaturesComponent implements OnDestroy {
|
||||
dreq.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
dreq.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
dreq.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
dreq.setLabelPolicy(this.features.labelPolicy);
|
||||
dreq.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
dreq.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
dreq.setCustomDomain(this.features.customDomain);
|
||||
|
||||
this.adminService.setDefaultFeatures(dreq).then(() => {
|
||||
|
@ -4,6 +4,7 @@
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.ng-untouched {
|
||||
.cnsl-error {
|
||||
@ -29,6 +30,10 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input {
|
||||
color: mat.get-color-from-palette($foreground, text);
|
||||
}
|
||||
|
||||
// Wrapper for the hints and error messages.
|
||||
.cnsl-form-field-subscript-wrapper {
|
||||
position: absolute;
|
||||
|
@ -8,17 +8,18 @@
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: .5rem;
|
||||
margin: 0 -.5rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.step {
|
||||
min-width: 220px;
|
||||
max-width: 280px;
|
||||
padding: 1rem;
|
||||
margin: 0 .5rem;
|
||||
border: 1px solid var(--grey);
|
||||
margin: 1rem .5rem;
|
||||
border: 1px solid #8795a150;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -26,6 +27,10 @@
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
|
||||
@media only screen and (min-width: 899px) {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
@ -46,14 +51,6 @@
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: LabelPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class LabelPolicyRoutingModule { }
|
@ -1,19 +0,0 @@
|
||||
<app-detail-layout [backRouterLink]="['/iam/policies']" [title]="'POLICY.LABEL.TITLE' | translate"
|
||||
[description]="'POLICY.LABEL.DESCRIPTION' | translate">
|
||||
|
||||
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||
|
||||
<div class="content" *ngIf="labelData">
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl
|
||||
[(ngModel)]="labelData.hideLoginNameSuffix">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
|
||||
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
|
||||
</app-detail-layout>
|
@ -1,23 +0,0 @@
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.default {
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
padding: .5rem 4rem;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
describe('LabelPolicyComponent', () => {
|
||||
let component: LabelPolicyComponent;
|
||||
let fixture: ComponentFixture<LabelPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LabelPolicyComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LabelPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,71 +0,0 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { GetLabelPolicyResponse, UpdateLabelPolicyRequest } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import { IAM_COMPLEXITY_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-label-policy',
|
||||
templateUrl: './label-policy.component.html',
|
||||
styleUrls: ['./label-policy.component.scss'],
|
||||
})
|
||||
export class LabelPolicyComponent implements OnDestroy {
|
||||
public labelData!: LabelPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: CnslLinks[] = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private adminService: AdminService,
|
||||
) {
|
||||
this.route.params.subscribe(() => {
|
||||
this.getData().then(data => {
|
||||
if (data?.policy) {
|
||||
this.labelData = data.policy;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetLabelPolicyResponse.AsObject> {
|
||||
return this.adminService.getLabelPolicy();
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
req.setPrimaryColor(this.labelData.primaryColor);
|
||||
req.setSecondaryColor(this.labelData.secondaryColor);
|
||||
req.setHideLoginNameSuffix(this.labelData.hideLoginNameSuffix);
|
||||
this.adminService.updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.labelData) {
|
||||
return (this.labelData as LabelPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { LinksModule } from '../../links/links.module';
|
||||
import { LabelPolicyRoutingModule } from './label-policy-routing.module';
|
||||
import { LabelPolicyComponent } from './label-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [LabelPolicyComponent],
|
||||
imports: [
|
||||
LabelPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
LinksModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
})
|
||||
export class LabelPolicyModule { }
|
@ -22,10 +22,11 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { AddIdpDialogComponent } from './add-idp-dialog/add-idp-dialog.component';
|
||||
@ -69,6 +70,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
@ -80,7 +82,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
@ -96,7 +98,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
if (resp.policy) {
|
||||
this.loginData = resp.policy;
|
||||
this.loading = false;
|
||||
this.disabled = this.isDefault ?? false;
|
||||
this.disabled = ((this.loginData as LoginPolicy.AsObject)?.isDefault) ?? false;
|
||||
}
|
||||
});
|
||||
this.getIdps().then(resp => {
|
||||
|
@ -13,141 +13,143 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
selector: 'app-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
})
|
||||
export class OrgIamPolicyComponent implements OnDestroy {
|
||||
private managementService!: ManagementService;
|
||||
public serviceType!: PolicyComponentServiceType;
|
||||
private managementService!: ManagementService;
|
||||
public serviceType!: PolicyComponentServiceType;
|
||||
|
||||
public iamData!: OrgIAMPolicy.AsObject;
|
||||
public iamData!: OrgIAMPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
private org!: Org.AsObject;
|
||||
private sub: Subscription = new Subscription();
|
||||
private org!: Org.AsObject;
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: Array<CnslLinks> = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private sessionStorage: StorageService,
|
||||
private injector: Injector,
|
||||
private adminService: AdminService,
|
||||
) {
|
||||
const temporg = this.sessionStorage.getItem('organization') as Org.AsObject;
|
||||
if (temporg) {
|
||||
this.org = temporg;
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public nextLinks: Array<CnslLinks> = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private sessionStorage: StorageService,
|
||||
private injector: Injector,
|
||||
private adminService: AdminService,
|
||||
) {
|
||||
const temporg = this.sessionStorage.getItem('organization') as Org.AsObject;
|
||||
if (temporg) {
|
||||
this.org = temporg;
|
||||
}
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
} else {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
}
|
||||
return this.route.params;
|
||||
})).subscribe(_ => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp?.policy) {
|
||||
this.iamData = resp.policy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_COMPLEXITY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
];
|
||||
} else {
|
||||
this.nextLinks = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
];
|
||||
}
|
||||
return this.route.params;
|
||||
})).subscribe(_ => {
|
||||
this.fetchData();
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then(resp => {
|
||||
if (resp?.policy) {
|
||||
this.iamData = resp.policy;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
this.adminService.addCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
this.adminService.updateCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
this.adminService.updateOrgIAMPolicy(
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.adminService.resetCustomOrgIAMPolicyToDefault(this.org.id).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.iamData as OrgIAMPolicy.AsObject).isDefault) {
|
||||
this.adminService.addCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
this.adminService.updateCustomOrgIAMPolicy(
|
||||
this.org.id,
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
this.adminService.updateOrgIAMPolicy(
|
||||
this.iamData.userLoginMustBeDomain,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
this.adminService.resetCustomOrgIAMPolicyToDefault(this.org.id).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.iamData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.iamData as OrgIAMPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse,
|
||||
GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
|
||||
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
@ -15,163 +15,165 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import {
|
||||
IAM_LABEL_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
} from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-policy',
|
||||
templateUrl: './password-complexity-policy.component.html',
|
||||
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||
selector: 'app-password-policy',
|
||||
templateUrl: './password-complexity-policy.component.html',
|
||||
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||
})
|
||||
export class PasswordComplexityPolicyComponent implements OnDestroy {
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public complexityData!: PasswordComplexityPolicy.AsObject;
|
||||
public complexityData!: PasswordComplexityPolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [];
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
this.nextLinks = [
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_LABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
this.nextLinks = [
|
||||
ORG_IAM_POLICY_LINK,
|
||||
ORG_LOGIN_POLICY_LINK,
|
||||
ORG_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
this.nextLinks = [
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
IAM_PRIVATELABEL_LINK,
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData().then(data => {
|
||||
if (data.policy) {
|
||||
this.complexityData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordComplexityPolicyResponse.AsObject | AdminGetPasswordComplexityPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordComplexityPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordComplexityPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getData().then(data => {
|
||||
if (data.policy) {
|
||||
this.complexityData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
public incrementLength(): void {
|
||||
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
||||
this.complexityData.minLength++;
|
||||
}
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordComplexityPolicyResponse.AsObject | AdminGetPasswordComplexityPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordComplexityPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordComplexityPolicy();
|
||||
}
|
||||
public decrementLength(): void {
|
||||
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
||||
this.complexityData.minLength--;
|
||||
}
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordComplexityPolicy(
|
||||
|
||||
public incrementLength(): void {
|
||||
if (this.complexityData?.minLength !== undefined && this.complexityData?.minLength <= 72) {
|
||||
this.complexityData.minLength++;
|
||||
}
|
||||
}
|
||||
|
||||
public decrementLength(): void {
|
||||
if (this.complexityData?.minLength && this.complexityData?.minLength > 1) {
|
||||
this.complexityData.minLength--;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordComplexityPolicy(
|
||||
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
(this.service as ManagementService).updateCustomPasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.complexityData as PasswordComplexityPolicy.AsObject).isDefault;
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
(this.service as ManagementService).updateCustomPasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordComplexityPolicy(
|
||||
this.complexityData.hasLowercase,
|
||||
this.complexityData.hasUppercase,
|
||||
this.complexityData.hasNumber,
|
||||
this.complexityData.hasSymbol,
|
||||
this.complexityData.minLength,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.complexityData as PasswordComplexityPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
export enum PolicyComponentType {
|
||||
LOCKOUT = 'lockout',
|
||||
AGE = 'age',
|
||||
COMPLEXITY = 'complexity',
|
||||
IAM = 'iam',
|
||||
LOGIN = 'login',
|
||||
LABEL = 'label',
|
||||
LOCKOUT = 'lockout',
|
||||
AGE = 'age',
|
||||
COMPLEXITY = 'complexity',
|
||||
IAM = 'iam',
|
||||
LOGIN = 'login',
|
||||
PRIVATELABEL = 'privatelabel',
|
||||
}
|
||||
export enum PolicyComponentServiceType {
|
||||
MGMT = 'mgmt',
|
||||
ADMIN = 'admin',
|
||||
MGMT = 'mgmt',
|
||||
ADMIN = 'admin',
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<div class="form-row">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{name}} (current {{color}})</cnsl-label>
|
||||
<input cnslInput [(ngModel)]="previewColor" (keydown.enter)="name ? emitPreview(previewColor) : null" />
|
||||
</cnsl-form-field>
|
||||
<button matTooltip="{{'ACTIONS.SET' | translate}}" (click)="emitPreview(previewColor)" mat-icon-button><mat-icon>check</mat-icon></button>
|
||||
</div>
|
||||
<div class="color-selector">
|
||||
<div *ngFor="let c of colors" class="circle mat-elevation-z3"
|
||||
[ngClass]="{'active': color == c.color || previewColor == c.color}"
|
||||
(click)="emitPreview(c.color)" [ngStyle]="{'background-color': c.color}">
|
||||
<span *ngIf="previewColor == c.color">P</span>
|
||||
<span *ngIf="color == c.color">C</span>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,52 @@
|
||||
.title {
|
||||
// font-size: 1rem;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
|
||||
&.border {
|
||||
border-top: 1px solid var(--grey);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-bottom: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.25rem;
|
||||
width: 100%;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.circle {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background-color: #cd5c5c;
|
||||
border-radius: 50%;
|
||||
margin: .25rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
span {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 3px solid #ffffff90;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ColorComponent } from './color.component';
|
||||
|
||||
describe('ColorComponent', () => {
|
||||
let component: ColorComponent;
|
||||
let fixture: ComponentFixture<ColorComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ColorComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ColorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,118 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
import { ColorType } from '../private-labeling-policy.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-color',
|
||||
templateUrl: './color.component.html',
|
||||
styleUrls: ['./color.component.scss'],
|
||||
})
|
||||
export class ColorComponent implements OnInit {
|
||||
public PRIMARY: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'red', color: '#f44336' },
|
||||
{ name: 'pink', color: '#e91e63' },
|
||||
{ name: 'purple', color: '#9c27b0' },
|
||||
{ name: 'deeppurple', color: '#673ab7' },
|
||||
{ name: 'indigo', color: '#3f51b5' },
|
||||
{ name: 'blue', color: '#2196f3' },
|
||||
{ name: 'lightblue', color: '#03a9f4' },
|
||||
{ name: 'cyan', color: '#00bcd4' },
|
||||
{ name: 'teal', color: '#009688' },
|
||||
{ name: 'green', color: '#4caf50' },
|
||||
{ name: 'lightgreen', color: '#8bc34a' },
|
||||
{ name: 'lime', color: '#cddc39' },
|
||||
{ name: 'yellow', color: '#ffeb3b' },
|
||||
{ name: 'amber', color: '#ffc107' },
|
||||
{ name: 'orange', color: '#fb8c00' },
|
||||
{ name: 'deeporange', color: '#ff5722' },
|
||||
{ name: 'brown', color: '#795548' },
|
||||
{ name: 'grey', color: '#9e9e9e' },
|
||||
{ name: 'bluegrey', color: '#607d8b' },
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
];
|
||||
|
||||
public WARN: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'red', color: '#f44336' },
|
||||
{ name: 'pink', color: '#e91e63' },
|
||||
{ name: 'purple', color: '#9c27b0' },
|
||||
{ name: 'deeppurple', color: '#673ab7' },
|
||||
];
|
||||
|
||||
public FONTLIGHT: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
{ name: 'gray-600', color: '#4b5563' },
|
||||
{ name: 'gray-700', color: '#374151' },
|
||||
{ name: 'gray-800', color: '#1f2937' },
|
||||
{ name: 'gray-900', color: '#111827' },
|
||||
{ name: 'black', color: '#000000' },
|
||||
];
|
||||
|
||||
public FONTDARK: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
{ name: 'gray-50', color: '#f9fafb' },
|
||||
{ name: 'gray-100', color: '#f3f4f6' },
|
||||
{ name: 'gray-200', color: '#e5e7eb' },
|
||||
{ name: 'gray-300', color: '#d1d5db' },
|
||||
{ name: 'gray-400', color: '#9ca3af' },
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
];
|
||||
|
||||
public BACKGROUNDLIGHT: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'white', color: '#ffffff' },
|
||||
{ name: 'gray-50', color: '#f9fafb' },
|
||||
{ name: 'gray-100', color: '#f3f4f6' },
|
||||
{ name: 'gray-200', color: '#e5e7eb' },
|
||||
{ name: 'gray-300', color: '#d1d5db' },
|
||||
{ name: 'gray-400', color: '#9ca3af' },
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
];
|
||||
|
||||
public BACKGROUNDDARK: Array<{ name: string; color: string; }> = [
|
||||
{ name: 'gray-500', color: '#6b7280' },
|
||||
{ name: 'gray-600', color: '#4b5563' },
|
||||
{ name: 'gray-700', color: '#374151' },
|
||||
{ name: 'gray-800', color: '#1f2937' },
|
||||
{ name: 'gray-900', color: '#111827' },
|
||||
{ name: 'black', color: '#000000' },
|
||||
];
|
||||
|
||||
public colors: Array<{ name: string; color: string; }> = this.PRIMARY;
|
||||
|
||||
@Input() colorType: ColorType = ColorType.PRIMARY;
|
||||
@Input() color: string = '';
|
||||
@Input() previewColor: string = '';
|
||||
@Input() name: string = '';
|
||||
@Output() previewChanged: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
public emitPreview(color: string): void {
|
||||
this.previewColor = color;
|
||||
this.previewChanged.emit(this.previewColor);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
switch (this.colorType) {
|
||||
case ColorType.PRIMARY:
|
||||
this.colors = this.PRIMARY;
|
||||
break;
|
||||
case ColorType.WARN:
|
||||
this.colors = this.WARN;
|
||||
break;
|
||||
case ColorType.FONTDARK:
|
||||
this.colors = this.FONTDARK;
|
||||
break;
|
||||
case ColorType.FONTLIGHT:
|
||||
this.colors = this.FONTLIGHT;
|
||||
break;
|
||||
case ColorType.BACKGROUNDDARK:
|
||||
this.colors = this.BACKGROUNDDARK;
|
||||
break;
|
||||
case ColorType.BACKGROUNDLIGHT:
|
||||
this.colors = this.BACKGROUNDLIGHT;
|
||||
break;
|
||||
default:
|
||||
this.colors = this.PRIMARY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<div class="preview" *ngIf="policy">
|
||||
<span class="label">{{label}}</span>
|
||||
<div class="dashed" [ngClass]="{'dark': theme === Theme.DARK, 'light': theme === Theme.LIGHT}" [style.background]="theme == Theme.DARK ? policy.backgroundColorDark : policy.backgroundColor">
|
||||
<div class="login-wrapper" [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor">
|
||||
<img *ngIf="images['previewLogo'] && theme == Theme.LIGHT" [src]="images['previewLogo']" alt="logo-mock" />
|
||||
<img *ngIf="!images['previewLogo'] && theme == Theme.LIGHT" src="../../../../assets/images/zitadel-logo-dark.svg" alt="logo-mock" />
|
||||
|
||||
<img *ngIf="images['previewDarkLogo'] && theme == Theme.DARK" [src]="images['previewDarkLogo']" alt="logo-mock" />
|
||||
<img *ngIf="!images['previewDarkLogo'] && theme == Theme.DARK" src="../../../../assets/images/zitadel-logo-light.svg" alt="logo-mock" />
|
||||
|
||||
<h1>{{'POLICY.PRIVATELABELING.PREVIEW.TITLE' | translate}}</h1>
|
||||
<p class="desc-text">{{'POLICY.PRIVATELABELING.PREVIEW.SECOND' | translate}}</p>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>Loginname</cnsl-label>
|
||||
<input cnslInput value="road.runner"/>
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="error-msg" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">
|
||||
<i class="las la-exclamation-circle" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor"></i>
|
||||
<span [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">{{'POLICY.PRIVATELABELING.PREVIEW.ERROR' | translate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button mat-stroked-button>{{'POLICY.PRIVATELABELING.PREVIEW.SECONDARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.DARK" mat-raised-button [style.background]="policy.primaryColorDark" [style.color]="policy.primaryColorDark == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.LIGHT" mat-raised-button [style.background]="policy.primaryColor" [style.color]="policy.primaryColor == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,92 @@
|
||||
.preview {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: var(--grey);
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border: 2px dashed rgba(#697386, .5);
|
||||
border-radius: 1rem;
|
||||
padding: 100px 20px;
|
||||
|
||||
.login-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
margin: auto;
|
||||
|
||||
@media only screen and (min-width: 1000px) {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 160px;
|
||||
max-height: 150px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
align-self: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
justify-content: flex-start;
|
||||
|
||||
i {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.light {
|
||||
background: #fff;
|
||||
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: #212224;
|
||||
|
||||
.login-wrapper *:not(.cnsl-label):not(.mat-button-wrapper) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PreviewComponent } from './preview.component';
|
||||
|
||||
describe('PreviewComponent', () => {
|
||||
let component: PreviewComponent;
|
||||
let fixture: ComponentFixture<PreviewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PreviewComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PreviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,35 @@
|
||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
|
||||
import { Preview, Theme } from '../private-labeling-policy.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-preview',
|
||||
templateUrl: './preview.component.html',
|
||||
styleUrls: ['./preview.component.scss'],
|
||||
})
|
||||
export class PreviewComponent implements OnInit, OnDestroy {
|
||||
@Input() preview: Preview = Preview.PREVIEW;
|
||||
@Input() policy!: LabelPolicy.AsObject;
|
||||
@Input() label: string = 'PREVIEW';
|
||||
@Input() images: { [imagekey: string]: any; } = {};
|
||||
@Input() theme: Theme = Theme.DARK;
|
||||
@Input() refresh: Observable<void> = of();
|
||||
private destroyed$: Subject<void> = new Subject();
|
||||
public Theme: any = Theme;
|
||||
public Preview: any = Preview;
|
||||
constructor(private chd: ChangeDetectorRef) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.refresh.pipe(takeUntil(this.destroyed$)).subscribe(() => {
|
||||
this.chd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroyed$.next();
|
||||
this.destroyed$.complete();
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PrivateLabelingPolicyComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PrivateLabelingPolicyRoutingModule { }
|
@ -0,0 +1,254 @@
|
||||
<div class="policy enlarged-container">
|
||||
<div class="header">
|
||||
<a [routerLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<div class="col">
|
||||
<h1>{{'POLICY.PRIVATELABELING.TITLE' | translate}}</h1>
|
||||
<p>{{'POLICY.PRIVATELABELING.DESCRIPTION' | translate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="desc">{{'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate}}</p>
|
||||
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
|
||||
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="top-row">
|
||||
<div>
|
||||
<p>{{'POLICY.PRIVATELABELING.THEME' | translate}}</p>
|
||||
<div class="theme-changer">
|
||||
<button (click)="theme = Theme.LIGHT" matTooltip="{{'POLICY.PRIVATELABELING.LIGHT' | translate}}" class="light" [ngClass]="{'active': theme === Theme.LIGHT}" >
|
||||
<i class="icon las la-edit"></i>
|
||||
</button>
|
||||
<button (click)="theme = Theme.DARK" matTooltip="{{'POLICY.PRIVATELABELING.DARK' | translate}}" class="dark" [ngClass]="{'active': theme === Theme.DARK}" >
|
||||
<i class="icon las la-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="activate-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button color="primary" (click)="activatePolicy()">
|
||||
{{'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="previewData && data" class="lab-policy-content">
|
||||
|
||||
<mat-accordion class="settings">
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-image"></i>
|
||||
Logo</div>
|
||||
</mat-panel-title>
|
||||
<mat-panel-description>
|
||||
|
||||
</mat-panel-description>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<p class="description">Your Logo will be used in the Login itself, while the icon is used for smaller UI elements like in the organisation switcher in console</p>
|
||||
|
||||
<!-- <span class="title">{{ theme === Theme.DARK ? ('POLICY.PRIVATELABELING.DARK' | translate) : ('POLICY.PRIVATELABELING.LIGHT' | translate)}}</span> -->
|
||||
<div class="logo-setup-wrapper">
|
||||
<div class="part">
|
||||
<span class="label">Logo</span>
|
||||
<mat-spinner class="spinner" color="primary" diameter="25" *ngIf="loadingImages"></mat-spinner>
|
||||
<container [ngSwitch]="theme">
|
||||
<div class="logo-view" *ngSwitchCase="Theme.DARK">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewDarkLogo']" [src]="images['previewDarkLogo']" alt="dark logo preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['darkLogo']" [src]="images['darkLogo']" alt="dark logo"/>
|
||||
</div>
|
||||
<div class="logo-view" *ngSwitchCase="Theme.LIGHT">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewLogo']" [src]="images['previewLogo']" alt="logo preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['logo']" [src]="images['logo']" alt="logo"/>
|
||||
</div>
|
||||
</container>
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverLogo(theme, $event)"
|
||||
(dropped)="onDropLogo(theme, $event)"
|
||||
[class.hovering]="isHoveringOverDarkLogo">
|
||||
<label class="file-label">
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDropLogo(theme, $event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFile.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span>{{isHoveringOverDarkLogo ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="part">
|
||||
<span class="label">Icon</span>
|
||||
<mat-spinner class="spinner" color="primary" diameter="25" *ngIf="loadingImages"></mat-spinner>
|
||||
<container [ngSwitch]="theme">
|
||||
<div class="logo-view" *ngSwitchCase="Theme.DARK">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewDarkIcon']" [src]="images['previewDarkIcon']" alt="dark icon preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['darkIcon']" [src]="images['darkIcon']" alt="dark icon"/>
|
||||
</div>
|
||||
<div class="logo-view" *ngSwitchCase="Theme.LIGHT">
|
||||
<img matTooltip="Preview" class="prev" *ngIf="images['previewIcon']" [src]="images['previewIcon']" alt="icon preview"/>
|
||||
<span class="fill-space"></span>
|
||||
<img matTooltip="Current" class="curr" *ngIf="images['icon']" [src]="images['icon']" alt="icon"/>
|
||||
</div>
|
||||
</container>
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverIcon(theme, $event)"
|
||||
(dropped)="onDropIcon(theme, $event)"
|
||||
[class.hovering]="isHoveringOverDarkIcon">
|
||||
<label class="file-label">
|
||||
<input #selectedFileIcon style="display: none;" class="file-input" type="file" (change)="onDropIcon(theme, $event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFileIcon.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span>{{isHoveringOverDarkIcon ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion" [expanded]="true">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-palette"></i>
|
||||
{{'POLICY.PRIVATELABELING.COLORS' | translate}}</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<!-- <span class="title">{{ theme === Theme.DARK ? ('POLICY.PRIVATELABELING.DARK' | translate) : ('POLICY.PRIVATELABELING.LIGHT' | translate)}}</span> -->
|
||||
|
||||
<ng-container *ngIf="theme==Theme.DARK">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDDARK"(previewChanged)="previewData.backgroundColorDark = $event; savePolicy()" name="Background Color Dark" [color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.PRIMARY"(previewChanged)="previewData.primaryColorDark = $event; savePolicy()" name="Preview Primary Color Dark" [color]="data.primaryColorDark" [previewColor]="previewData.primaryColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.WARN" (previewChanged)="previewData.warnColorDark = $event; savePolicy()" name="Preview Warn Color Dark" [color]="data.warnColorDark" [previewColor]="previewData.warnColorDark"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.FONTDARK"(previewChanged)="previewData.fontColorDark = $event; savePolicy()" name="Font Color Dark" [color]="data.fontColorDark" [previewColor]="previewData.fontColorDark"></cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="theme==Theme.LIGHT">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDLIGHT" (previewChanged)="previewData.backgroundColor = $event; savePolicy()" name="Background Color Light" [color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.PRIMARY" (previewChanged)="previewData.primaryColor = $event; savePolicy()" name="Preview Primary Color Light" [color]="data.primaryColor" [previewColor]="previewData.primaryColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.WARN" name="Preview Warn Color Light" (previewChanged)="previewData.warnColor= $event; savePolicy()" [color]="data.warnColor" [previewColor]="previewData.warnColor"></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.FONTLIGHT" (previewChanged)="previewData.fontColor = $event; savePolicy()" name="Font Color" [color]="data.fontColor" [previewColor]="previewData.fontColor"></cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header class="header">
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-font"></i>
|
||||
{{'POLICY.PRIVATELABELING.FONT' | translate}}</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="fonts">
|
||||
<div class="font-preview">
|
||||
<mat-icon>text_fields</mat-icon>
|
||||
<span>ABC • abc • 123</span>
|
||||
<span>_</span>
|
||||
<span>Upload your favorite font for the UI</span>
|
||||
</div>
|
||||
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverFont($event)"
|
||||
(dropped)="onDropFont($event)"
|
||||
[class.hovering]="isHoveringOverFont">
|
||||
<label class="file-label">
|
||||
<input #selectedFontFile style="display: none;" class="file-input" type="file" (change)="onDropFont($event.target.files)">
|
||||
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFontFile.click();" />
|
||||
|
||||
<i class="icon las la-cloud-upload-alt"></i>
|
||||
<span >{{isHoveringOverFont ? 'Release': 'Drop your Logo here'}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel class="expansion">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-universal-access"></i>
|
||||
{{'POLICY.PRIVATELABELING.ADVANCEDBEHAVIOR' | translate}}
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="adv-container" *ngIf="previewData">
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false">
|
||||
<cnsl-info-section class="info" type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy.private_label'})}}
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl
|
||||
[(ngModel)]="previewData.hideLoginNameSuffix" (change)="savePolicy()">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false">
|
||||
<cnsl-info-section class="info" type="WARN">{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy.watermark'})}}
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false"
|
||||
[(ngModel)]="previewData.disableWatermark" (change)="saveWatermark()">
|
||||
{{'POLICY.DATA.DISABLEWATERMARK' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
||||
|
||||
<div class="preview-wrapper">
|
||||
<!-- <cnsl-preview class="prev" label="CURRENT CONFIG" [policy]="data"></cnsl-preview> -->
|
||||
<div class="col">
|
||||
<button color="primary" mat-raised-button class="preview-changer" (click)="preview = preview == Preview.PREVIEW ? Preview.CURRENT : Preview.PREVIEW" matTooltip="{{'POLICY.PRIVATELABELING.CHANGEVIEW' | translate}}" [ngClass]="{'active': preview === Preview.PREVIEW}" >
|
||||
<span><span [ngClass]="{'strong': preview == Preview.PREVIEW}">P</span> / <span [ngClass]="{'strong': preview == Preview.CURRENT}">C</span></span>
|
||||
</button>
|
||||
|
||||
<cnsl-preview *ngIf="preview === Preview.CURRENT" [refresh]="refreshPreview" [images]="images" [preview]="preview" [theme]="theme" class="prev" label="CURRENT" [policy]="data"></cnsl-preview>
|
||||
<cnsl-preview *ngIf="preview === Preview.PREVIEW" [refresh]="refreshPreview" [images]="images" [preview]="preview" [theme]="theme" class="prev" label="PREVIEW" [policy]="previewData"></cnsl-preview>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<cnsl-links *ngIf="nextLinks" [links]="nextLinks"></cnsl-links>
|
||||
</div>
|
@ -0,0 +1,398 @@
|
||||
@use '~@angular/material' as mat;
|
||||
|
||||
@import './preview/preview.component.scss';
|
||||
|
||||
@mixin private-label-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat.get-color-from-palette($primary, 500);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
|
||||
.policy {
|
||||
.header {
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 1rem;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.default {
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
max-width: 800px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.spinner-wr {
|
||||
margin: .5rem 0;
|
||||
}
|
||||
|
||||
.theme-changer {
|
||||
display: flex;
|
||||
margin: 1rem -.5rem;
|
||||
|
||||
button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #81868a50;
|
||||
margin: 0 .5rem;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: black;
|
||||
|
||||
&.active {
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: white;
|
||||
|
||||
&.active {
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.activate-button {
|
||||
border-radius: 50%;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.theme-changer {
|
||||
display: flex;
|
||||
margin: 0 -.25rem;
|
||||
|
||||
button {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #81868a50;
|
||||
margin: 0 .25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: black;
|
||||
|
||||
&.active {
|
||||
transform: scale(1.1);
|
||||
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.light {
|
||||
background-color: white;
|
||||
|
||||
&.active {
|
||||
transform: scale(1.1);
|
||||
|
||||
.icon {
|
||||
visibility: visible;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lab-policy-content {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -1rem;
|
||||
|
||||
.settings {
|
||||
flex: 1;
|
||||
margin: 0 1rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.expansion {
|
||||
background: if($is-dark-theme, #2d2e30, #fff) !important;
|
||||
|
||||
.header {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: .5rem;
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
outline: none;
|
||||
height: 150px;
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
background: if($is-dark-theme, #2d2e30, #fff);
|
||||
border: 1px solid if($is-dark-theme, #4a4b4b, #ddd);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
transition: all .2s ease-in-out;
|
||||
|
||||
.file-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
padding: .5rem 1rem;
|
||||
background-color: inherit;
|
||||
border: 1px solid if($is-dark-theme, #ffffff20, #000);
|
||||
color: if($is-dark-theme, white, #000);
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 1.5rem;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
&.hovering {
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
if(
|
||||
$is-dark-theme,
|
||||
(inset 26px 26px 52px #252628, inset -26px -26px 52px #353638),
|
||||
(inset 26px 26px 52px #d4d4d4, inset -26px -26px 52px #fff)
|
||||
);
|
||||
|
||||
.desc {
|
||||
color: if($is-dark-theme, white, black);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: if($is-dark-theme, white, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo-setup-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.part {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.spinner {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: var(--grey);
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.logo-view {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.prev,
|
||||
.curr {
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.colors {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.color {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fonts {
|
||||
.title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.font-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30px 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.font-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.25rem;
|
||||
width: 100%;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.font {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: .5rem;
|
||||
margin: .25rem;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid if($is-dark-theme, #ffffff30, #00000030);
|
||||
font-size: 1.5rem;
|
||||
background-color: inherit;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border: 2px solid if($is-dark-theme, #fff, #000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adv-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 50px;
|
||||
|
||||
.info {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-wrapper {
|
||||
margin: 0 1rem;
|
||||
flex: 1;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 2rem;
|
||||
position: relative;
|
||||
min-height: 600px;
|
||||
|
||||
.preview-changer {
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
left: .5rem;
|
||||
border-radius: 10px !important;
|
||||
z-index: 1;
|
||||
|
||||
span {
|
||||
color: if($is-dark-theme, #ffffff50, #00000050);
|
||||
}
|
||||
|
||||
.strong {
|
||||
color: if($is-dark-theme, #fff, #000);
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1000px) {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.preview-wrapper {
|
||||
.col {
|
||||
min-width: 400px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
describe('PrivateLabelingPolicyComponent', () => {
|
||||
let component: PrivateLabelingPolicyComponent;
|
||||
let fixture: ComponentFixture<PrivateLabelingPolicyComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PrivateLabelingPolicyComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PrivateLabelingPolicyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,511 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, EventEmitter, Injector, OnDestroy, Type } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_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';
|
||||
import { DownloadEndpoint, UploadEndpoint, UploadService } from 'src/app/services/upload.service';
|
||||
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import { IAM_COMPLEXITY_LINK, IAM_LOGIN_POLICY_LINK, IAM_POLICY_LINK } from '../../policy-grid/policy-links';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
export enum Theme {
|
||||
DARK,
|
||||
LIGHT,
|
||||
}
|
||||
|
||||
export enum Preview {
|
||||
CURRENT,
|
||||
PREVIEW,
|
||||
}
|
||||
|
||||
export enum ColorType {
|
||||
BACKGROUND,
|
||||
PRIMARY,
|
||||
WARN,
|
||||
FONTDARK,
|
||||
FONTLIGHT,
|
||||
BACKGROUNDDARK,
|
||||
BACKGROUNDLIGHT,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-private-labeling-policy',
|
||||
templateUrl: './private-labeling-policy.component.html',
|
||||
styleUrls: ['./private-labeling-policy.component.scss'],
|
||||
})
|
||||
export class PrivateLabelingPolicyComponent implements OnDestroy {
|
||||
public theme: Theme = Theme.LIGHT;
|
||||
public preview: Preview = Preview.PREVIEW;
|
||||
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: ManagementService | AdminService;
|
||||
|
||||
public previewData!: LabelPolicy.AsObject;
|
||||
public data!: LabelPolicy.AsObject;
|
||||
|
||||
public images: { [key: string]: any; } = {};
|
||||
|
||||
public panelOpenState: boolean = false;
|
||||
public isHoveringOverDarkLogo: boolean = false;
|
||||
public isHoveringOverDarkIcon: boolean = false;
|
||||
public isHoveringOverLightLogo: boolean = false;
|
||||
public isHoveringOverLightIcon: boolean = false;
|
||||
public isHoveringOverFont: boolean = false;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
|
||||
public loading: boolean = false;
|
||||
public nextLinks: CnslLinks[] = [
|
||||
IAM_COMPLEXITY_LINK,
|
||||
IAM_POLICY_LINK,
|
||||
IAM_LOGIN_POLICY_LINK,
|
||||
];
|
||||
|
||||
public Theme: any = Theme;
|
||||
public Preview: any = Preview;
|
||||
public ColorType: any = ColorType;
|
||||
|
||||
public refreshPreview: EventEmitter<void> = new EventEmitter();
|
||||
public loadingImages: boolean = false;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
private uploadService: UploadService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
public toggleHoverLogo(theme: Theme, isHovering: boolean): void {
|
||||
if (theme === Theme.DARK) {
|
||||
this.isHoveringOverDarkLogo = isHovering;
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
this.isHoveringOverLightLogo = isHovering;
|
||||
}
|
||||
}
|
||||
|
||||
public toggleHoverFont(isHovering: boolean): void {
|
||||
this.isHoveringOverFont = isHovering;
|
||||
}
|
||||
|
||||
public onDropLogo(theme: Theme, filelist: FileList): Promise<any> | void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (theme === Theme.DARK) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTDARKLOGO, formData));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMDARKLOGO, formData));
|
||||
}
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTLIGHTLOGO, formData));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMLIGHTLOGO, formData));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public onDropFont(filelist: FileList): Promise<any> | void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.uploadService.upload(UploadEndpoint.MGMTFONT, formData);
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.uploadService.upload(UploadEndpoint.IAMFONT, formData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public toggleHoverIcon(theme: Theme, isHovering: boolean): void {
|
||||
if (theme === Theme.DARK) {
|
||||
this.isHoveringOverDarkIcon = isHovering;
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
this.isHoveringOverLightIcon = isHovering;
|
||||
}
|
||||
}
|
||||
|
||||
public onDropIcon(theme: Theme, filelist: FileList): void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (theme === Theme.DARK) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTDARKICON, formData));
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMDARKICON, formData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (theme === Theme.LIGHT) {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.MGMTLIGHTICON, formData));
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.handleUploadPromise(this.uploadService.upload(UploadEndpoint.IAMLIGHTICON, formData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleUploadPromise(task: Promise<any>): Promise<any> {
|
||||
return task.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.UPLOADSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getPreviewData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loadPreviewImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => this.toast.showError(error));
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.getPreviewData().then(data => {
|
||||
console.log('preview', data);
|
||||
this.loadingImages = true;
|
||||
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loading = false;
|
||||
|
||||
this.loadPreviewImages();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.getData().then(data => {
|
||||
console.log('data', data);
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loading = false;
|
||||
|
||||
this.loadImages();
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private loadImages(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
if (this.data.logoUrlDark) {
|
||||
this.loadAsset('darkLogo', DownloadEndpoint.IAMDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrlDark) {
|
||||
this.loadAsset('darkIcon', DownloadEndpoint.IAMDARKICONPREVIEW);
|
||||
}
|
||||
if (this.data.logoUrl) {
|
||||
this.loadAsset('logo', DownloadEndpoint.IAMLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrl) {
|
||||
this.loadAsset('icon', DownloadEndpoint.IAMICONPREVIEW);
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
if (this.data.logoUrlDark) {
|
||||
this.loadAsset('darkLogo', DownloadEndpoint.MGMTDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrlDark) {
|
||||
this.loadAsset('darkIcon', DownloadEndpoint.MGMTDARKICONPREVIEW);
|
||||
}
|
||||
if (this.data.logoUrl) {
|
||||
this.loadAsset('logo', DownloadEndpoint.MGMTLOGOPREVIEW);
|
||||
}
|
||||
if (this.data.iconUrl) {
|
||||
this.loadAsset('icon', DownloadEndpoint.MGMTICONPREVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private loadPreviewImages(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
if (this.previewData.logoUrlDark) {
|
||||
this.loadAsset('previewDarkLogo', DownloadEndpoint.IAMDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrlDark) {
|
||||
this.loadAsset('previewDarkIcon', DownloadEndpoint.IAMDARKICONPREVIEW);
|
||||
}
|
||||
if (this.previewData.logoUrl) {
|
||||
this.loadAsset('previewLogo', DownloadEndpoint.IAMLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrl) {
|
||||
this.loadAsset('previewIcon', DownloadEndpoint.IAMICONPREVIEW);
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
if (this.previewData.logoUrlDark) {
|
||||
this.loadAsset('previewDarkLogo', DownloadEndpoint.MGMTDARKLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrlDark) {
|
||||
this.loadAsset('previewDarkIcon', DownloadEndpoint.MGMTDARKICONPREVIEW);
|
||||
}
|
||||
if (this.previewData.logoUrl) {
|
||||
this.loadAsset('previewLogo', DownloadEndpoint.MGMTLOGOPREVIEW);
|
||||
}
|
||||
if (this.previewData.iconUrl) {
|
||||
this.loadAsset('previewIcon', DownloadEndpoint.MGMTICONPREVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getPreviewData():
|
||||
Promise<MgmtGetPreviewLabelPolicyResponse.AsObject |
|
||||
AdminGetPreviewLabelPolicyResponse.AsObject |
|
||||
MgmtGetLabelPolicyResponse.AsObject |
|
||||
AdminGetLabelPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPreviewLabelPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPreviewLabelPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPreviewLabelPolicyResponse.AsObject |
|
||||
AdminGetPreviewLabelPolicyResponse.AsObject |
|
||||
MgmtGetLabelPolicyResponse.AsObject |
|
||||
AdminGetLabelPolicyResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getLabelPolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getLabelPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
private loadAsset(imagekey: string, url: string): Promise<any> {
|
||||
return this.uploadService.load(`${url}`).then(data => {
|
||||
const objectURL = URL.createObjectURL(data);
|
||||
this.images[imagekey] = this.sanitizer.bypassSecurityTrustUrl(objectURL);
|
||||
this.refreshPreview.emit();
|
||||
this.loadingImages = false;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
this.loadingImages = false;
|
||||
});
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.service instanceof ManagementService) {
|
||||
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.previewData as LabelPolicy.AsObject).isDefault) {
|
||||
const req0 = new AddCustomLabelPolicyRequest();
|
||||
this.overwriteValues(req0);
|
||||
|
||||
(this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch((error: HttpErrorResponse) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req1 = new UpdateCustomLabelPolicyRequest();
|
||||
this.overwriteValues(req1);
|
||||
|
||||
(this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
this.overwriteValues(req);
|
||||
(this.service as AdminService).updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public saveWatermark(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if ((this.previewData as LabelPolicy.AsObject).isDefault) {
|
||||
const req0 = new AddCustomLabelPolicyRequest();
|
||||
req0.setDisableWatermark(this.previewData.disableWatermark);
|
||||
|
||||
(this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch((error: HttpErrorResponse) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
const req1 = new UpdateCustomLabelPolicyRequest();
|
||||
req1.setDisableWatermark(this.previewData.disableWatermark);
|
||||
|
||||
(this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
const req = new UpdateLabelPolicyRequest();
|
||||
req.setDisableWatermark(this.data.disableWatermark);
|
||||
|
||||
(this.service as AdminService).updateLabelPolicy(req).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.previewData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.previewData as LabelPolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public overwriteValues(req: AddCustomLabelPolicyRequest | UpdateCustomLabelPolicyRequest): void {
|
||||
req.setBackgroundColorDark(this.previewData.backgroundColorDark);
|
||||
req.setBackgroundColor(this.previewData.backgroundColor);
|
||||
|
||||
req.setFontColorDark(this.previewData.fontColorDark);
|
||||
req.setFontColor(this.previewData.fontColor);
|
||||
|
||||
req.setPrimaryColorDark(this.previewData.primaryColorDark);
|
||||
req.setPrimaryColor(this.previewData.primaryColor);
|
||||
|
||||
req.setWarnColorDark(this.previewData.warnColorDark);
|
||||
req.setWarnColor(this.previewData.warnColor);
|
||||
|
||||
req.setDisableWatermark(this.previewData.disableWatermark);
|
||||
req.setHideLoginNameSuffix(this.previewData.hideLoginNameSuffix);
|
||||
}
|
||||
|
||||
public activatePolicy(): Promise<any> {
|
||||
// dialog warning
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).activateCustomLabelPolicy().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.ACTIVATED', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loadImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).activateLabelPolicy().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.ACTIVATED', true);
|
||||
setTimeout(() => {
|
||||
this.loadingImages = true;
|
||||
this.getData().then(data => {
|
||||
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loadImages();
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public resetPolicy(): Promise<any> {
|
||||
return (this.service as ManagementService).resetLabelPolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.PRIVATELABELING.RESET', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
|
||||
import { DropzoneModule } from '../../../directives/dropzone/dropzone.module';
|
||||
import { DetailLayoutModule } from '../../detail-layout/detail-layout.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
import { InputModule } from '../../input/input.module';
|
||||
import { LinksModule } from '../../links/links.module';
|
||||
import { ColorComponent } from './color/color.component';
|
||||
import { PreviewComponent } from './preview/preview.component';
|
||||
import { PrivateLabelingPolicyRoutingModule } from './private-labeling-policy-routing.module';
|
||||
import { PrivateLabelingPolicyComponent } from './private-labeling-policy.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
PrivateLabelingPolicyComponent,
|
||||
PreviewComponent,
|
||||
ColorComponent,
|
||||
],
|
||||
imports: [
|
||||
PrivateLabelingPolicyRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
DropzoneModule,
|
||||
MatProgressSpinnerModule,
|
||||
LinksModule,
|
||||
MatExpansionModule,
|
||||
InfoSectionModule,
|
||||
HasFeaturePipeModule,
|
||||
],
|
||||
})
|
||||
export class PrivateLabelingPolicyModule { }
|
@ -75,6 +75,38 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<div class="p-item card">
|
||||
<div class="avatar">
|
||||
<i class="icon las la-gem"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{'POLICY.PRIVATELABELING_POLICY.TITLE' | translate}}</span>
|
||||
<button mat-icon-button disabled>
|
||||
<i class="icon las la-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="desc">
|
||||
{{'POLICY.PRIVATELABELING_POLICY.DESCRIPTION' | translate}}</p>
|
||||
<!-- <cnsl-info-section class="warn"
|
||||
*ngIf="type == PolicyGridType.ORG && (['password_complexity_policy'] | hasFeature | async) == false"
|
||||
type="WARN">
|
||||
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'password_complexity_policy'})}}
|
||||
</cnsl-info-section> -->
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="btn-wrapper">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||
<button
|
||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.PRIVATELABEL ]"
|
||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="type == PolicyGridType.IAM ? ['iam.policy.read'] : type == PolicyGridType.ORG ? ['policy.read'] : []">
|
||||
<div class="p-item card">
|
||||
@ -101,40 +133,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-container *ngIf="type === PolicyGridType.IAM">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<div class="p-item card">
|
||||
<div class="avatar">
|
||||
<i class="icon las la-envelope"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{'POLICY.LABEL.TITLE' | translate}}</span>
|
||||
<button mat-icon-button disabled>
|
||||
<i class="icon las la-check-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="desc">
|
||||
{{'POLICY.LABEL.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<cnsl-info-section class="warn"
|
||||
*ngIf="type == PolicyGridType.ORG && (['label_policy'] | hasFeature | async) == false" type="WARN">
|
||||
{{'FEATURES.NOTAVAILABLE' | translate: ({value:
|
||||
'label_policy'})}}
|
||||
</cnsl-info-section>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="btn-wrapper">
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.write']">
|
||||
<button
|
||||
[routerLink]="[type == PolicyGridType.IAM ? '/iam' : type == PolicyGridType.ORG ? '/org' : '','policy', PolicyComponentType.LABEL ]"
|
||||
mat-stroked-button
|
||||
[disabled]="type == PolicyGridType.ORG && (['label_policy'] | hasFeature | async) == false">
|
||||
{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
@ -1,51 +1,58 @@
|
||||
import { PolicyComponentType } from '../policies/policy-component-types.enum';
|
||||
|
||||
export const IAM_COMPLEXITY_LINK = {
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_LOGIN_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const IAM_LABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.LABEL],
|
||||
withRole: ['iam.policy.read'],
|
||||
export const IAM_PRIVATELABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/iam', 'policy', PolicyComponentType.PRIVATELABEL],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
|
||||
export const ORG_COMPLEXITY_LINK = {
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['policy.read'],
|
||||
i18nTitle: 'POLICY.PWD_COMPLEXITY.TITLE',
|
||||
i18nDesc: 'POLICY.PWD_COMPLEXITY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.COMPLEXITY],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
||||
export const ORG_IAM_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
i18nTitle: 'POLICY.IAM_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.IAM_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.IAM],
|
||||
withRole: ['iam.policy.read'],
|
||||
};
|
||||
|
||||
export const ORG_LOGIN_POLICY_LINK = {
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['policy.read'],
|
||||
i18nTitle: 'POLICY.LOGIN_POLICY.TITLE',
|
||||
i18nDesc: 'POLICY.LOGIN_POLICY.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.LOGIN],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
||||
|
||||
export const ORG_PRIVATELABEL_LINK = {
|
||||
i18nTitle: 'POLICY.LABEL.TITLE',
|
||||
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
|
||||
routerLink: ['/org', 'policy', PolicyComponentType.PRIVATELABEL],
|
||||
withRole: ['policy.read'],
|
||||
};
|
||||
|
@ -9,119 +9,119 @@ import { EventstoreComponent } from './eventstore/eventstore.component';
|
||||
import { IamComponent } from './iam.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'policies',
|
||||
component: IamComponent,
|
||||
{
|
||||
path: 'policies',
|
||||
component: IamComponent,
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'eventstore',
|
||||
component: EventstoreComponent,
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./iam-members/iam-members.module').then(m => m.IamMembersModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.member.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
// canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.features.read'],
|
||||
serviceType: FeatureServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
roles: ['iam.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'eventstore',
|
||||
component: EventstoreComponent,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.read'],
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./iam-members/iam-members.module').then(m => m.IamMembersModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
roles: ['iam.member.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
// canActivate: [RoleGuard],
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
roles: ['iam.features.read'],
|
||||
serviceType: FeatureServiceType.ADMIN,
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [AuthGuard, RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/label-policy/label-policy.module')
|
||||
.then(m => m.LabelPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.PRIVATELABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/private-labeling-policy/private-labeling-policy.module')
|
||||
.then(m => m.PrivateLabelingPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.ADMIN,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class IamRoutingModule { }
|
||||
|
@ -73,7 +73,7 @@ h2 {
|
||||
|
||||
.new-desc {
|
||||
font-size: 14px;
|
||||
color: #818a8a;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.custom-domain-deactivated {
|
||||
|
@ -8,108 +8,116 @@ import { OrgCreateComponent } from './org-create/org-create.component';
|
||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
{
|
||||
path: 'create',
|
||||
component: OrgCreateComponent,
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['(org.create)?(iam.write)?'],
|
||||
},
|
||||
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
component: OrgCreateComponent,
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['(org.create)?(iam.write)?'],
|
||||
roles: ['org.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
loadChildren: () => import('src/app/modules/idp-create/idp-create.module').then(m => m.IdpCreateModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['org.idp.write'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
loadChildren: () => import('src/app/modules/idp/idp.module').then(m => m.IdpModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['features.read'],
|
||||
serviceType: FeatureServiceType.MGMT,
|
||||
roles: ['iam.idp.read'],
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then(m => m.FeaturesModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['features.read'],
|
||||
serviceType: FeatureServiceType.MGMT,
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./org-members/org-members.module').then(m => m.OrgMembersModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: OrgDetailComponent,
|
||||
},
|
||||
{
|
||||
path: 'overview',
|
||||
loadChildren: () => import('./org-list/org-list.module').then(m => m.OrgListModule),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
{
|
||||
path: PolicyComponentType.AGE,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-age-policy/password-age-policy.module')
|
||||
.then(m => m.PasswordAgePolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOCKOUT,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-lockout-policy/password-lockout-policy.module')
|
||||
.then(m => m.PasswordLockoutPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.PRIVATELABEL,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/private-labeling-policy/private-labeling-policy.module')
|
||||
.then(m => m.PrivateLabelingPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.COMPLEXITY,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/password-complexity-policy/password-complexity-policy.module')
|
||||
.then(m => m.PasswordComplexityPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.IAM,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/org-iam-policy/org-iam-policy.module')
|
||||
.then(m => m.OrgIamPolicyModule),
|
||||
},
|
||||
{
|
||||
path: PolicyComponentType.LOGIN,
|
||||
data: {
|
||||
serviceType: PolicyComponentServiceType.MGMT,
|
||||
},
|
||||
loadChildren: () => import('src/app/modules/policies/login-policy/login-policy.module')
|
||||
.then(m => m.LoginPolicyModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
loadChildren: () => import('./org-members/org-members.module').then(m => m.OrgMembersModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: OrgDetailComponent,
|
||||
},
|
||||
{
|
||||
path: 'overview',
|
||||
loadChildren: () => import('./org-list/org-list.module').then(m => m.OrgListModule),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrgsRoutingModule { }
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
106
console/src/app/services/upload.service.ts
Normal file
106
console/src/app/services/upload.service.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Org } from '../proto/generated/zitadel/org_pb';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
const ORG_STORAGE_KEY = 'organization';
|
||||
const authorizationKey = 'Authorization';
|
||||
const orgKey = 'x-zitadel-orgid';
|
||||
|
||||
const bearerPrefix = 'Bearer';
|
||||
const accessTokenStorageKey = 'access_token';
|
||||
|
||||
export enum UploadEndpoint {
|
||||
IAMFONT = 'iam/policy/label/font',
|
||||
MGMTFONT = 'org/policy/label/font',
|
||||
|
||||
IAMDARKLOGO = 'iam/policy/label/logo/dark',
|
||||
IAMLIGHTLOGO = 'iam/policy/label/logo',
|
||||
IAMDARKICON = 'iam/policy/label/icon/dark',
|
||||
IAMLIGHTICON = 'iam/policy/label/icon',
|
||||
|
||||
MGMTDARKLOGO = 'org/policy/label/logo/dark',
|
||||
MGMTLIGHTLOGO = 'org/policy/label/logo',
|
||||
MGMTDARKICON = 'org/policy/label/icon/dark',
|
||||
MGMTLIGHTICON = 'org/policy/label/icon',
|
||||
}
|
||||
|
||||
export enum DownloadEndpoint {
|
||||
IAMFONT = 'iam/policy/label/font',
|
||||
MGMTFONT = 'org/policy/label/font',
|
||||
|
||||
IAMDARKLOGO = 'iam/policy/label/logo/dark',
|
||||
IAMLOGO = 'iam/policy/label/logo',
|
||||
IAMDARKICON = 'iam/policy/label/icon/dark',
|
||||
IAMICON = 'iam/policy/label/icon',
|
||||
|
||||
MGMTDARKLOGO = 'org/policy/label/logo/dark',
|
||||
MGMTLOGO = 'org/policy/label/logo',
|
||||
MGMTDARKICON = 'org/policy/label/icon/dark',
|
||||
MGMTICON = 'org/policy/label/icon',
|
||||
|
||||
IAMDARKLOGOPREVIEW = 'iam/policy/label/logo/dark/_preview',
|
||||
IAMLOGOPREVIEW = 'iam/policy/label/logo/_preview',
|
||||
IAMDARKICONPREVIEW = 'iam/policy/label/icon/dark/_preview',
|
||||
IAMICONPREVIEW = 'iam/policy/label/icon/_preview',
|
||||
|
||||
MGMTDARKLOGOPREVIEW = 'org/policy/label/logo/dark/_preview',
|
||||
MGMTLOGOPREVIEW = 'org/policy/label/logo/_preview',
|
||||
MGMTDARKICONPREVIEW = 'org/policy/label/icon/dark/_preview',
|
||||
MGMTICONPREVIEW = 'org/policy/label/icon/_preview',
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UploadService {
|
||||
private serviceUrl: string = '';
|
||||
private accessToken: string = '';
|
||||
private org!: Org.AsObject;
|
||||
constructor(private http: HttpClient, private storageService: StorageService) {
|
||||
|
||||
http.get('./assets/environment.json')
|
||||
.toPromise().then((data: any) => {
|
||||
if (data && data.uploadServiceUrl) {
|
||||
this.serviceUrl = data.uploadServiceUrl;
|
||||
const aT = this.storageService.getItem(accessTokenStorageKey);
|
||||
|
||||
if (aT) {
|
||||
this.accessToken = aT;
|
||||
}
|
||||
|
||||
const org: Org.AsObject | null = (this.storageService.getItem(ORG_STORAGE_KEY));
|
||||
|
||||
if (org) {
|
||||
this.org = org;
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
public upload(endpoint: UploadEndpoint, body: any): Promise<any> {
|
||||
return this.http.post(`${this.serviceUrl}/assets/v1/${endpoint}`,
|
||||
body,
|
||||
{
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${this.accessToken}`,
|
||||
[orgKey]: `${this.org.id}`,
|
||||
},
|
||||
}).toPromise();
|
||||
}
|
||||
|
||||
public load(endpoint: string): Promise<any> {
|
||||
return this.http.get(`${this.serviceUrl}/assets/v1/${endpoint}`,
|
||||
|
||||
{
|
||||
responseType: 'blob',
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${this.accessToken}`,
|
||||
[orgKey]: `${this.org.id}`,
|
||||
},
|
||||
}).toPromise();
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"authServiceUrl": "https://api.zitadel.dev",
|
||||
"mgmtServiceUrl": "https://api.zitadel.dev",
|
||||
"adminServiceUrl":"https://api.zitadel.dev",
|
||||
"subscriptionServiceUrl":"https://sub.zitadel.dev",
|
||||
"issuer": "https://issuer.zitadel.dev",
|
||||
"clientid": "70669160379706195@zitadel"
|
||||
"authServiceUrl": "https://api.zitadel.io",
|
||||
"mgmtServiceUrl": "https://api.zitadel.io",
|
||||
"adminServiceUrl":"https://api.zitadel.io",
|
||||
"subscriptionServiceUrl":"https://sub.zitadel.io",
|
||||
"uploadServiceUrl":"https://api.zitadel.io",
|
||||
"issuer": "https://issuer.zitadel.io",
|
||||
"clientid": "69234247558357051@zitadel"
|
||||
}
|
||||
|
@ -104,8 +104,9 @@
|
||||
}
|
||||
},
|
||||
"ACTIONS": {
|
||||
"SHOW":"Aufklappen",
|
||||
"HIDE":"Zuklappen",
|
||||
"SET":"Übernehmen",
|
||||
"SHOW":"Aufklappen",
|
||||
"HIDE":"Zuklappen",
|
||||
"SAVE": "Speichern",
|
||||
"SAVENOW": "Speichern",
|
||||
"NEW": "Neu",
|
||||
@ -613,7 +614,8 @@
|
||||
"LOGINPOLICYFACTORS": "Login Richtlinie: Mltifaktoren - benutzerdefiniert",
|
||||
"LOGINPOLICYPASSWORDLESS": "Login Richtlinie: Passwortlose Authentifizierung - benutzerdefiniert",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Passwortkomplexitäts Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICY": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen",
|
||||
"CUSTOMDOMAIN": "Domänen Verifikation - verfügbar"
|
||||
},
|
||||
"TIERSTATES": {
|
||||
@ -636,6 +638,28 @@
|
||||
"NUMBERERROR": "Das Password muss eine Ziffer beinhalten.",
|
||||
"PATTERNERROR": "Das Passwort erfüllt nicht die vorgeschriebene Richtlinie."
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE":"Private Labeling",
|
||||
"DESCRIPTION":"Verleihe dem Login deinen benutzerdefinierten Style und passe das Verhalten an.",
|
||||
"PREVIEW_DESCRIPTION":"Änderungen dieser Richtlinie werden automatisch in der Preview Umgebung verfügbar. Um die Preview zu Testen muss dem login flow der scope 'x-preview' mitgegeben werden.",
|
||||
"BTN":"Datei auswählen",
|
||||
"ACTIVATEPREVIEW":"Konfiguration übernehmen",
|
||||
"DARK":"Dunkler Modus",
|
||||
"LIGHT":"Heller Modus",
|
||||
"CHANGEVIEW":"Ansicht wechseln",
|
||||
"ACTIVATED":"Richtlinie wurde LIVE geschaltet",
|
||||
"THEME":"Modus",
|
||||
"COLORS":"Farben",
|
||||
"FONT":"Schrift",
|
||||
"ADVANCEDBEHAVIOR":"Erweitertes Verhalten",
|
||||
"PREVIEW": {
|
||||
"TITLE":"Anmeldung",
|
||||
"SECOND":"mit ZITADEL-Konto anmelden.",
|
||||
"ERROR":"Benutzer konnte nicht gefunden werden!",
|
||||
"PRIMARYBUTTON":"weiter",
|
||||
"SECONDARYBUTTON":"registrieren"
|
||||
}
|
||||
},
|
||||
"PWD_AGE": {
|
||||
"TITLE": "Gültigkeitsdauer für Passwörter",
|
||||
"DESCRIPTION": "Du kannst eine Richtlinie für die maximale Gültigkeitsdauer von Passwörtern festlegen. Diese Richtlinie löst eine Warnung nach Ablauf einer festgelegten Gültigkeitsdauer aus."
|
||||
@ -648,6 +672,10 @@
|
||||
"TITLE": "Zugangseinstellungen IAM",
|
||||
"DESCRIPTION": "Definiere die Zugangseistellungen für Benutzer."
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Private Labeling",
|
||||
"DESCRIPTION": "Definiere das Erscheinungsbild des Logins."
|
||||
},
|
||||
"LOGIN_POLICY": {
|
||||
"TITLE": "Login Richtlinien",
|
||||
"DESCRIPTION": "Definiere die Loginmethoden für Benutzer",
|
||||
@ -655,13 +683,6 @@
|
||||
"DESCRIPTIONCREATEMGMT": "Nutzer können sich mit den verfügbaren Idps authentifizieren. Achtung: Es kann zwischen System- und organisationsspezifischen Providern gewählt werden.",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"LABEL": {
|
||||
"TITLE": "Email Labelling Einstellungen",
|
||||
"DESCRIPTION": "Definieren Sie das Erscheinungsbild Ihrer Benachrichtigungs-Mails",
|
||||
"PRIMARYCOLOR": "Hintergrundfarbe",
|
||||
"SECONDARYCOLOR": "Schriftfarbe",
|
||||
"SAVED": "Erfolgreich gespeichert."
|
||||
},
|
||||
"DEFAULTLABEL": "Die aktuelle Richtlinie entspricht der IAM-Standard Einstellung.",
|
||||
"BTN_INSTALL": "Installieren",
|
||||
"BTN_EDIT": "Modifizieren",
|
||||
@ -685,14 +706,19 @@
|
||||
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
|
||||
"FORCEMFA": "Mfa erzwingen",
|
||||
"FORCEMFA_DESC": "Ist die Option gewählt, müssen Benutzer einen zweiten Faktor für den Login verwenden.",
|
||||
"HIDEPASSWORDRESET": "Passwort vergessen, nicht anzeigen",
|
||||
"FORCEMFA_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link."
|
||||
"HIDEPASSWORDRESET": "Passwort vergessen ausblenden",
|
||||
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
|
||||
"HIDELOGINNAMESUFFIX":"Loginname Suffix ausblenden",
|
||||
"ERRORMSGPOPUP":"Fehler als Dialog Fenster",
|
||||
"DISABLEWATERMARK":"Wasserzeichen ausblenden"
|
||||
},
|
||||
"RESET": "Richtlinie zurücksetzen",
|
||||
"CREATECUSTOM": "Benutzerdefinierte Richtlinie erstellen",
|
||||
"TOAST": {
|
||||
"SET": "Richtline erfolgreich gesetzt!",
|
||||
"RESETSUCCESS": "Richtline zurückgesetzt!"
|
||||
"RESETSUCCESS": "Richtline zurückgesetzt!",
|
||||
"UPLOADSUCCESS": "Upload erfolgreich",
|
||||
"UPLOADFAILED":"Upload fehlgeschlagen!"
|
||||
}
|
||||
},
|
||||
"ORG_DETAIL": {
|
||||
|
@ -104,6 +104,7 @@
|
||||
}
|
||||
},
|
||||
"ACTIONS": {
|
||||
"SET":"Set",
|
||||
"SHOW":"Show",
|
||||
"HIDE":"Hide",
|
||||
"SAVE": "Save",
|
||||
@ -613,7 +614,8 @@
|
||||
"LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom",
|
||||
"LOGINPOLICYPASSWORDLESS": "Login Policy: Passwordless Authentication - custom",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Password Complexity Policy - custom",
|
||||
"LABELPOLICY": "Labeling Policy - custom",
|
||||
"LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert",
|
||||
"LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen",
|
||||
"CUSTOMDOMAIN": "Domain Verification - available"
|
||||
},
|
||||
"TIERSTATES": {
|
||||
@ -636,6 +638,28 @@
|
||||
"NUMBERERROR": "The password must include a digit.",
|
||||
"PATTERNERROR": "The password does not meet the required pattern."
|
||||
},
|
||||
"PRIVATELABELING": {
|
||||
"TITLE":"Private Labeling",
|
||||
"DESCRIPTION":"Give the login your personalized style and modify its behavior.",
|
||||
"PREVIEW_DESCRIPTION":"Changes of the policy will automatically deployed to preview environment. To view those changes, a 'x-preview' scope will have to be added to your login scopes.",
|
||||
"BTN":"Select File",
|
||||
"ACTIVATEPREVIEW":"Set preview as current configuration",
|
||||
"DARK":"Dark Mode",
|
||||
"LIGHT":"Lighg Mode",
|
||||
"CHANGEVIEW":"Change View",
|
||||
"ACTIVATED":"Policy changes are now LIVE",
|
||||
"THEME":"Theme",
|
||||
"COLORS":"Colors",
|
||||
"FONT":"Font",
|
||||
"ADVANCEDBEHAVIOR":"Advanced Behavior",
|
||||
"PREVIEW": {
|
||||
"TITLE":"Login",
|
||||
"SECOND":"login with your ZITADEL-Account.",
|
||||
"ERROR":"User could not be found!",
|
||||
"PRIMARYBUTTON":"next",
|
||||
"SECONDARYBUTTON":"register"
|
||||
}
|
||||
},
|
||||
"PWD_AGE": {
|
||||
"TITLE": "Password Aging",
|
||||
"DESCRIPTION": "You can set a policy for the aging of passwords. This policy emits a warning after the specific aging time has elapsed."
|
||||
@ -648,6 +672,12 @@
|
||||
"TITLE": "IAM Access Preferences",
|
||||
"DESCRIPTION": "Define access properties of your users."
|
||||
},
|
||||
"PRIVATELABELING_POLICY": {
|
||||
"TITLE": "Private Labeling",
|
||||
"BTN":"Select File",
|
||||
"DESCRIPTION": "Customize the appearance of the Login",
|
||||
"ACTIVATEPREVIEW":"Activate Configuration"
|
||||
},
|
||||
"LOGIN_POLICY": {
|
||||
"TITLE": "Login Policy",
|
||||
"DESCRIPTION": "Define how Users can be authenticated and configure Identity Providers",
|
||||
@ -655,13 +685,6 @@
|
||||
"DESCRIPTIONCREATEMGMT": "Users can choose from the available identity providers below. Note: You can use System-set providers as well as providers set for your organisation only.",
|
||||
"SAVED": "Saved successfully!"
|
||||
},
|
||||
"LABEL": {
|
||||
"TITLE": "Email Labelling Settings",
|
||||
"DESCRIPTION": "Change the look of your emails.",
|
||||
"PRIMARYCOLOR": "Background color",
|
||||
"SECONDARYCOLOR": "Font color",
|
||||
"SAVED": "Saved successfully"
|
||||
},
|
||||
"DEFAULTLABEL": "The currently set guideline corresponds to the standard setting set by the IAM Administrator.",
|
||||
"BTN_INSTALL": "Setup",
|
||||
"BTN_EDIT": "Modify",
|
||||
@ -686,13 +709,18 @@
|
||||
"FORCEMFA": "Force MFA",
|
||||
"FORCEMFA_DESC": "If the option is selected, users have to configure a second factor for login.",
|
||||
"HIDEPASSWORDRESET": "Hide Password reset",
|
||||
"FORCEMFA_DESC": "If the option is selected, the user can't reset his password in the login process."
|
||||
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
|
||||
"HIDELOGINNAMESUFFIX":"Hide Loginname suffix",
|
||||
"ERRORMSGPOPUP":"Show Error in Dialog",
|
||||
"DISABLEWATERMARK":"Disable Watermark"
|
||||
},
|
||||
"RESET": "Reset Policy",
|
||||
"CREATECUSTOM": "Create Custom Policy",
|
||||
"TOAST": {
|
||||
"SET": "Policy set successfully!",
|
||||
"RESETSUCCESS": "Policy reset successfully!"
|
||||
"RESETSUCCESS": "Policy reset successfully!",
|
||||
"UPLOADSUCCESS": "Uploaded successfully!",
|
||||
"UPLOADFAILED":"Upload failed!"
|
||||
}
|
||||
},
|
||||
"ORG_DETAIL": {
|
||||
|
@ -19,6 +19,7 @@
|
||||
@import 'src/app/modules/meta-layout/meta.scss';
|
||||
@import 'src/app/modules/zitadel-tier/zitadel-tier.component.scss';
|
||||
@import 'src/app/modules/onboarding/onboarding.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss';
|
||||
|
||||
@mixin component-themes($theme) {
|
||||
@include avatar-theme($theme);
|
||||
@ -42,4 +43,5 @@
|
||||
@include info-section-theme($theme);
|
||||
@include onboarding-theme($theme);
|
||||
@include tier-theme($theme);
|
||||
@include private-label-theme($theme);
|
||||
}
|
||||
|
@ -146,7 +146,6 @@ $dark-accent: mat.define-palette(mat.$pink-palette);
|
||||
$dark-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
$light-theme: mat.define-light-theme($light-primary, $light-accent, $light-warn);
|
||||
|
||||
$dark-theme: mat.define-dark-theme($dark-primary, $dark-accent, $dark-warn);
|
||||
|
||||
$custom-typography: mat.define-typography-config($font-family: 'Lato');
|
||||
|
@ -266,6 +266,16 @@ Returns the label policy defined by the administrators of ZITADEL
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicy
|
||||
|
||||
> **rpc** GetPreviewLabelPolicy([GetPreviewLabelPolicyRequest](#getpreviewlabelpolicyrequest))
|
||||
[GetPreviewLabelPolicyResponse](#getpreviewlabelpolicyresponse)
|
||||
|
||||
Returns the preview label policy defined by the administrators of ZITADEL
|
||||
|
||||
|
||||
|
||||
|
||||
### UpdateLabelPolicy
|
||||
|
||||
> **rpc** UpdateLabelPolicy([UpdateLabelPolicyRequest](#updatelabelpolicyrequest))
|
||||
@ -277,6 +287,66 @@ it impacts all organisations without a customised policy
|
||||
|
||||
|
||||
|
||||
### ActivateLabelPolicy
|
||||
|
||||
> **rpc** ActivateLabelPolicy([ActivateLabelPolicyRequest](#activatelabelpolicyrequest))
|
||||
[ActivateLabelPolicyResponse](#activatelabelpolicyresponse)
|
||||
|
||||
Activates all changes of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogo
|
||||
|
||||
> **rpc** RemoveLabelPolicyLogo([RemoveLabelPolicyLogoRequest](#removelabelpolicylogorequest))
|
||||
[RemoveLabelPolicyLogoResponse](#removelabelpolicylogoresponse)
|
||||
|
||||
Removes the logo of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogoDark
|
||||
|
||||
> **rpc** RemoveLabelPolicyLogoDark([RemoveLabelPolicyLogoDarkRequest](#removelabelpolicylogodarkrequest))
|
||||
[RemoveLabelPolicyLogoDarkResponse](#removelabelpolicylogodarkresponse)
|
||||
|
||||
Removes the logo dark of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIcon
|
||||
|
||||
> **rpc** RemoveLabelPolicyIcon([RemoveLabelPolicyIconRequest](#removelabelpolicyiconrequest))
|
||||
[RemoveLabelPolicyIconResponse](#removelabelpolicyiconresponse)
|
||||
|
||||
Removes the icon of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIconDark
|
||||
|
||||
> **rpc** RemoveLabelPolicyIconDark([RemoveLabelPolicyIconDarkRequest](#removelabelpolicyicondarkrequest))
|
||||
[RemoveLabelPolicyIconDarkResponse](#removelabelpolicyicondarkresponse)
|
||||
|
||||
Removes the logo dark of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyFont
|
||||
|
||||
> **rpc** RemoveLabelPolicyFont([RemoveLabelPolicyFontRequest](#removelabelpolicyfontrequest))
|
||||
[RemoveLabelPolicyFontResponse](#removelabelpolicyfontresponse)
|
||||
|
||||
Removes the font of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### GetLoginPolicy
|
||||
|
||||
> **rpc** GetLoginPolicy([GetLoginPolicyRequest](#getloginpolicyrequest))
|
||||
@ -570,6 +640,23 @@ failed event. You can find out if it worked on the `failure_count`
|
||||
## Messages
|
||||
|
||||
|
||||
### ActivateLabelPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### ActivateLabelPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddCustomOrgIAMPolicyRequest
|
||||
|
||||
|
||||
@ -975,6 +1062,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.LabelPolicy | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### HealthzRequest
|
||||
This is an empty request
|
||||
|
||||
@ -1316,6 +1420,91 @@ This is an empty response
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyFontRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyFontResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIconDarkRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIconDarkResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIconRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyIconResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogoDarkRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogoDarkResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogoRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveLabelPolicyLogoResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveMultiFactorFromLoginPolicyRequest
|
||||
|
||||
|
||||
@ -1422,6 +1611,8 @@ This is an empty response
|
||||
| label_policy | bool | - | |
|
||||
| custom_domain | bool | - | |
|
||||
| login_policy_password_reset | bool | - | |
|
||||
| label_policy_private_label | bool | - | |
|
||||
| label_policy_watermark | bool | - | |
|
||||
|
||||
|
||||
|
||||
@ -1458,6 +1649,8 @@ This is an empty response
|
||||
| label_policy | bool | - | |
|
||||
| custom_domain | bool | - | |
|
||||
| login_policy_password_reset | bool | - | |
|
||||
| label_policy_private_label | bool | - | |
|
||||
| label_policy_watermark | bool | - | |
|
||||
|
||||
|
||||
|
||||
@ -1669,9 +1862,16 @@ This is an empty response
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| primary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| secondary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| primary_color | string | - | string.max_len: 50<br /> |
|
||||
| hide_login_name_suffix | bool | - | |
|
||||
| warn_color | string | - | string.max_len: 50<br /> |
|
||||
| background_color | string | - | string.max_len: 50<br /> |
|
||||
| font_color | string | - | string.max_len: 50<br /> |
|
||||
| primary_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| background_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| warn_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| font_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| disable_watermark | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -220,6 +220,16 @@ Removed the phone number of the authorized user
|
||||
|
||||
|
||||
|
||||
### RemoveMyAvatar
|
||||
|
||||
> **rpc** RemoveMyAvatar([RemoveMyAvatarRequest](#removemyavatarrequest))
|
||||
[RemoveMyAvatarResponse](#removemyavatarresponse)
|
||||
|
||||
Remove my avatar
|
||||
|
||||
|
||||
|
||||
|
||||
### ListMyLinkedIDPs
|
||||
|
||||
> **rpc** ListMyLinkedIDPs([ListMyLinkedIDPsRequest](#listmylinkedidpsrequest))
|
||||
@ -820,6 +830,23 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### RemoveMyAvatarRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveMyAvatarResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveMyLinkedIDPRequest
|
||||
|
||||
|
||||
|
@ -293,6 +293,16 @@ An sms will be sent to the given phone number to finish the phone verification p
|
||||
|
||||
|
||||
|
||||
### RemoveMyAvatar
|
||||
|
||||
> **rpc** RemoveMyAvatar([RemoveHumanAvatarRequest](#removehumanavatarrequest))
|
||||
[RemoveHumanAvatarResponse](#removehumanavatarresponse)
|
||||
|
||||
Removes the avatar number of the human
|
||||
|
||||
|
||||
|
||||
|
||||
### SetHumanInitialPassword
|
||||
|
||||
> **rpc** SetHumanInitialPassword([SetHumanInitialPasswordRequest](#sethumaninitialpasswordrequest))
|
||||
@ -1581,7 +1591,18 @@ The password lockout policy is not used at the moment
|
||||
> **rpc** GetLabelPolicy([GetLabelPolicyRequest](#getlabelpolicyrequest))
|
||||
[GetLabelPolicyResponse](#getlabelpolicyresponse)
|
||||
|
||||
Returns the label policy of the organisation
|
||||
Returns the active label policy of the organisation
|
||||
With this policy the private labeling can be configured (colors, etc.)
|
||||
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicy
|
||||
|
||||
> **rpc** GetPreviewLabelPolicy([GetPreviewLabelPolicyRequest](#getpreviewlabelpolicyrequest))
|
||||
[GetPreviewLabelPolicyResponse](#getpreviewlabelpolicyresponse)
|
||||
|
||||
Returns the preview label policy of the organisation
|
||||
With this policy the private labeling can be configured (colors, etc.)
|
||||
|
||||
|
||||
@ -1620,6 +1641,66 @@ With this policy the private labeling can be configured (colors, etc.)
|
||||
|
||||
|
||||
|
||||
### ActivateCustomLabelPolicy
|
||||
|
||||
> **rpc** ActivateCustomLabelPolicy([ActivateCustomLabelPolicyRequest](#activatecustomlabelpolicyrequest))
|
||||
[ActivateCustomLabelPolicyResponse](#activatecustomlabelpolicyresponse)
|
||||
|
||||
Activates all changes of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogo
|
||||
|
||||
> **rpc** RemoveCustomLabelPolicyLogo([RemoveCustomLabelPolicyLogoRequest](#removecustomlabelpolicylogorequest))
|
||||
[RemoveCustomLabelPolicyLogoResponse](#removecustomlabelpolicylogoresponse)
|
||||
|
||||
Removes the logo of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogoDark
|
||||
|
||||
> **rpc** RemoveCustomLabelPolicyLogoDark([RemoveCustomLabelPolicyLogoDarkRequest](#removecustomlabelpolicylogodarkrequest))
|
||||
[RemoveCustomLabelPolicyLogoDarkResponse](#removecustomlabelpolicylogodarkresponse)
|
||||
|
||||
Removes the logo dark of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIcon
|
||||
|
||||
> **rpc** RemoveCustomLabelPolicyIcon([RemoveCustomLabelPolicyIconRequest](#removecustomlabelpolicyiconrequest))
|
||||
[RemoveCustomLabelPolicyIconResponse](#removecustomlabelpolicyiconresponse)
|
||||
|
||||
Removes the icon of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIconDark
|
||||
|
||||
> **rpc** RemoveCustomLabelPolicyIconDark([RemoveCustomLabelPolicyIconDarkRequest](#removecustomlabelpolicyicondarkrequest))
|
||||
[RemoveCustomLabelPolicyIconDarkResponse](#removecustomlabelpolicyicondarkresponse)
|
||||
|
||||
Removes the logo dark of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyFont
|
||||
|
||||
> **rpc** RemoveCustomLabelPolicyFont([RemoveCustomLabelPolicyFontRequest](#removecustomlabelpolicyfontrequest))
|
||||
[RemoveCustomLabelPolicyFontResponse](#removecustomlabelpolicyfontresponse)
|
||||
|
||||
Removes the font of the label policy
|
||||
|
||||
|
||||
|
||||
|
||||
### ResetLabelPolicyToDefault
|
||||
|
||||
> **rpc** ResetLabelPolicyToDefault([ResetLabelPolicyToDefaultRequest](#resetlabelpolicytodefaultrequest))
|
||||
@ -1725,6 +1806,23 @@ Change OIDC identity provider configuration of the organisation
|
||||
## Messages
|
||||
|
||||
|
||||
### ActivateCustomLabelPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### ActivateCustomLabelPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### AddAPIAppRequest
|
||||
|
||||
|
||||
@ -1785,9 +1883,16 @@ Change OIDC identity provider configuration of the organisation
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| primary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| secondary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| hide_login_name_suffix | bool | - | |
|
||||
| primary_color | string | - | string.max_len: 50<br /> |
|
||||
| hide_login_name_suffix | bool | hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes | |
|
||||
| warn_color | string | - | string.max_len: 50<br /> |
|
||||
| background_color | string | - | string.max_len: 50<br /> |
|
||||
| font_color | string | - | string.max_len: 50<br /> |
|
||||
| primary_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| background_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| warn_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| font_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| disable_watermark | bool | - | |
|
||||
|
||||
|
||||
|
||||
@ -3074,6 +3179,24 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicyRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### GetPreviewLabelPolicyResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| policy | zitadel.policy.v1.LabelPolicy | - | |
|
||||
| is_default | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### GetProjectByIDRequest
|
||||
|
||||
|
||||
@ -4251,6 +4374,91 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyFontRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyFontResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIconDarkRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIconDarkResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIconRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyIconResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogoDarkRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogoDarkResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogoRequest
|
||||
This is an empty request
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveCustomLabelPolicyLogoResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveHumanAuthFactorOTPRequest
|
||||
|
||||
|
||||
@ -4296,6 +4504,28 @@ This is an empty request
|
||||
|
||||
|
||||
|
||||
### RemoveHumanAvatarRequest
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| user_id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveHumanAvatarResponse
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### RemoveHumanLinkedIDPRequest
|
||||
|
||||
|
||||
@ -4995,9 +5225,16 @@ This is an empty request
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| primary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| secondary_color | string | - | string.min_len: 1<br /> string.max_len: 50<br /> |
|
||||
| primary_color | string | - | string.max_len: 50<br /> |
|
||||
| hide_login_name_suffix | bool | - | |
|
||||
| warn_color | string | - | string.max_len: 50<br /> |
|
||||
| background_color | string | - | string.max_len: 50<br /> |
|
||||
| font_color | string | - | string.max_len: 50<br /> |
|
||||
| primary_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| background_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| warn_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| font_color_dark | string | - | string.max_len: 50<br /> |
|
||||
| disable_watermark | bool | - | |
|
||||
|
||||
|
||||
|
||||
|
@ -16,10 +16,22 @@ title: zitadel/policy.proto
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| details | zitadel.v1.ObjectDetails | - | |
|
||||
| primary_color | string | - | |
|
||||
| secondary_color | string | - | |
|
||||
| is_default | bool | - | |
|
||||
| hide_login_name_suffix | bool | - | |
|
||||
| primary_color | string | hex value for primary color | |
|
||||
| is_default | bool | defines if the organisation's admin changed the policy | |
|
||||
| hide_login_name_suffix | bool | hides the org suffix on the login form if the scope \"urn:zitadel:iam:org:domain:primary:{domainname}\" is set. Details about this scope in https://docs.zitadel.ch/concepts#Reserved_Scopes | |
|
||||
| warn_color | string | hex value for secondary color | |
|
||||
| background_color | string | hex value for background color | |
|
||||
| font_color | string | hex value for font color | |
|
||||
| primary_color_dark | string | hex value for primary color dark theme | |
|
||||
| background_color_dark | string | hex value for background color dark theme | |
|
||||
| warn_color_dark | string | hex value for warn color dark theme | |
|
||||
| font_color_dark | string | hex value for font color dark theme | |
|
||||
| disable_watermark | bool | - | |
|
||||
| logo_url | string | - | |
|
||||
| icon_url | string | - | |
|
||||
| logo_url_dark | string | - | |
|
||||
| icon_url_dark | string | - | |
|
||||
| font_url | string | - | |
|
||||
|
||||
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -43,10 +43,12 @@ require (
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac
|
||||
github.com/lib/pq v1.9.0
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/manifoldco/promptui v0.7.0
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect; indirect github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.10
|
||||
github.com/mitchellh/copystructure v1.1.2 // indirect
|
||||
github.com/muesli/gamut v0.2.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.2.0
|
||||
|
20
go.sum
20
go.sum
@ -60,6 +60,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/DataDog/sketches-go v0.0.1 h1:RtG+76WKgZuz6FIaGsjoPePmadDBkuD/KC6+ZWu78b8=
|
||||
@ -342,6 +343,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -632,6 +635,10 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
|
||||
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/lyft/protoc-gen-star v0.5.1/go.mod h1:9toiA3cC7z5uVbODF7kEQ91Xn7XNFkVUl+SrEe+ZORU=
|
||||
@ -704,6 +711,13 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/muesli/clusters v0.0.0-20180605185049-a07a36e67d36/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY=
|
||||
github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 h1:p4A2Jx7Lm3NV98VRMKlyWd3nqf8obft8NfXlAUmqd3I=
|
||||
github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762/go.mod h1:mw5KDqUj0eLj/6DUNINLVJNoPTFkEuGMHtJsXLviLkY=
|
||||
github.com/muesli/gamut v0.2.0 h1:IZbl/hQzChTXtqDSXL8CDtjdRt58LivY03bGCm1yDyU=
|
||||
github.com/muesli/gamut v0.2.0/go.mod h1:kz1+UJqI1thNtocJlowyqG2o0FNsN0W534VoMVsR9/Y=
|
||||
github.com/muesli/kmeans v0.2.1 h1:ja5AnwfyDCVBCANrAfXr2pOh292FQnSeu1lySACDJU0=
|
||||
github.com/muesli/kmeans v0.2.1/go.mod h1:eNyybq0tX9/iBEP6EMU4Y7dpmGK0uEhODdZpnG1a/iQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@ -901,6 +915,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
@ -908,6 +924,8 @@ github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0B
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b h1:tnWgqoOBmInkt5pbLjagwNVjjT4RdJhFHzL1ebCSRh8=
|
||||
github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -1007,6 +1025,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -300,7 +300,15 @@ func (repo *IAMRepository) GetOrgIAMPolicy(ctx context.Context) (*iam_model.OrgI
|
||||
}
|
||||
|
||||
func (repo *IAMRepository) GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
|
||||
policy, err := repo.View.LabelPolicyByAggregateID(repo.SystemDefaults.IamID)
|
||||
policy, err := repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStateActive))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iam_es_model.LabelPolicyViewToModel(policy), err
|
||||
}
|
||||
|
||||
func (repo *IAMRepository) GetDefaultPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
|
||||
policy, err := repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
|
||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
@ -30,8 +31,8 @@ func (h *handler) Eventstore() v1.Eventstore {
|
||||
return h.es
|
||||
}
|
||||
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults) []query.Handler {
|
||||
return []query.Handler{
|
||||
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, defaults systemdefaults.SystemDefaults, static static.Storage, localDevMode bool) []query.Handler {
|
||||
handlers := []query.Handler{
|
||||
newOrg(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Org"), errorCount, es}),
|
||||
newIAMMember(
|
||||
@ -66,6 +67,13 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
newFeatures(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Features"), errorCount, es}),
|
||||
}
|
||||
if static != nil {
|
||||
handlers = append(handlers, newStyling(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Styling"), errorCount, es},
|
||||
static,
|
||||
localDevMode))
|
||||
}
|
||||
return handlers
|
||||
}
|
||||
|
||||
func (configs Configs) cycleDuration(viewModel string) time.Duration {
|
||||
|
@ -2,6 +2,8 @@ package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/query"
|
||||
@ -77,8 +79,25 @@ func (p *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case model.LabelPolicyAdded:
|
||||
err = policy.AppendEvent(event)
|
||||
case model.LabelPolicyChanged:
|
||||
policy, err = p.view.LabelPolicyByAggregateID(event.AggregateID)
|
||||
case model.LabelPolicyChanged,
|
||||
model.LabelPolicyLogoAdded,
|
||||
model.LabelPolicyLogoRemoved,
|
||||
model.LabelPolicyIconAdded,
|
||||
model.LabelPolicyIconRemoved,
|
||||
model.LabelPolicyLogoDarkAdded,
|
||||
model.LabelPolicyLogoDarkRemoved,
|
||||
model.LabelPolicyIconDarkAdded,
|
||||
model.LabelPolicyIconDarkRemoved,
|
||||
model.LabelPolicyFontAdded,
|
||||
model.LabelPolicyFontRemoved,
|
||||
model.LabelPolicyAssetsRemoved:
|
||||
policy, err = p.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = policy.AppendEvent(event)
|
||||
case model.LabelPolicyActivated:
|
||||
policy, err = p.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
298
internal/admin/repository/eventsourcing/handler/styling.go
Normal file
298
internal/admin/repository/eventsourcing/handler/styling.go
Normal file
@ -0,0 +1,298 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/lucasb-eyer/go-colorful"
|
||||
"github.com/muesli/gamut"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/query"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
|
||||
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
const (
|
||||
stylingTable = "adminapi.styling"
|
||||
)
|
||||
|
||||
type Styling struct {
|
||||
handler
|
||||
static static.Storage
|
||||
subscription *v1.Subscription
|
||||
devMode bool
|
||||
resourceUrl string
|
||||
}
|
||||
|
||||
func newStyling(handler handler, static static.Storage, localDevMode bool) *Styling {
|
||||
h := &Styling{
|
||||
handler: handler,
|
||||
static: static,
|
||||
}
|
||||
prefix := ""
|
||||
if localDevMode {
|
||||
prefix = "/login"
|
||||
}
|
||||
h.resourceUrl = prefix + "/resources/dynamic" //TODO: ?
|
||||
|
||||
h.subscribe()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (m *Styling) subscribe() {
|
||||
m.subscription = m.es.Subscribe(m.AggregateTypes()...)
|
||||
go func() {
|
||||
for event := range m.subscription.Events {
|
||||
query.ReduceEvent(m, event)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *Styling) ViewModel() string {
|
||||
return stylingTable
|
||||
}
|
||||
|
||||
func (_ *Styling) AggregateTypes() []es_models.AggregateType {
|
||||
return []es_models.AggregateType{model.OrgAggregate, iam_es_model.IAMAggregate}
|
||||
}
|
||||
|
||||
func (m *Styling) CurrentSequence() (uint64, error) {
|
||||
sequence, err := m.view.GetLatestStylingSequence()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return sequence.CurrentSequence, nil
|
||||
}
|
||||
|
||||
func (m *Styling) EventQuery() (*es_models.SearchQuery, error) {
|
||||
sequence, err := m.view.GetLatestStylingSequence()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(m.AggregateTypes()...).
|
||||
LatestSequenceFilter(sequence.CurrentSequence), nil
|
||||
}
|
||||
|
||||
func (m *Styling) Reduce(event *es_models.Event) (err error) {
|
||||
switch event.AggregateType {
|
||||
case model.OrgAggregate, iam_es_model.IAMAggregate:
|
||||
err = m.processLabelPolicy(event)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Styling) processLabelPolicy(event *es_models.Event) (err error) {
|
||||
policy := new(iam_model.LabelPolicyView)
|
||||
switch event.Type {
|
||||
case iam_es_model.LabelPolicyAdded, model.LabelPolicyAdded:
|
||||
err = policy.AppendEvent(event)
|
||||
case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged,
|
||||
iam_es_model.LabelPolicyLogoAdded, model.LabelPolicyLogoAdded,
|
||||
iam_es_model.LabelPolicyLogoRemoved, model.LabelPolicyLogoRemoved,
|
||||
iam_es_model.LabelPolicyIconAdded, model.LabelPolicyIconAdded,
|
||||
iam_es_model.LabelPolicyIconRemoved, model.LabelPolicyIconRemoved,
|
||||
iam_es_model.LabelPolicyLogoDarkAdded, model.LabelPolicyLogoDarkAdded,
|
||||
iam_es_model.LabelPolicyLogoDarkRemoved, model.LabelPolicyLogoDarkRemoved,
|
||||
iam_es_model.LabelPolicyIconDarkAdded, model.LabelPolicyIconDarkAdded,
|
||||
iam_es_model.LabelPolicyIconDarkRemoved, model.LabelPolicyIconDarkRemoved,
|
||||
iam_es_model.LabelPolicyFontAdded, model.LabelPolicyFontAdded,
|
||||
iam_es_model.LabelPolicyFontRemoved, model.LabelPolicyFontRemoved,
|
||||
iam_es_model.LabelPolicyAssetsRemoved, model.LabelPolicyAssetsRemoved:
|
||||
policy, err = m.view.StylingByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = policy.AppendEvent(event)
|
||||
|
||||
case iam_es_model.LabelPolicyActivated, model.LabelPolicyActivated:
|
||||
policy, err = m.view.StylingByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = policy.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.generateStylingFile(policy)
|
||||
default:
|
||||
return m.view.ProcessedStylingSequence(event)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.view.PutStyling(policy, event)
|
||||
}
|
||||
|
||||
func (m *Styling) OnError(event *es_models.Event, err error) error {
|
||||
logging.LogWithFields("SPOOL-2m9fs", "id", event.AggregateID).WithError(err).Warn("something went wrong in label policy handler")
|
||||
return spooler.HandleError(event, err, m.view.GetLatestLabelPolicyFailedEvent, m.view.ProcessedLabelPolicyFailedEvent, m.view.ProcessedLabelPolicySequence, m.errorCountUntilSkip)
|
||||
}
|
||||
|
||||
func (m *Styling) OnSuccess() error {
|
||||
return spooler.HandleSuccess(m.view.UpdateLabelPolicySpoolerRunTimestamp)
|
||||
}
|
||||
|
||||
func (m *Styling) generateStylingFile(policy *iam_model.LabelPolicyView) error {
|
||||
reader, size, err := m.writeFile(policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.uploadFilesToBucket(policy.AggregateID, "text/css", reader, size)
|
||||
}
|
||||
|
||||
func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64, error) {
|
||||
cssContent := ""
|
||||
cssContent += fmt.Sprint(":root {")
|
||||
if policy.PrimaryColor != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.PrimaryColor)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-primary-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
|
||||
if policy.BackgroundColor != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.BackgroundColor)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-background-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
if policy.WarnColor != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.WarnColor)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-warn-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
var fontname string
|
||||
if policy.FontURL != "" {
|
||||
split := strings.Split(policy.FontURL, "/")
|
||||
fontname = split[len(split)-1]
|
||||
cssContent += fmt.Sprintf("--zitadel-font-family: %s;", fontname)
|
||||
}
|
||||
cssContent += fmt.Sprint("}")
|
||||
if policy.FontURL != "" {
|
||||
cssContent += fmt.Sprintf(fontFaceTemplate, fontname, m.resourceUrl, policy.AggregateID, policy.FontURL)
|
||||
}
|
||||
cssContent += fmt.Sprint(".lgn-dark-theme {")
|
||||
if policy.PrimaryColorDark != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.PrimaryColorDark)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-primary-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
if policy.BackgroundColorDark != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.BackgroundColorDark)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-background-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
if policy.WarnColorDark != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.WarnColorDark)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-warn-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
if policy.FontColorDark != "" {
|
||||
palette := m.generateColorPaletteRGBA255(policy.FontColorDark)
|
||||
for i, color := range palette {
|
||||
cssContent += fmt.Sprintf("--zitadel-color-font-%v: %s;", i, color)
|
||||
}
|
||||
}
|
||||
cssContent += fmt.Sprint("}")
|
||||
|
||||
data := []byte(cssContent)
|
||||
buffer := bytes.NewBuffer(data)
|
||||
return buffer, int64(buffer.Len()), nil
|
||||
}
|
||||
|
||||
const fontFaceTemplate = `
|
||||
@font-face {
|
||||
font-family: '%s';
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: url(%s?orgId=%s&filename=%s);
|
||||
}
|
||||
`
|
||||
|
||||
func (m *Styling) uploadFilesToBucket(aggregateID, contentType string, reader io.Reader, size int64) error {
|
||||
fileName := domain.CssPath + "/" + domain.CssVariablesFileName
|
||||
_, err := m.static.PutObject(context.Background(), aggregateID, fileName, contentType, reader, size, true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Styling) generateColorPaletteRGBA255(hex string) map[string]string {
|
||||
palette := make(map[string]string)
|
||||
defaultColor := gamut.Hex(hex)
|
||||
|
||||
color50, ok := colorful.MakeColor(gamut.Lighter(defaultColor, 1.0))
|
||||
if ok {
|
||||
palette["50"] = cssRGB(color50.RGB255())
|
||||
}
|
||||
|
||||
color100, ok := colorful.MakeColor(gamut.Lighter(defaultColor, 0.8))
|
||||
if ok {
|
||||
palette["100"] = cssRGB(color100.RGB255())
|
||||
}
|
||||
|
||||
color200, ok := colorful.MakeColor(gamut.Lighter(defaultColor, 0.6))
|
||||
if ok {
|
||||
palette["200"] = cssRGB(color200.RGB255())
|
||||
}
|
||||
|
||||
color300, ok := colorful.MakeColor(gamut.Lighter(defaultColor, 0.4))
|
||||
if ok {
|
||||
palette["300"] = cssRGB(color300.RGB255())
|
||||
}
|
||||
|
||||
color400, ok := colorful.MakeColor(gamut.Lighter(defaultColor, 0.1))
|
||||
if ok {
|
||||
palette["400"] = cssRGB(color400.RGB255())
|
||||
}
|
||||
|
||||
color500, ok := colorful.MakeColor(defaultColor)
|
||||
if ok {
|
||||
palette["500"] = cssRGB(color500.RGB255())
|
||||
}
|
||||
|
||||
color600, ok := colorful.MakeColor(gamut.Darker(defaultColor, 0.1))
|
||||
if ok {
|
||||
palette["600"] = cssRGB(color600.RGB255())
|
||||
}
|
||||
|
||||
color700, ok := colorful.MakeColor(gamut.Darker(defaultColor, 0.2))
|
||||
if ok {
|
||||
palette["700"] = cssRGB(color700.RGB255())
|
||||
}
|
||||
|
||||
color800, ok := colorful.MakeColor(gamut.Darker(defaultColor, 0.3))
|
||||
if ok {
|
||||
palette["800"] = cssRGB(color800.RGB255())
|
||||
}
|
||||
|
||||
color900, ok := colorful.MakeColor(gamut.Darker(defaultColor, 0.4))
|
||||
if ok {
|
||||
palette["900"] = cssRGB(color900.RGB255())
|
||||
}
|
||||
|
||||
colorContrast, ok := colorful.MakeColor(gamut.Contrast(defaultColor))
|
||||
if ok {
|
||||
palette["contrast"] = cssRGB(colorContrast.RGB255())
|
||||
}
|
||||
|
||||
return palette
|
||||
}
|
||||
|
||||
func cssRGB(r, g, b uint8) string {
|
||||
return fmt.Sprintf("rgb(%v, %v, %v)", r, g, b)
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/config/types"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@ -28,7 +29,7 @@ type EsRepository struct {
|
||||
eventstore.UserRepo
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRepository, error) {
|
||||
func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, static static.Storage, roles []string, localDevMode bool) (*EsRepository, error) {
|
||||
es, err := v1.Start(conf.Eventstore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -42,7 +43,7 @@ func Start(ctx context.Context, conf Config, systemDefaults sd.SystemDefaults, r
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults)
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, systemDefaults, static, localDevMode)
|
||||
|
||||
return &EsRepository{
|
||||
spooler: spool,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
|
||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/handler"
|
||||
"github.com/caos/zitadel/internal/admin/repository/eventsourcing/view"
|
||||
@ -17,12 +18,12 @@ type SpoolerConfig struct {
|
||||
Handlers handler.Configs
|
||||
}
|
||||
|
||||
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, defaults systemdefaults.SystemDefaults) *spooler.Spooler {
|
||||
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, defaults systemdefaults.SystemDefaults, static static.Storage, localDevMode bool) *spooler.Spooler {
|
||||
spoolerConfig := spooler.Config{
|
||||
Eventstore: es,
|
||||
Locker: &locker{dbClient: sql},
|
||||
ConcurrentWorkers: c.ConcurrentWorkers,
|
||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, defaults),
|
||||
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, defaults, static, localDevMode),
|
||||
}
|
||||
spool := spoolerConfig.New()
|
||||
spool.Start()
|
||||
|
@ -11,8 +11,8 @@ const (
|
||||
labelPolicyTable = "adminapi.label_policies"
|
||||
)
|
||||
|
||||
func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyView, error) {
|
||||
return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID)
|
||||
func (v *View) LabelPolicyByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) {
|
||||
return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTable, aggregateID, state)
|
||||
}
|
||||
|
||||
func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error {
|
||||
|
44
internal/admin/repository/eventsourcing/view/styling.go
Normal file
44
internal/admin/repository/eventsourcing/view/styling.go
Normal file
@ -0,0 +1,44 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/iam/repository/view"
|
||||
"github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
global_view "github.com/caos/zitadel/internal/view/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
stylingTyble = "adminapi.styling"
|
||||
)
|
||||
|
||||
func (v *View) StylingByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) {
|
||||
return view.GetLabelPolicyByAggregateIDAndState(v.Db, stylingTyble, aggregateID, state)
|
||||
}
|
||||
|
||||
func (v *View) PutStyling(policy *model.LabelPolicyView, event *models.Event) error {
|
||||
err := view.PutLabelPolicy(v.Db, stylingTyble, policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedStylingSequence(event)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestStylingSequence() (*global_view.CurrentSequence, error) {
|
||||
return v.latestSequence(stylingTyble)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedStylingSequence(event *models.Event) error {
|
||||
return v.saveCurrentSequence(stylingTyble, event)
|
||||
}
|
||||
|
||||
func (v *View) UpdateStylingSpoolerRunTimestamp() error {
|
||||
return v.updateSpoolerRunSequence(stylingTyble)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestStylingFailedEvent(sequence uint64) (*global_view.FailedEvent, error) {
|
||||
return v.latestFailedEvent(stylingTyble, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedStylingFailedEvent(failedEvent *global_view.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
@ -24,6 +24,7 @@ type IAMRepository interface {
|
||||
ExternalIDPsByIDPConfigIDFromDefaultPolicy(ctx context.Context, idpConfigID string) ([]*usr_model.ExternalIDPView, error)
|
||||
|
||||
GetDefaultLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error)
|
||||
GetDefaultPreviewLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error)
|
||||
|
||||
GetDefaultMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error)
|
||||
|
||||
|
194
internal/api/assets/asset.go
Normal file
194
internal/api/assets/asset.go
Normal file
@ -0,0 +1,194 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/management/repository"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
errorHandler ErrorHandler
|
||||
storage static.Storage
|
||||
commands *command.Commands
|
||||
authInterceptor *http_mw.AuthInterceptor
|
||||
idGenerator id.Generator
|
||||
orgRepo repository.OrgRepository
|
||||
}
|
||||
|
||||
func (h *Handler) AuthInterceptor() *http_mw.AuthInterceptor {
|
||||
return h.authInterceptor
|
||||
}
|
||||
|
||||
func (h *Handler) Commands() *command.Commands {
|
||||
return h.commands
|
||||
}
|
||||
|
||||
func (h *Handler) ErrorHandler() ErrorHandler {
|
||||
return DefaultErrorHandler
|
||||
}
|
||||
|
||||
func (h *Handler) Storage() static.Storage {
|
||||
return h.storage
|
||||
}
|
||||
|
||||
type Uploader interface {
|
||||
Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error
|
||||
ObjectName(data authz.CtxData) (string, error)
|
||||
BucketName(data authz.CtxData) string
|
||||
ContentTypeAllowed(contentType string) bool
|
||||
MaxFileSize() int64
|
||||
}
|
||||
|
||||
type Downloader interface {
|
||||
ObjectName(ctx context.Context, path string) (string, error)
|
||||
BucketName(ctx context.Context, id string) string
|
||||
}
|
||||
|
||||
type ErrorHandler func(http.ResponseWriter, *http.Request, error)
|
||||
|
||||
func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error) {
|
||||
logging.Log("ASSET-g5ef1").WithError(err).WithField("uri", r.RequestURI).Error("error occurred on asset api")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func NewHandler(
|
||||
commands *command.Commands,
|
||||
verifier *authz.TokenVerifier,
|
||||
authConfig authz.Config,
|
||||
idGenerator id.Generator,
|
||||
storage static.Storage,
|
||||
orgRepo repository.OrgRepository,
|
||||
) http.Handler {
|
||||
h := &Handler{
|
||||
commands: commands,
|
||||
errorHandler: DefaultErrorHandler,
|
||||
authInterceptor: http_mw.AuthorizationInterceptor(verifier, authConfig),
|
||||
idGenerator: idGenerator,
|
||||
storage: storage,
|
||||
orgRepo: orgRepo,
|
||||
}
|
||||
|
||||
verifier.RegisterServer("Management-API", "assets", AssetsService_AuthMethods) //TODO: separate api?
|
||||
router := mux.NewRouter()
|
||||
RegisterRoutes(router, h)
|
||||
router.PathPrefix("/{id}").Methods("GET").HandlerFunc(DownloadHandleFunc(h, h.GetFile()))
|
||||
return router
|
||||
}
|
||||
|
||||
func (h *Handler) GetFile() Downloader {
|
||||
return &publicFileDownloader{}
|
||||
}
|
||||
|
||||
type publicFileDownloader struct{}
|
||||
|
||||
func (l *publicFileDownloader) ObjectName(_ context.Context, path string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (l *publicFileDownloader) BucketName(_ context.Context, id string) string {
|
||||
return id
|
||||
}
|
||||
|
||||
const maxMemory = 2 << 20
|
||||
const paramFile = "file"
|
||||
|
||||
func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
err := r.ParseMultipartForm(maxMemory)
|
||||
file, handler, err := r.FormFile(paramFile)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err = file.Close()
|
||||
logging.Log("UPLOAD-GDg34").OnError(err).Warn("could not close file")
|
||||
}()
|
||||
contentType := handler.Header.Get("content-type")
|
||||
size := handler.Size
|
||||
if !uploader.ContentTypeAllowed(contentType) {
|
||||
s.ErrorHandler()(w, r, caos_errs.ThrowInvalidArgument(nil, "UPLOAD-Dbvfs", "invalid content-type"))
|
||||
return
|
||||
}
|
||||
if size > uploader.MaxFileSize() {
|
||||
s.ErrorHandler()(w, r, caos_errs.ThrowInvalidArgumentf(nil, "UPLOAD-Bfb32", "file to big, max file size is %v", uploader.MaxFileSize()))
|
||||
return
|
||||
}
|
||||
|
||||
bucketName := uploader.BucketName(ctxData)
|
||||
objectName, err := uploader.ObjectName(ctxData)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, file, size)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
err = uploader.Callback(ctx, info, ctxData.OrgID, s.Commands())
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if s.Storage() == nil {
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
id := mux.Vars(r)["id"]
|
||||
bucketName := downloader.BucketName(ctx, id)
|
||||
path := ""
|
||||
if id != "" {
|
||||
path = strings.Split(r.RequestURI, id+"/")[1]
|
||||
}
|
||||
objectName, err := downloader.ObjectName(ctx, path)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
if objectName == "" {
|
||||
s.ErrorHandler()(w, r, caos_errs.ThrowNotFound(nil, "UPLOAD-adf4f", "file not found"))
|
||||
return
|
||||
}
|
||||
reader, getInfo, err := s.Storage().GetObject(ctx, bucketName, objectName)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
info, err := getInfo()
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("content-length", strconv.FormatInt(info.Size, 10))
|
||||
w.Header().Set("content-type", info.ContentType)
|
||||
w.Header().Set("ETag", info.ETag)
|
||||
w.Write(data)
|
||||
}
|
||||
}
|
119
internal/api/assets/generator/asset.yaml
Normal file
119
internal/api/assets/generator/asset.yaml
Normal file
@ -0,0 +1,119 @@
|
||||
Services:
|
||||
IAM:
|
||||
Prefix: "/iam"
|
||||
Methods:
|
||||
DefaultLabelPolicyLogo:
|
||||
Path: "/policy/label/logo"
|
||||
HasDarkMode: true
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: iam.policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: iam.policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: iam.policy.read
|
||||
DefaultLabelPolicyIcon:
|
||||
Path: "/policy/label/icon"
|
||||
HasDarkMode: true
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: iam.policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: iam.policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: iam.policy.read
|
||||
DefaultLabelPolicyFont:
|
||||
Path: "/policy/label/font"
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: iam.policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: iam.policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: iam.policy.read
|
||||
Org:
|
||||
Prefix: "/org"
|
||||
Methods:
|
||||
OrgLabelPolicyLogo:
|
||||
Path: "/policy/label/logo"
|
||||
Feature: "label_policy.private_label"
|
||||
HasDarkMode: true
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: policy.read
|
||||
OrgLabelPolicyIcon:
|
||||
Path: "/policy/label/icon"
|
||||
Feature: "label_policy.private_label"
|
||||
HasDarkMode: true
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: policy.read
|
||||
OrgLabelPolicyFont:
|
||||
Path: "/policy/label/font"
|
||||
Feature: "label_policy.private_label"
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: policy.write
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: policy.read
|
||||
- Name: GetPreview
|
||||
Comment:
|
||||
Type: preview
|
||||
Permission: policy.read
|
||||
Users:
|
||||
Prefix: "/users"
|
||||
Methods:
|
||||
MyUserAvatar:
|
||||
Path: "/me/avatar"
|
||||
Features: "label_policy.private_label"
|
||||
Handlers:
|
||||
- Name: Upload
|
||||
Comment:
|
||||
Type: upload
|
||||
Permission: authenticated
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: authenticated
|
200
internal/api/assets/generator/asset_generator.go
Normal file
200
internal/api/assets/generator/asset_generator.go
Normal file
@ -0,0 +1,200 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/config"
|
||||
)
|
||||
|
||||
var (
|
||||
directory = flag.String("directory", "./", "working directory: asset.yaml must be in this directory, files will be generated into parent directory")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
configFile := *directory + "asset.yaml"
|
||||
authz, err := os.OpenFile(*directory+"../authz.go", os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0755)
|
||||
logging.Log("ASSETS-Gn31f").OnError(err).Fatal("cannot open authz file")
|
||||
router, err := os.OpenFile(*directory+"../router.go", os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0755)
|
||||
logging.Log("ASSETS-ABen3").OnError(err).Fatal("cannot open router file")
|
||||
GenerateAssetHandler(configFile, authz, router)
|
||||
}
|
||||
|
||||
type Method struct {
|
||||
Path string
|
||||
Feature string
|
||||
HasDarkMode bool
|
||||
Handlers []Handler
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
Name string
|
||||
Comment string
|
||||
Type HandlerType
|
||||
Permission string
|
||||
}
|
||||
|
||||
func (a Handler) Method() string {
|
||||
if a.Type == MethodTypeUpload {
|
||||
return "POST"
|
||||
}
|
||||
return "GET"
|
||||
}
|
||||
|
||||
func (a Handler) PathSuffix() string {
|
||||
if a.Type == MethodTypePreview {
|
||||
return "/_preview"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a Handler) MethodReturn() string {
|
||||
if a.Type == MethodTypeUpload {
|
||||
return "Uploader"
|
||||
}
|
||||
if a.Type == MethodTypeDownload {
|
||||
return "Downloader"
|
||||
}
|
||||
if a.Type == MethodTypePreview {
|
||||
return "Downloader"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a Handler) HandlerType() string {
|
||||
if a.Type == MethodTypeUpload {
|
||||
return "UploadHandleFunc"
|
||||
}
|
||||
if a.Type == MethodTypeDownload {
|
||||
return "DownloadHandleFunc"
|
||||
}
|
||||
if a.Type == MethodTypePreview {
|
||||
return "DownloadHandleFunc"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HandlerType string
|
||||
|
||||
const (
|
||||
MethodTypeUpload = "upload"
|
||||
MethodTypeDownload = "download"
|
||||
MethodTypePreview = "preview"
|
||||
)
|
||||
|
||||
type Services map[string]Service
|
||||
|
||||
type Service struct {
|
||||
Prefix string
|
||||
Methods map[string]Method
|
||||
}
|
||||
|
||||
func GenerateAssetHandler(configFilePath string, output io.Writer, output2 io.Writer) {
|
||||
conf := new(struct {
|
||||
Services Services
|
||||
})
|
||||
err := config.Read(conf, configFilePath)
|
||||
logging.Log("ASSETS-Dgbn4").OnError(err).Fatal("cannot read config")
|
||||
tmplAuthz, err := template.New("").Parse(authzTmpl)
|
||||
logging.Log("ASSETS-BGbbg").OnError(err).Fatal("cannot parse authz template")
|
||||
tmplRouter, err := template.New("").Parse(routerTmpl)
|
||||
logging.Log("ASSETS-gh4rq").OnError(err).Fatal("cannot parse router template")
|
||||
data := &struct {
|
||||
GoPkgName string
|
||||
Name string
|
||||
Prefix string
|
||||
Services Services
|
||||
}{
|
||||
GoPkgName: "assets",
|
||||
Name: "AssetsService",
|
||||
Prefix: "/assets/v1",
|
||||
Services: conf.Services,
|
||||
}
|
||||
err = tmplAuthz.Execute(output, data)
|
||||
logging.Log("ASSETS-BHngj").OnError(err).Fatal("cannot generate authz")
|
||||
err = tmplRouter.Execute(output2, data)
|
||||
logging.Log("ASSETS-Bfd41").OnError(err).Fatal("cannot generate router")
|
||||
}
|
||||
|
||||
const authzTmpl = `package {{.GoPkgName}}
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
)
|
||||
|
||||
/**
|
||||
* {{.Name}}
|
||||
*/
|
||||
|
||||
{{ $prefix := .Prefix }}
|
||||
var {{.Name}}_AuthMethods = authz.MethodMapping {
|
||||
{{ range $service := .Services}}
|
||||
{{ range $method := .Methods}}
|
||||
{{ range $handler := .Handlers}}
|
||||
{{ if (or $method.Feature $handler.Permission) }}
|
||||
"{{$handler.Method}}:{{$prefix}}{{$service.Prefix}}{{$method.Path}}{{$handler.PathSuffix}}": authz.Option{
|
||||
Permission: "{{$handler.Permission}}",
|
||||
Feature: "{{$method.Feature}}",
|
||||
},
|
||||
{{ if $method.HasDarkMode }}
|
||||
"{{$handler.Method}}:{{$prefix}}{{$service.Prefix}}{{$method.Path}}/dark{{$handler.PathSuffix}}": authz.Option{
|
||||
Permission: "{{$handler.Permission}}",
|
||||
Feature: "{{$method.Feature}}",
|
||||
},
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
}
|
||||
`
|
||||
|
||||
const routerTmpl = `package {{.GoPkgName}}
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
type {{.Name}} interface {
|
||||
AuthInterceptor() *http_mw.AuthInterceptor
|
||||
Commands() *command.Commands
|
||||
ErrorHandler() ErrorHandler
|
||||
Storage() static.Storage
|
||||
|
||||
{{ range $service := .Services}}
|
||||
{{ range $methodName, $method := .Methods}}
|
||||
{{ range $handler := .Handlers}}
|
||||
{{$handler.Name}}{{$methodName}}() {{if $handler.MethodReturn}}{{$handler.MethodReturn}}{{end}}
|
||||
{{ if $method.HasDarkMode }}
|
||||
{{$handler.Name}}{{$methodName}}Dark() {{if $handler.MethodReturn}}{{$handler.MethodReturn}}{{end}}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
func RegisterRoutes(router *mux.Router, s {{.Name}}) {
|
||||
|
||||
router.Use(s.AuthInterceptor().Handler)
|
||||
|
||||
{{ range $service := .Services}}
|
||||
{{ range $methodName, $method := .Methods}}
|
||||
{{ range $handler := .Handlers}}
|
||||
router.Path("{{$service.Prefix}}{{$method.Path}}{{$handler.PathSuffix}}").Methods("{{$handler.Method}}").HandlerFunc({{if $handler.HandlerType}}{{$handler.HandlerType}}(s, {{end}}s.{{$handler.Name}}{{$methodName}}(){{if $handler.HandlerType}}){{end}})
|
||||
{{ if $method.HasDarkMode }}
|
||||
router.Path("{{$service.Prefix}}{{$method.Path}}/dark{{$handler.PathSuffix}}").Methods("{{$handler.Method}}").HandlerFunc({{if $handler.HandlerType}}{{$handler.HandlerType}}(s, {{end}}s.{{$handler.Name}}{{$methodName}}Dark(){{if $handler.HandlerType}}){{end}})
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
}
|
||||
`
|
377
internal/api/assets/login_policy.go
Normal file
377
internal/api/assets/login_policy.go
Normal file
@ -0,0 +1,377 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/management/repository"
|
||||
)
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyLogo() Uploader {
|
||||
return &labelPolicyLogoUploader{h.idGenerator, false, true, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyLogoDark() Uploader {
|
||||
return &labelPolicyLogoUploader{h.idGenerator, true, true, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadOrgLabelPolicyLogo() Uploader {
|
||||
return &labelPolicyLogoUploader{h.idGenerator, false, false, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadOrgLabelPolicyLogoDark() Uploader {
|
||||
return &labelPolicyLogoUploader{h.idGenerator, true, false, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
type labelPolicyLogoUploader struct {
|
||||
idGenerator id.Generator
|
||||
darkMode bool
|
||||
defaultPolicy bool
|
||||
contentTypes []string
|
||||
maxSize int64
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) ContentTypeAllowed(contentType string) bool {
|
||||
for _, ct := range l.contentTypes {
|
||||
if strings.HasPrefix(contentType, ct) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
suffixID, err := l.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prefix := domain.LabelPolicyLogoPath
|
||||
if l.darkMode {
|
||||
return prefix + "-" + domain.Dark + "-" + suffixID, nil
|
||||
}
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) BucketName(ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
if l.darkMode {
|
||||
_, err := commands.AddLogoDarkDefaultLabelPolicy(ctx, info.Key)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddLogoDefaultLabelPolicy(ctx, info.Key)
|
||||
return err
|
||||
}
|
||||
if l.darkMode {
|
||||
_, err := commands.AddLogoDarkLabelPolicy(ctx, orgID, info.Key)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddLogoLabelPolicy(ctx, orgID, info.Key)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) GetDefaultLabelPolicyLogo() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetDefaultLabelPolicyLogoDark() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewDefaultLabelPolicyLogo() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewDefaultLabelPolicyLogoDark() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrgLabelPolicyLogo() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrgLabelPolicyLogoDark() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewOrgLabelPolicyLogo() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewOrgLabelPolicyLogoDark() Downloader {
|
||||
return &labelPolicyLogoDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: true}
|
||||
}
|
||||
|
||||
type labelPolicyLogoDownloader struct {
|
||||
org repository.OrgRepository
|
||||
darkMode bool
|
||||
defaultPolicy bool
|
||||
preview bool
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoDownloader) ObjectName(ctx context.Context, path string) (string, error) {
|
||||
policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
if l.darkMode {
|
||||
return policy.LogoDarkURL, nil
|
||||
}
|
||||
return policy.LogoURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoDownloader) BucketName(ctx context.Context, id string) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyIcon() Uploader {
|
||||
return &labelPolicyIconUploader{h.idGenerator, false, true, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyIconDark() Uploader {
|
||||
return &labelPolicyIconUploader{h.idGenerator, true, true, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadOrgLabelPolicyIcon() Uploader {
|
||||
return &labelPolicyIconUploader{h.idGenerator, false, false, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadOrgLabelPolicyIconDark() Uploader {
|
||||
return &labelPolicyIconUploader{h.idGenerator, true, false, []string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
type labelPolicyIconUploader struct {
|
||||
idGenerator id.Generator
|
||||
darkMode bool
|
||||
defaultPolicy bool
|
||||
contentTypes []string
|
||||
maxSize int64
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) ContentTypeAllowed(contentType string) bool {
|
||||
for _, ct := range l.contentTypes {
|
||||
if strings.HasPrefix(contentType, ct) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
suffixID, err := l.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prefix := domain.LabelPolicyIconPath
|
||||
if l.darkMode {
|
||||
return prefix + "-" + domain.Dark + "-" + suffixID, nil
|
||||
}
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) BucketName(ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
if l.darkMode {
|
||||
_, err := commands.AddIconDarkDefaultLabelPolicy(ctx, info.Key)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddIconDefaultLabelPolicy(ctx, info.Key)
|
||||
return err
|
||||
}
|
||||
|
||||
if l.darkMode {
|
||||
_, err := commands.AddIconDarkLabelPolicy(ctx, orgID, info.Key)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddIconLabelPolicy(ctx, orgID, info.Key)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) GetDefaultLabelPolicyIcon() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetDefaultLabelPolicyIconDark() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewDefaultLabelPolicyIcon() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewDefaultLabelPolicyIconDark() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrgLabelPolicyIcon() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrgLabelPolicyIconDark() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewOrgLabelPolicyIcon() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: false, defaultPolicy: false, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewOrgLabelPolicyIconDark() Downloader {
|
||||
return &labelPolicyIconDownloader{org: h.orgRepo, darkMode: true, defaultPolicy: false, preview: true}
|
||||
}
|
||||
|
||||
type labelPolicyIconDownloader struct {
|
||||
org repository.OrgRepository
|
||||
darkMode bool
|
||||
defaultPolicy bool
|
||||
preview bool
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconDownloader) ObjectName(ctx context.Context, path string) (string, error) {
|
||||
policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
if l.darkMode {
|
||||
return policy.IconDarkURL, nil
|
||||
}
|
||||
return policy.IconURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconDownloader) BucketName(ctx context.Context, id string) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyFont() Uploader {
|
||||
return &labelPolicyFontUploader{h.idGenerator, true, []string{"font/"}, 1 << 19}
|
||||
}
|
||||
|
||||
func (h *Handler) UploadOrgLabelPolicyFont() Uploader {
|
||||
return &labelPolicyFontUploader{h.idGenerator, false, []string{"font/"}, 1 << 19}
|
||||
}
|
||||
|
||||
type labelPolicyFontUploader struct {
|
||||
idGenerator id.Generator
|
||||
defaultPolicy bool
|
||||
contentTypes []string
|
||||
maxSize int64
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) ContentTypeAllowed(contentType string) bool {
|
||||
for _, ct := range l.contentTypes {
|
||||
if strings.HasPrefix(contentType, ct) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
suffixID, err := l.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prefix := domain.LabelPolicyFontPath
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) BucketName(ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
_, err := commands.AddFontDefaultLabelPolicy(ctx, info.Key)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddFontLabelPolicy(ctx, orgID, info.Key)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) GetDefaultLabelPolicyFont() Downloader {
|
||||
return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewDefaultLabelPolicyFont() Downloader {
|
||||
return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
func (h *Handler) GetOrgLabelPolicyFont() Downloader {
|
||||
return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: false, preview: false}
|
||||
}
|
||||
|
||||
func (h *Handler) GetPreviewOrgLabelPolicyFont() Downloader {
|
||||
return &labelPolicyFontDownloader{org: h.orgRepo, defaultPolicy: true, preview: true}
|
||||
}
|
||||
|
||||
type labelPolicyFontDownloader struct {
|
||||
org repository.OrgRepository
|
||||
defaultPolicy bool
|
||||
preview bool
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontDownloader) ObjectName(ctx context.Context, path string) (string, error) {
|
||||
policy, err := getLabelPolicy(ctx, l.defaultPolicy, l.preview, l.org)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return policy.FontURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontDownloader) BucketName(ctx context.Context, id string) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
}
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
}
|
||||
|
||||
func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, orgRepo repository.OrgRepository) (*model.LabelPolicyView, error) {
|
||||
if defaultPolicy {
|
||||
if preview {
|
||||
return orgRepo.GetPreviewDefaultLabelPolicy(ctx)
|
||||
}
|
||||
return orgRepo.GetDefaultLabelPolicy(ctx)
|
||||
}
|
||||
if preview {
|
||||
return orgRepo.GetPreviewLabelPolicy(ctx)
|
||||
}
|
||||
return orgRepo.GetLabelPolicy(ctx)
|
||||
}
|
59
internal/api/assets/user_avatar.go
Normal file
59
internal/api/assets/user_avatar.go
Normal file
@ -0,0 +1,59 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
func (h *Handler) UploadMyUserAvatar() Uploader {
|
||||
return &myHumanAvatarUploader{[]string{"image/"}, 1 << 19}
|
||||
}
|
||||
|
||||
type myHumanAvatarUploader struct {
|
||||
contentTypes []string
|
||||
maxSize int64
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) ContentTypeAllowed(contentType string) bool {
|
||||
for _, ct := range l.contentTypes {
|
||||
if strings.HasPrefix(contentType, ct) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) ObjectName(ctxData authz.CtxData) (string, error) {
|
||||
return domain.GetHumanAvatarAssetPath(ctxData.UserID), nil
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) BucketName(ctxData authz.CtxData) string {
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
_, err := commands.AddHumanAvatar(ctx, orgID, authz.GetCtxData(ctx).UserID, info.Key)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) GetMyUserAvatar() Downloader {
|
||||
return &myHumanAvatarDownloader{}
|
||||
}
|
||||
|
||||
type myHumanAvatarDownloader struct{}
|
||||
|
||||
func (l *myHumanAvatarDownloader) ObjectName(ctx context.Context, path string) (string, error) {
|
||||
return domain.GetHumanAvatarAssetPath(authz.GetCtxData(ctx).UserID), nil
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarDownloader) BucketName(ctx context.Context, id string) string {
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
}
|
@ -71,7 +71,8 @@ func setDefaultFeaturesRequestToDomain(req *admin_pb.SetDefaultFeaturesRequest)
|
||||
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
|
||||
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
|
||||
PasswordComplexityPolicy: req.PasswordComplexityPolicy,
|
||||
LabelPolicy: req.LabelPolicy,
|
||||
LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel,
|
||||
LabelPolicyWatermark: req.LabelPolicyWatermark,
|
||||
CustomDomain: req.CustomDomain,
|
||||
}
|
||||
}
|
||||
@ -90,7 +91,8 @@ func setOrgFeaturesRequestToDomain(req *admin_pb.SetOrgFeaturesRequest) *domain.
|
||||
LoginPolicyUsernameLogin: req.LoginPolicyUsernameLogin,
|
||||
LoginPolicyPasswordReset: req.LoginPolicyPasswordReset,
|
||||
PasswordComplexityPolicy: req.PasswordComplexityPolicy,
|
||||
LabelPolicy: req.LabelPolicy,
|
||||
LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel,
|
||||
LabelPolicyWatermark: req.LabelPolicyWatermark,
|
||||
CustomDomain: req.CustomDomain,
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,14 @@ func (s *Server) GetLabelPolicy(ctx context.Context, req *admin_pb.GetLabelPolic
|
||||
return &admin_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetPreviewLabelPolicy(ctx context.Context, req *admin_pb.GetPreviewLabelPolicyRequest) (*admin_pb.GetPreviewLabelPolicyResponse, error) {
|
||||
policy, err := s.iam.GetDefaultPreviewLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.GetPreviewLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateLabelPolicy(ctx context.Context, req *admin_pb.UpdateLabelPolicyRequest) (*admin_pb.UpdateLabelPolicyResponse, error) {
|
||||
policy, err := s.command.ChangeDefaultLabelPolicy(ctx, updateLabelPolicyToDomain(req))
|
||||
if err != nil {
|
||||
@ -29,3 +37,87 @@ func (s *Server) UpdateLabelPolicy(ctx context.Context, req *admin_pb.UpdateLabe
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ActivateLabelPolicy(ctx context.Context, req *admin_pb.ActivateLabelPolicyRequest) (*admin_pb.ActivateLabelPolicyResponse, error) {
|
||||
policy, err := s.command.ActivateDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.ActivateLabelPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveLabelPolicyLogo(ctx context.Context, req *admin_pb.RemoveLabelPolicyLogoRequest) (*admin_pb.RemoveLabelPolicyLogoResponse, error) {
|
||||
policy, err := s.command.RemoveLogoDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RemoveLabelPolicyLogoResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveLabelPolicyLogoDark(ctx context.Context, req *admin_pb.RemoveLabelPolicyLogoDarkRequest) (*admin_pb.RemoveLabelPolicyLogoDarkResponse, error) {
|
||||
policy, err := s.command.RemoveLogoDarkDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RemoveLabelPolicyLogoDarkResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveLabelPolicyIcon(ctx context.Context, req *admin_pb.RemoveLabelPolicyIconRequest) (*admin_pb.RemoveLabelPolicyIconResponse, error) {
|
||||
policy, err := s.command.RemoveIconDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RemoveLabelPolicyIconResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveLabelPolicyIconDark(ctx context.Context, req *admin_pb.RemoveLabelPolicyIconDarkRequest) (*admin_pb.RemoveLabelPolicyIconDarkResponse, error) {
|
||||
policy, err := s.command.RemoveIconDarkDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RemoveLabelPolicyIconDarkResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveLabelPolicyFont(ctx context.Context, req *admin_pb.RemoveLabelPolicyFontRequest) (*admin_pb.RemoveLabelPolicyFontResponse, error) {
|
||||
policy, err := s.command.RemoveFontDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RemoveLabelPolicyFontResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
@ -8,7 +8,14 @@ import (
|
||||
func updateLabelPolicyToDomain(policy *admin_pb.UpdateLabelPolicyRequest) *domain.LabelPolicy {
|
||||
return &domain.LabelPolicy{
|
||||
PrimaryColor: policy.PrimaryColor,
|
||||
SecondaryColor: policy.SecondaryColor,
|
||||
BackgroundColor: policy.BackgroundColor,
|
||||
WarnColor: policy.WarnColor,
|
||||
FontColor: policy.FontColor,
|
||||
PrimaryColorDark: policy.PrimaryColorDark,
|
||||
BackgroundColorDark: policy.BackgroundColorDark,
|
||||
WarnColorDark: policy.WarnColorDark,
|
||||
FontColorDark: policy.FontColorDark,
|
||||
HideLoginNameSuffix: policy.HideLoginNameSuffix,
|
||||
DisableWatermark: policy.DisableWatermark,
|
||||
}
|
||||
}
|
||||
|
20
internal/api/grpc/auth/avatar.go
Normal file
20
internal/api/grpc/auth/avatar.go
Normal file
@ -0,0 +1,20 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
||||
)
|
||||
|
||||
func (s *Server) RemoveMyAvatar(ctx context.Context, req *auth_pb.RemoveMyAvatarRequest) (*auth_pb.RemoveMyAvatarResponse, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
objectDetails, err := s.command.RemoveHumanAvatar(ctx, ctxData.ResourceOwner, ctxData.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &auth_pb.RemoveMyAvatarResponse{
|
||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
||||
}, nil
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/server"
|
||||
"github.com/caos/zitadel/internal/auth/repository"
|
||||
@ -8,7 +10,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/pkg/grpc/auth"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var _ auth.AuthServiceServer = (*Server)(nil)
|
||||
|
@ -23,8 +23,10 @@ func FeaturesFromModel(features *features_model.FeaturesView) *features_pb.Featu
|
||||
LoginPolicyUsernameLogin: features.LoginPolicyUsernameLogin,
|
||||
LoginPolicyPasswordReset: features.LoginPolicyPasswordReset,
|
||||
PasswordComplexityPolicy: features.PasswordComplexityPolicy,
|
||||
LabelPolicy: features.LabelPolicy,
|
||||
LabelPolicy: features.LabelPolicyPrivateLabel,
|
||||
CustomDomain: features.CustomDomain,
|
||||
LabelPolicyPrivateLabel: features.LabelPolicyPrivateLabel,
|
||||
LabelPolicyWatermark: features.LabelPolicyWatermark,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,15 @@ func (s *Server) GetLabelPolicy(ctx context.Context, req *mgmt_pb.GetLabelPolicy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
|
||||
return &mgmt_pb.GetLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy), IsDefault: policy.Default}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetPreviewLabelPolicy(ctx context.Context, req *mgmt_pb.GetPreviewLabelPolicyRequest) (*mgmt_pb.GetPreviewLabelPolicyResponse, error) {
|
||||
policy, err := s.org.GetPreviewLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetPreviewLabelPolicyResponse{Policy: policy_grpc.ModelLabelPolicyToPb(policy)}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultLabelPolicy(ctx context.Context, req *mgmt_pb.GetDefaultLabelPolicyRequest) (*mgmt_pb.GetDefaultLabelPolicyResponse, error) {
|
||||
@ -53,6 +61,20 @@ func (s *Server) UpdateCustomLabelPolicy(ctx context.Context, req *mgmt_pb.Updat
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ActivateCustomLabelPolicy(ctx context.Context, req *mgmt_pb.ActivateCustomLabelPolicyRequest) (*mgmt_pb.ActivateCustomLabelPolicyResponse, error) {
|
||||
policy, err := s.command.ActivateLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ActivateCustomLabelPolicyResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ResetLabelPolicyToDefault(ctx context.Context, req *mgmt_pb.ResetLabelPolicyToDefaultRequest) (*mgmt_pb.ResetLabelPolicyToDefaultResponse, error) {
|
||||
objectDetails, err := s.command.RemoveLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
@ -62,3 +84,73 @@ func (s *Server) ResetLabelPolicyToDefault(ctx context.Context, req *mgmt_pb.Res
|
||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveCustomLabelPolicyLogo(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyLogoRequest) (*mgmt_pb.RemoveCustomLabelPolicyLogoResponse, error) {
|
||||
policy, err := s.command.RemoveLogoDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveCustomLabelPolicyLogoResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveCustomLabelPolicyLogoDark(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyLogoDarkRequest) (*mgmt_pb.RemoveCustomLabelPolicyLogoDarkResponse, error) {
|
||||
policy, err := s.command.RemoveLogoDarkDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveCustomLabelPolicyLogoDarkResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveCustomLabelPolicyIcon(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyIconRequest) (*mgmt_pb.RemoveCustomLabelPolicyIconResponse, error) {
|
||||
policy, err := s.command.RemoveIconDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveCustomLabelPolicyIconResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveCustomLabelPolicyIconDark(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyIconDarkRequest) (*mgmt_pb.RemoveCustomLabelPolicyIconDarkResponse, error) {
|
||||
policy, err := s.command.RemoveIconDarkDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveCustomLabelPolicyIconDarkResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveCustomLabelPolicyFont(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyFontRequest) (*mgmt_pb.RemoveCustomLabelPolicyFontResponse, error) {
|
||||
policy, err := s.command.RemoveFontDefaultLabelPolicy(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveCustomLabelPolicyFontResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.EventDate,
|
||||
policy.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
@ -8,15 +8,27 @@ import (
|
||||
func addLabelPolicyToDomain(p *mgmt_pb.AddCustomLabelPolicyRequest) *domain.LabelPolicy {
|
||||
return &domain.LabelPolicy{
|
||||
PrimaryColor: p.PrimaryColor,
|
||||
SecondaryColor: p.SecondaryColor,
|
||||
BackgroundColor: p.BackgroundColor,
|
||||
WarnColor: p.WarnColor,
|
||||
PrimaryColorDark: p.PrimaryColorDark,
|
||||
BackgroundColorDark: p.BackgroundColorDark,
|
||||
WarnColorDark: p.WarnColorDark,
|
||||
HideLoginNameSuffix: p.HideLoginNameSuffix,
|
||||
DisableWatermark: p.DisableWatermark,
|
||||
}
|
||||
}
|
||||
|
||||
func updateLabelPolicyToDomain(p *mgmt_pb.UpdateCustomLabelPolicyRequest) *domain.LabelPolicy {
|
||||
return &domain.LabelPolicy{
|
||||
PrimaryColor: p.PrimaryColor,
|
||||
SecondaryColor: p.SecondaryColor,
|
||||
BackgroundColor: p.BackgroundColor,
|
||||
WarnColor: p.WarnColor,
|
||||
FontColor: p.FontColor,
|
||||
PrimaryColorDark: p.PrimaryColorDark,
|
||||
BackgroundColorDark: p.BackgroundColorDark,
|
||||
WarnColorDark: p.WarnColorDark,
|
||||
FontColorDark: p.FontColorDark,
|
||||
HideLoginNameSuffix: p.HideLoginNameSuffix,
|
||||
DisableWatermark: p.DisableWatermark,
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func (s *Server) GetLoginPolicy(ctx context.Context, req *mgmt_pb.GetLoginPolicy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetLoginPolicyResponse{Policy: policy_grpc.ModelLoginPolicyToPb(policy)}, nil
|
||||
return &mgmt_pb.GetLoginPolicyResponse{Policy: policy_grpc.ModelLoginPolicyToPb(policy), IsDefault: policy.Default}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultLoginPolicy(ctx context.Context, req *mgmt_pb.GetDefaultLoginPolicyRequest) (*mgmt_pb.GetDefaultLoginPolicyResponse, error) {
|
||||
|
@ -14,7 +14,8 @@ func (s *Server) GetPasswordAgePolicy(ctx context.Context, req *mgmt_pb.GetPassw
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetPasswordAgePolicyResponse{
|
||||
Policy: policy_grpc.ModelPasswordAgePolicyToPb(policy),
|
||||
Policy: policy_grpc.ModelPasswordAgePolicyToPb(policy),
|
||||
IsDefault: policy.Default,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ func (s *Server) GetPasswordComplexityPolicy(ctx context.Context, req *mgmt_pb.G
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetPasswordComplexityPolicyResponse{Policy: policy_grpc.ModelPasswordComplexityPolicyToPb(policy)}, nil
|
||||
return &mgmt_pb.GetPasswordComplexityPolicyResponse{Policy: policy_grpc.ModelPasswordComplexityPolicyToPb(policy), IsDefault: policy.Default}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordComplexityPolicy(ctx context.Context, req *mgmt_pb.GetDefaultPasswordComplexityPolicyRequest) (*mgmt_pb.GetDefaultPasswordComplexityPolicyResponse, error) {
|
||||
|
@ -13,7 +13,7 @@ func (s *Server) GetPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.GetP
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetPasswordLockoutPolicyResponse{Policy: policy_grpc.ModelPasswordLockoutPolicyToPb(policy)}, nil
|
||||
return &mgmt_pb.GetPasswordLockoutPolicyResponse{Policy: policy_grpc.ModelPasswordLockoutPolicyToPb(policy), IsDefault: policy.Default}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDefaultPasswordLockoutPolicy(ctx context.Context, req *mgmt_pb.GetDefaultPasswordLockoutPolicyRequest) (*mgmt_pb.GetDefaultPasswordLockoutPolicyResponse, error) {
|
||||
|
@ -323,6 +323,17 @@ func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveHumanAvatar(ctx context.Context, req *mgmt_pb.RemoveHumanAvatarRequest) (*mgmt_pb.RemoveHumanAvatarResponse, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
objectDetails, err := s.command.RemoveHumanAvatar(ctx, ctxData.OrgID, req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveHumanAvatarResponse{
|
||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetHumanInitialPassword(ctx context.Context, req *mgmt_pb.SetHumanInitialPasswordRequest) (*mgmt_pb.SetHumanInitialPasswordResponse, error) {
|
||||
objectDetails, err := s.command.SetPassword(ctx, authz.GetCtxData(ctx).OrgID, req.UserId, req.Password, true)
|
||||
if err != nil {
|
||||
|
@ -10,7 +10,20 @@ func ModelLabelPolicyToPb(policy *model.LabelPolicyView) *policy_pb.LabelPolicy
|
||||
return &policy_pb.LabelPolicy{
|
||||
IsDefault: policy.Default,
|
||||
PrimaryColor: policy.PrimaryColor,
|
||||
SecondaryColor: policy.SecondaryColor,
|
||||
BackgroundColor: policy.BackgroundColor,
|
||||
FontColor: policy.FontColor,
|
||||
WarnColor: policy.WarnColor,
|
||||
PrimaryColorDark: policy.PrimaryColorDark,
|
||||
BackgroundColorDark: policy.BackgroundColorDark,
|
||||
WarnColorDark: policy.WarnColorDark,
|
||||
FontColorDark: policy.FontColorDark,
|
||||
FontUrl: policy.FontURL,
|
||||
LogoUrl: policy.LogoURL,
|
||||
LogoUrlDark: policy.LogoDarkURL,
|
||||
IconUrl: policy.IconURL,
|
||||
IconUrlDark: policy.IconDarkURL,
|
||||
|
||||
DisableWatermark: policy.DisableWatermark,
|
||||
HideLoginNameSuffix: policy.HideLoginNameSuffix,
|
||||
Details: object.ToViewDetailsPb(
|
||||
policy.Sequence,
|
||||
|
@ -80,6 +80,14 @@ func RemoteIPStringFromRequest(r *http.Request) string {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
|
||||
func GetAuthorization(r *http.Request) string {
|
||||
return r.Header.Get(Authorization)
|
||||
}
|
||||
|
||||
func GetOrgID(r *http.Request) string {
|
||||
return r.Header.Get(ZitadelOrgID)
|
||||
}
|
||||
|
||||
func GetForwardedFor(headers http.Header) (string, bool) {
|
||||
forwarded, ok := headers[ForwardedFor]
|
||||
if ok {
|
||||
|
71
internal/api/http/middleware/auth_interceptor.go
Normal file
71
internal/api/http/middleware/auth_interceptor.go
Normal file
@ -0,0 +1,71 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
type AuthInterceptor struct {
|
||||
verifier *authz.TokenVerifier
|
||||
authConfig authz.Config
|
||||
}
|
||||
|
||||
func AuthorizationInterceptor(verifier *authz.TokenVerifier, authConfig authz.Config) *AuthInterceptor {
|
||||
return &AuthInterceptor{
|
||||
verifier: verifier,
|
||||
authConfig: authConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AuthInterceptor) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, err := authorize(r, a.verifier, a.authConfig)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *AuthInterceptor) HandlerFunc(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, err := authorize(r, a.verifier, a.authConfig)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
type httpReq struct{}
|
||||
|
||||
func authorize(r *http.Request, verifier *authz.TokenVerifier, authConfig authz.Config) (_ context.Context, err error) {
|
||||
ctx := r.Context()
|
||||
authOpt, needsToken := verifier.CheckAuthMethod(r.Method + ":" + r.RequestURI)
|
||||
if !needsToken {
|
||||
return ctx, nil
|
||||
}
|
||||
authCtx, span := tracing.NewServerInterceptorSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
authToken := http_util.GetAuthorization(r)
|
||||
if authToken == "" {
|
||||
return nil, errors.New("auth header missing")
|
||||
}
|
||||
|
||||
ctxSetter, err := authz.CheckUserAuthorization(authCtx, &httpReq{}, authToken, http_util.GetOrgID(r), verifier, authConfig, authOpt, r.RequestURI) //TODO: permission
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
span.End()
|
||||
return ctxSetter(ctx), nil
|
||||
}
|
@ -248,7 +248,7 @@ func (repo *AuthRequestRepo) SelectUser(ctx context.Context, id, userID, userAge
|
||||
if request.RequestedOrgID == "" {
|
||||
username = user.PreferredLoginName
|
||||
}
|
||||
request.SetUserInfo(user.ID, username, user.PreferredLoginName, user.DisplayName, user.ResourceOwner)
|
||||
request.SetUserInfo(user.ID, username, user.PreferredLoginName, user.DisplayName, user.AvatarKey, user.ResourceOwner)
|
||||
return repo.AuthRequests.UpdateAuthRequest(ctx, request)
|
||||
}
|
||||
|
||||
@ -466,7 +466,7 @@ func (repo *AuthRequestRepo) checkLoginName(ctx context.Context, request *domain
|
||||
return err
|
||||
}
|
||||
|
||||
request.SetUserInfo(user.ID, loginName, user.PreferredLoginName, "", user.ResourceOwner)
|
||||
request.SetUserInfo(user.ID, loginName, user.PreferredLoginName, "", "", user.ResourceOwner)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -509,7 +509,7 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(request *domain.AuthRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.SetUserInfo(externalIDP.UserID, "", "", "", externalIDP.ResourceOwner)
|
||||
request.SetUserInfo(externalIDP.UserID, "", "", "", "", externalIDP.ResourceOwner)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -614,6 +614,7 @@ func (repo *AuthRequestRepo) usersForUserSelection(request *domain.AuthRequest)
|
||||
DisplayName: session.DisplayName,
|
||||
UserName: session.UserName,
|
||||
LoginName: session.LoginName,
|
||||
AvatarKey: session.AvatarKey,
|
||||
UserSessionState: auth_req_model.UserSessionStateToDomain(session.State),
|
||||
SelectionPossible: request.RequestedOrgID == "" || request.RequestedOrgID == session.ResourceOwner,
|
||||
}
|
||||
@ -710,9 +711,9 @@ func (repo *AuthRequestRepo) getLoginPolicy(ctx context.Context, orgID string) (
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) getLabelPolicy(ctx context.Context, orgID string) (*domain.LabelPolicy, error) {
|
||||
policy, err := repo.View.LabelPolicyByAggregateID(orgID)
|
||||
policy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive))
|
||||
if errors.IsNotFound(err) {
|
||||
policy, err = repo.View.LabelPolicyByAggregateID(repo.IAMID)
|
||||
policy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.IAMID, int32(domain.LabelPolicyStateActive))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
@ -103,3 +104,14 @@ func (repo *OrgRepository) GetMyPasswordComplexityPolicy(ctx context.Context) (*
|
||||
}
|
||||
return iam_view_model.PasswordComplexityViewToModel(policy), err
|
||||
}
|
||||
|
||||
func (repo *OrgRepository) GetLabelPolicy(ctx context.Context, orgID string) (*iam_model.LabelPolicyView, error) {
|
||||
orgPolicy, err := repo.View.LabelPolicyByAggregateIDAndState(orgID, int32(domain.LabelPolicyStateActive))
|
||||
if errors.IsNotFound(err) {
|
||||
orgPolicy, err = repo.View.LabelPolicyByAggregateIDAndState(repo.SystemDefaults.IamID, int32(domain.LabelPolicyStateActive))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iam_view_model.LabelPolicyViewToModel(orgPolicy), nil
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
@ -79,8 +81,19 @@ func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case iam_es_model.LabelPolicyAdded, model.LabelPolicyAdded:
|
||||
err = policy.AppendEvent(event)
|
||||
case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged:
|
||||
policy, err = m.view.LabelPolicyByAggregateID(event.AggregateID)
|
||||
case iam_es_model.LabelPolicyChanged, model.LabelPolicyChanged,
|
||||
iam_es_model.LabelPolicyActivated, model.LabelPolicyActivated,
|
||||
iam_es_model.LabelPolicyLogoAdded, model.LabelPolicyLogoAdded,
|
||||
iam_es_model.LabelPolicyLogoRemoved, model.LabelPolicyLogoRemoved,
|
||||
iam_es_model.LabelPolicyIconAdded, model.LabelPolicyIconAdded,
|
||||
iam_es_model.LabelPolicyIconRemoved, model.LabelPolicyIconRemoved,
|
||||
iam_es_model.LabelPolicyLogoDarkAdded, model.LabelPolicyLogoDarkAdded,
|
||||
iam_es_model.LabelPolicyLogoDarkRemoved, model.LabelPolicyLogoDarkRemoved,
|
||||
iam_es_model.LabelPolicyIconDarkAdded, model.LabelPolicyIconDarkAdded,
|
||||
iam_es_model.LabelPolicyIconDarkRemoved, model.LabelPolicyIconDarkRemoved,
|
||||
iam_es_model.LabelPolicyFontAdded, model.LabelPolicyFontAdded,
|
||||
iam_es_model.LabelPolicyFontRemoved, model.LabelPolicyFontRemoved:
|
||||
policy, err = m.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -122,6 +122,8 @@ func (u *User) ProcessUser(event *es_models.Event) (err error) {
|
||||
es_model.HumanProfileChanged,
|
||||
es_model.HumanEmailChanged,
|
||||
es_model.HumanEmailVerified,
|
||||
es_model.HumanAvatarAdded,
|
||||
es_model.HumanAvatarRemoved,
|
||||
es_model.HumanPhoneChanged,
|
||||
es_model.HumanPhoneVerified,
|
||||
es_model.HumanPhoneRemoved,
|
||||
|
@ -111,6 +111,8 @@ func (u *UserSession) Reduce(event *models.Event) (err error) {
|
||||
es_model.HumanPasswordChanged,
|
||||
es_model.HumanMFAOTPRemoved,
|
||||
es_model.HumanProfileChanged,
|
||||
es_model.HumanAvatarAdded,
|
||||
es_model.HumanAvatarRemoved,
|
||||
es_model.DomainClaimed,
|
||||
es_model.UserUserNameChanged,
|
||||
es_model.HumanExternalIDPRemoved,
|
||||
@ -167,5 +169,6 @@ func (u *UserSession) fillUserInfo(session *view_model.UserSessionView, id strin
|
||||
session.UserName = user.UserName
|
||||
session.LoginName = user.PreferredLoginName
|
||||
session.DisplayName = user.DisplayName
|
||||
session.AvatarKey = user.AvatarKey
|
||||
return nil
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ const (
|
||||
labelPolicyTable = "auth.label_policies"
|
||||
)
|
||||
|
||||
func (v *View) LabelPolicyByAggregateID(aggregateID string) (*model.LabelPolicyView, error) {
|
||||
return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTable, aggregateID)
|
||||
func (v *View) LabelPolicyByAggregateIDAndState(aggregateID string, state int32) (*model.LabelPolicyView, error) {
|
||||
return view.GetLabelPolicyByAggregateIDAndState(v.Db, labelPolicyTable, aggregateID, state)
|
||||
}
|
||||
|
||||
func (v *View) PutLabelPolicy(policy *model.LabelPolicyView, event *models.Event) error {
|
||||
|
@ -12,4 +12,5 @@ type OrgRepository interface {
|
||||
GetDefaultOrgIAMPolicy(ctx context.Context) (*iam_model.OrgIAMPolicyView, error)
|
||||
GetIDPConfigByID(ctx context.Context, idpConfigID string) (*iam_model.IDPConfigView, error)
|
||||
GetMyPasswordComplexityPolicy(ctx context.Context) (*iam_model.PasswordComplexityPolicyView, error)
|
||||
GetLabelPolicy(ctx context.Context, orgID string) (*iam_model.LabelPolicyView, error)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/authz/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
@ -130,13 +131,21 @@ func checkFeatures(features *features_view_model.FeaturesView, requiredFeatures
|
||||
if err := checkLoginPolicyFeatures(features, requiredFeature); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if requiredFeature == domain.FeaturePasswordComplexityPolicy && !features.PasswordComplexityPolicy {
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
if requiredFeature == domain.FeaturePasswordComplexityPolicy {
|
||||
if !features.PasswordComplexityPolicy {
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if requiredFeature == domain.FeatureLabelPolicy && !features.PasswordComplexityPolicy {
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
if strings.HasPrefix(requiredFeature, domain.FeatureLabelPolicy) {
|
||||
if err := checkLabelPolicyFeatures(features, requiredFeature); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -175,6 +184,20 @@ func checkLoginPolicyFeatures(features *features_view_model.FeaturesView, requir
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkLabelPolicyFeatures(features *features_view_model.FeaturesView, requiredFeature string) error {
|
||||
switch requiredFeature {
|
||||
case domain.FeatureLabelPolicyPrivateLabel:
|
||||
if !features.LabelPolicyPrivateLabel {
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
}
|
||||
case domain.FeatureLabelPolicyWatermark:
|
||||
if !features.LabelPolicyWatermark {
|
||||
return MissingFeatureErr(requiredFeature)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MissingFeatureErr(feature string) error {
|
||||
return caos_errs.ThrowPermissionDeniedf(nil, "AUTH-Dvgsf", "missing feature %v", feature)
|
||||
}
|
||||
|
@ -20,12 +20,14 @@ import (
|
||||
proj_repo "github.com/caos/zitadel/internal/repository/project"
|
||||
usr_repo "github.com/caos/zitadel/internal/repository/user"
|
||||
usr_grant_repo "github.com/caos/zitadel/internal/repository/usergrant"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
webauthn_helper "github.com/caos/zitadel/internal/webauthn"
|
||||
)
|
||||
|
||||
type Commands struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
static static.Storage
|
||||
idGenerator id.Generator
|
||||
iamDomain string
|
||||
zitadelRoles []authz.RoleMapping
|
||||
@ -58,9 +60,10 @@ type Config struct {
|
||||
Eventstore types.SQLUser
|
||||
}
|
||||
|
||||
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) {
|
||||
func StartCommands(eventstore *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, staticStore static.Storage, authZRepo *authz_repo.EsRepository) (repo *Commands, err error) {
|
||||
repo = &Commands{
|
||||
eventstore: eventstore,
|
||||
static: staticStore,
|
||||
idGenerator: id.SonyFlakeGenerator,
|
||||
iamDomain: defaults.Domain,
|
||||
zitadelRoles: authZConfig.RolePermissionMappings,
|
||||
|
54
internal/command/existing_label_policies_model.go
Normal file
54
internal/command/existing_label_policies_model.go
Normal file
@ -0,0 +1,54 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/iam"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
type ExistingLabelPoliciesReadModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
aggregateIDs []string
|
||||
}
|
||||
|
||||
func NewExistingLabelPoliciesReadModel(ctx context.Context) *ExistingLabelPoliciesReadModel {
|
||||
return &ExistingLabelPoliciesReadModel{}
|
||||
}
|
||||
|
||||
func (rm *ExistingLabelPoliciesReadModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
rm.WriteModel.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (rm *ExistingLabelPoliciesReadModel) Reduce() error {
|
||||
for _, event := range rm.Events {
|
||||
switch e := event.(type) {
|
||||
case *iam.LabelPolicyAddedEvent,
|
||||
*org.LabelPolicyAddedEvent:
|
||||
rm.aggregateIDs = append(rm.aggregateIDs, e.Aggregate().ID)
|
||||
case *org.LabelPolicyRemovedEvent:
|
||||
for i := len(rm.aggregateIDs) - 1; i >= 0; i-- {
|
||||
if rm.aggregateIDs[i] == e.Aggregate().ID {
|
||||
copy(rm.aggregateIDs[i:], rm.aggregateIDs[i+1:])
|
||||
rm.aggregateIDs[len(rm.aggregateIDs)-1] = ""
|
||||
rm.aggregateIDs = rm.aggregateIDs[:len(rm.aggregateIDs)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rm *ExistingLabelPoliciesReadModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(
|
||||
eventstore.ColumnsEvent,
|
||||
iam.AggregateType,
|
||||
org.AggregateType).
|
||||
EventTypes(
|
||||
iam.LabelPolicyAddedEventType,
|
||||
org.LabelPolicyAddedEventType,
|
||||
org.LabelPolicyRemovedEventType,
|
||||
)
|
||||
}
|
@ -23,7 +23,8 @@ type FeaturesWriteModel struct {
|
||||
LoginPolicyUsernameLogin bool
|
||||
LoginPolicyPasswordReset bool
|
||||
PasswordComplexityPolicy bool
|
||||
LabelPolicy bool
|
||||
LabelPolicyPrivateLabel bool
|
||||
LabelPolicyWatermark bool
|
||||
CustomDomain bool
|
||||
}
|
||||
|
||||
@ -69,7 +70,13 @@ func (wm *FeaturesWriteModel) Reduce() error {
|
||||
wm.PasswordComplexityPolicy = *e.PasswordComplexityPolicy
|
||||
}
|
||||
if e.LabelPolicy != nil {
|
||||
wm.LabelPolicy = *e.LabelPolicy
|
||||
wm.LabelPolicyPrivateLabel = *e.LabelPolicy
|
||||
}
|
||||
if e.LabelPolicyPrivateLabel != nil {
|
||||
wm.LabelPolicyPrivateLabel = *e.LabelPolicyPrivateLabel
|
||||
}
|
||||
if e.LabelPolicyWatermark != nil {
|
||||
wm.LabelPolicyWatermark = *e.LabelPolicyWatermark
|
||||
}
|
||||
if e.CustomDomain != nil {
|
||||
wm.CustomDomain = *e.CustomDomain
|
||||
|
@ -49,8 +49,16 @@ func writeModelToLabelPolicy(wm *LabelPolicyWriteModel) *domain.LabelPolicy {
|
||||
return &domain.LabelPolicy{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
PrimaryColor: wm.PrimaryColor,
|
||||
SecondaryColor: wm.SecondaryColor,
|
||||
BackgroundColor: wm.BackgroundColor,
|
||||
WarnColor: wm.WarnColor,
|
||||
FontColor: wm.FontColor,
|
||||
PrimaryColorDark: wm.PrimaryColorDark,
|
||||
BackgroundColorDark: wm.BackgroundColorDark,
|
||||
WarnColorDark: wm.WarnColorDark,
|
||||
FontColorDark: wm.FontColorDark,
|
||||
HideLoginNameSuffix: wm.HideLoginNameSuffix,
|
||||
ErrorMsgPopup: wm.ErrorMsgPopup,
|
||||
DisableWatermark: wm.DisableWatermark,
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,6 +184,8 @@ func writeModelToFeatures(wm *FeaturesWriteModel) *domain.Features {
|
||||
LoginPolicyRegistration: wm.LoginPolicyRegistration,
|
||||
LoginPolicyUsernameLogin: wm.LoginPolicyUsernameLogin,
|
||||
PasswordComplexityPolicy: wm.PasswordComplexityPolicy,
|
||||
LabelPolicy: wm.LabelPolicy,
|
||||
LabelPolicyPrivateLabel: wm.LabelPolicyPrivateLabel,
|
||||
LabelPolicyWatermark: wm.LabelPolicyWatermark,
|
||||
CustomDomain: wm.CustomDomain,
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,8 @@ func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *IAM
|
||||
features.LoginPolicyRegistration,
|
||||
features.LoginPolicyUsernameLogin,
|
||||
features.PasswordComplexityPolicy,
|
||||
features.LabelPolicy,
|
||||
features.LabelPolicyPrivateLabel,
|
||||
features.LabelPolicyWatermark,
|
||||
features.CustomDomain,
|
||||
)
|
||||
if !hasChanged {
|
||||
@ -61,5 +62,7 @@ func (c *Commands) getDefaultFeatures(ctx context.Context) (*domain.Features, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToFeatures(&existingFeatures.FeaturesWriteModel), nil
|
||||
features := writeModelToFeatures(&existingFeatures.FeaturesWriteModel)
|
||||
features.IsDefault = true
|
||||
return features, nil
|
||||
}
|
||||
|
@ -62,7 +62,8 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
|
||||
loginPolicyRegistration,
|
||||
loginPolicyUsernameLogin,
|
||||
passwordComplexityPolicy,
|
||||
labelPolicy,
|
||||
labelPolicyPrivateLabel,
|
||||
labelPolicyWatermark,
|
||||
customDomain bool,
|
||||
) (*iam.FeaturesSetEvent, bool) {
|
||||
|
||||
@ -101,8 +102,11 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent(
|
||||
if wm.PasswordComplexityPolicy != passwordComplexityPolicy {
|
||||
changes = append(changes, features.ChangePasswordComplexityPolicy(passwordComplexityPolicy))
|
||||
}
|
||||
if wm.LabelPolicy != labelPolicy {
|
||||
changes = append(changes, features.ChangeLabelPolicy(labelPolicy))
|
||||
if wm.LabelPolicyPrivateLabel != labelPolicyPrivateLabel {
|
||||
changes = append(changes, features.ChangeLabelPolicyPrivateLabel(labelPolicyPrivateLabel))
|
||||
}
|
||||
if wm.LabelPolicyWatermark != labelPolicyWatermark {
|
||||
changes = append(changes, features.ChangeLabelPolicyWatermark(labelPolicyWatermark))
|
||||
}
|
||||
if wm.CustomDomain != customDomain {
|
||||
changes = append(changes, features.ChangeCustomDomain(customDomain))
|
||||
|
@ -29,8 +29,8 @@ func (c *Commands) AddDefaultLabelPolicy(ctx context.Context, policy *domain.Lab
|
||||
}
|
||||
|
||||
func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMLabelPolicyWriteModel, policy *domain.LabelPolicy) (eventstore.EventPusher, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m9fo", "Errors.IAM.LabelPolicy.Invalid")
|
||||
if err := policy.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
@ -40,13 +40,26 @@ func (c *Commands) addDefaultLabelPolicy(ctx context.Context, iamAgg *eventstore
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
return iam_repo.NewLabelPolicyAddedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix), nil
|
||||
return iam_repo.NewLabelPolicyAddedEvent(
|
||||
ctx,
|
||||
iamAgg,
|
||||
policy.PrimaryColor,
|
||||
policy.BackgroundColor,
|
||||
policy.WarnColor,
|
||||
policy.FontColor,
|
||||
policy.PrimaryColorDark,
|
||||
policy.BackgroundColorDark,
|
||||
policy.WarnColorDark,
|
||||
policy.FontColorDark,
|
||||
policy.HideLoginNameSuffix,
|
||||
policy.ErrorMsgPopup,
|
||||
policy.DisableWatermark), nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
||||
if !policy.IsValid() {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-33m8f", "Errors.IAM.LabelPolicy.Invalid")
|
||||
if err := policy.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
@ -57,7 +70,20 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.PrimaryColor, policy.SecondaryColor, policy.HideLoginNameSuffix)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(
|
||||
ctx,
|
||||
iamAgg,
|
||||
policy.PrimaryColor,
|
||||
policy.BackgroundColor,
|
||||
policy.WarnColor,
|
||||
policy.FontColor,
|
||||
policy.PrimaryColorDark,
|
||||
policy.BackgroundColorDark,
|
||||
policy.WarnColorDark,
|
||||
policy.FontColorDark,
|
||||
policy.HideLoginNameSuffix,
|
||||
policy.ErrorMsgPopup,
|
||||
policy.DisableWatermark)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
@ -73,6 +99,275 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.
|
||||
return writeModelToLabelPolicy(&existingPolicy.LabelPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ActivateDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-6M23e", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyActivatedEvent(ctx, iamAgg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3m20c", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-Qw0pd", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoAddedEvent(ctx, iamAgg, storageKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveLogoDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-Xc8Kf", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.LogoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoRemovedEvent(ctx, iamAgg, existingPolicy.LogoKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-yxE4f", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-1yMx0", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconAddedEvent(ctx, iamAgg, storageKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveIconDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M0qw", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.IconKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconRemovedEvent(ctx, iamAgg, existingPolicy.IconKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4fMs9", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-ZR9fs", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoDarkAddedEvent(ctx, iamAgg, storageKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveLogoDarkDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-3FGds", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.LogoDarkKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyLogoDarkRemovedEvent(ctx, iamAgg, existingPolicy.LogoDarkKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-1cxM3", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-vMsf9", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconDarkAddedEvent(ctx, iamAgg, storageKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveIconDarkDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-2nc7F", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.IconDarkKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyIconDarkRemovedEvent(ctx, iamAgg, existingPolicy.IconDarkKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddFontDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-1N8fs", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-1N8fE", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyFontAddedEvent(ctx, iamAgg, storageKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveFontDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-Tk0gw", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, domain.IAMID, existingPolicy.FontKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewLabelPolicyFontRemovedEvent(ctx, iamAgg, existingPolicy.FontKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(existingPolicy, pushedEvents...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) defaultLabelPolicyWriteModelByID(ctx context.Context) (policy *IAMLabelPolicyWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
@ -84,3 +379,13 @@ func (c *Commands) defaultLabelPolicyWriteModelByID(ctx context.Context) (policy
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getDefaultLabelPolicy(ctx context.Context) (*domain.LabelPolicy, error) {
|
||||
policyWriteModel, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy := writeModelToLabelPolicy(&policyWriteModel.LabelPolicyWriteModel)
|
||||
policy.Default = true
|
||||
return policy, nil
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user