cherry pick changes from main (#3371)

* feat: remove exif data from uploaded images (#3221)

* feat: remove exif tags from images

* feat: remove exif data

* feat: remove exif

* fix: add preferredLoginName to user grant response (#3271)

* chore: log webauthn parse error (#3272)

* log error

* log error

* feat: Help link in privacy policy

* fix: convert correct detail data on organization (#3279)

* fix: handle empty editor users

* fix: add some missing translations (#3291)

* fix: org policy translations

* fix: metadata event types translation

* fix: translations

* fix: filter resource owner correctly on project grant members (#3281)

* fix: filter resource owner correctly on project grant members

* fix: filter resource owner correctly on project grant members

* fix: add orgIDs to zitadel permissions request

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>

* fix: get IAM memberships correctly in MyZitadelPermissions (#3309)

* fix: correct login names on auth and notification users (#3349)

* fix: correct login names on auth and notification users

* fix: migration

* fix: handle resource owner in action flows (#3361)

* fix merge

* fix: exchange exif library (#3366)

* fix: exchange exif library

* ignore tiffs

* requested fixes

* feat: Help link in privacy policy

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Livio Amstutz 2022-03-24 14:00:24 +01:00 committed by GitHub
parent 56b916a2b0
commit 504fe5b761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 1055 additions and 602 deletions

View File

@ -1,31 +1,42 @@
<div *ngIf="currentMap">
<div *ngIf="currentMap">
<form [formGroup]="form" >
<form [formGroup]="form">
<ng-container *ngFor="let key of (current$ | async) | keyvalue">
<div class="block">
<div class="flex" *ngIf="(default$ | async) as defaultmap">
<cnsl-form-field class="formfield" >
<cnsl-form-field class="formfield">
<cnsl-label>{{key.key}}</cnsl-label>
<textarea class="text" cnslInput [formControlName]="key.key" [placeholder]="defaultmap[key.key]" [name]="key.key" [ngClass]="{'defaulttext': form.get(key.key)?.value === ''}"></textarea>
<textarea class="text" cnslInput [formControlName]="key.key" [placeholder]="defaultmap[key.key]"
[name]="key.key" [ngClass]="{'defaulttext': form.get(key.key)?.value === ''}"></textarea>
<div class="chips" *ngIf="warnText[key.key] === undefined">
<ng-container *ngFor="let chip of chips" >
<div class="chip" cnslCopyToClipboard [valueToCopy]="chip.value" (copiedValue)="copied = $event" (click)="addChip(key.key, chip.value)">
<span class="key">{{chip.key | translate}}</span>
<span class="value">{{chip.value}}</span>
<i *ngIf="copied !== chip.value" class="las la-clipboard"></i>
<i *ngIf="copied === chip.value" class="las la-clipboard-check"></i>
</div>
</ng-container>
<ng-container *ngFor="let chip of chips">
<div class="chip" cnslCopyToClipboard [valueToCopy]="chip.value" (copiedValue)="copied = $event"
(click)="addChip(key.key, chip.value)">
<span class="key">{{chip.key | translate}}</span>
<span class="value">{{chip.value}}</span>
<i *ngIf="copied !== chip.value" class="las la-clipboard"></i>
<i *ngIf="copied === chip.value" class="las la-clipboard-check"></i>
</div>
</ng-container>
</div>
</cnsl-form-field>
<div class="actions">
<button matTooltip="{{'ACTIONS.RESETDEFAULT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value === defaultmap[key.key] || disabled" (click)="form.get(key.key)?.setValue(defaultmap[key.key])" (mouseenter) = "form.get(key.key)?.value !== defaultmap[key.key] && setWarnText(key.key, defaultmap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-history"></i></button>
<button matTooltip="{{'ACTIONS.RESETCURRENT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value === currentMap[key.key] || disabled" (click)="form.get(key.key)?.setValue(currentMap[key.key])" (mouseenter) = "form.get(key.key)?.value !== currentMap[key.key] && setWarnText(key.key, currentMap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-undo"></i></button>
<button matTooltip="{{'ACTIONS.RESETDEFAULT'| translate }}" mat-icon-button
[disabled]="form.get(key.key)?.value === defaultmap[key.key] || disabled"
(click)="form.get(key.key)?.setValue(defaultmap[key.key])"
(mouseenter)="form.get(key.key)?.value !== defaultmap[key.key] && setWarnText(key.key, defaultmap[key.key])"
(mouseleave)="setWarnText(key.key, undefined)"><i class="las la-history"></i></button>
<button matTooltip="{{'ACTIONS.RESETCURRENT'| translate }}" mat-icon-button
[disabled]="form.get(key.key)?.value === currentMap[key.key] || disabled"
(click)="form.get(key.key)?.setValue(currentMap[key.key])"
(mouseenter)="form.get(key.key)?.value !== currentMap[key.key] && setWarnText(key.key, currentMap[key.key])"
(mouseleave)="setWarnText(key.key, undefined)"><i class="las la-undo"></i></button>
</div>
</div>
</div>
<cnsl-info-section *ngIf="warnText[key.key] !== undefined" class="info" [type]="InfoSectionType.WARN">{{'ACTIONS.RESETTO'| translate }} <cite>'{{warnText[key.key]}}'</cite></cnsl-info-section>
<cnsl-info-section *ngIf="warnText[key.key] !== undefined" class="info" [type]="InfoSectionType.WARN">
{{'ACTIONS.RESETTO'| translate }} <cite>'{{warnText[key.key]}}'</cite></cnsl-info-section>
</ng-container>
</form>
</div>

View File

@ -71,7 +71,6 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
const r3 = new FooterText();
r3.setHelp(map.footerText?.help ?? '');
r3.setHelpLink(map.footerText?.helpLink ?? '');
r3.setPrivacyPolicy(map.footerText?.privacyPolicy ?? '');
r3.setTos(map.footerText?.tos ?? '');
req.setFooterText(r3);
@ -102,7 +101,6 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
r6.setU2fOption(map.initMfaPromptText?.otpOption ?? '');
req.setInitMfaPromptText(r6);
const r7 = new InitMFAU2FScreenText();
r7.setDescription(map.initMfaU2fText?.description ?? '');
r7.setErrorRetry(map.initMfaU2fText?.errorRetry ?? '');
@ -112,7 +110,6 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
r7.setTokenNameLabel(map.initMfaU2fText?.tokenNameLabel ?? '');
req.setInitMfaU2fText(r7);
const r8 = new InitPasswordDoneScreenText();
r8.setCancelButtonText(map.initPasswordDoneText?.cancelButtonText ?? '');
r8.setDescription(map.initPasswordDoneText?.description ?? '');
@ -348,7 +345,6 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
r32.setTokenNameLabel(map.passwordlessRegistrationText?.tokenNameLabel ?? '');
req.setPasswordlessRegistrationText(r32);
const r33 = new PasswordlessScreenText();
r33.setDescription(map.passwordlessText?.description ?? '');
r33.setErrorRetry(map.passwordlessText?.errorRetry ?? '');

View File

@ -1,35 +1,59 @@
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'POLICY.PRIVACY_POLICY.TITLE' | translate"
[description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate">
[title]="'POLICY.PRIVACY_POLICY.TITLE' | translate" [description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate">
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
<cnsl-info-section
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false"
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'privacy_policy'})"></span>
</cnsl-info-section>
<div class="divider"></div>
<div class="content" >
<div class="content">
<form *ngIf="form" [formGroup]="form">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }}</cnsl-label>
<input cnslInput name="tosLink" formControlName="tosLink" />
<cnsl-form-field class="privacy-policy-formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }}</cnsl-label>
<input cnslInput name="tosLink" formControlName="tosLink" />
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'tosLink'}"></template>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-form-field class="privacy-policy-formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.POLICYLINK' | translate }}</cnsl-label>
<input cnslInput name="privacyLink" formControlName="privacyLink" />
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'privacyLink'}"></template>
</cnsl-form-field>
<cnsl-form-field class="privacy-policy-formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.HELPLINK' | translate }}</cnsl-label>
<input cnslInput name="helpLink" formControlName="helpLink" />
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'helpLink'}"></template>
</cnsl-form-field>
</form>
</div>
<div class="actions">
<button *ngIf="!privacyPolicy?.isDefault" class="reset-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" (click)="resetDefault()" color="warn" type="submit"
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
<button class="save-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" (click)="saveCurrentMessage()" color="primary" type="submit"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
<button *ngIf="privacyPolicy && privacyPolicy.isDefault === false" class="reset-button"
[disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false"
(click)="resetDefault()" color="warn" type="submit" mat-stroked-button><i class="las la-history"></i> {{
'ACTIONS.RESETDEFAULT' | translate }}</button>
<button class="save-button"
[disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false"
(click)="saveCurrentMessage()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
}}</button>
</div>
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
</cnsl-detail-layout>
<ng-template #templateRef let-key="key">
<div class="chips">
<div class="chip" cnslCopyToClipboard [valueToCopy]="LANGPLACEHOLDER" (copiedValue)="copied = $event"
(click)="addChip(key, LANGPLACEHOLDER)">
<span class="key">{{LANGPLACEHOLDER}}</span>
<i *ngIf="copied !== LANGPLACEHOLDER" class="las la-clipboard"></i>
<i *ngIf="copied === LANGPLACEHOLDER" class="las la-clipboard-check"></i>
</div>
</div>
</ng-template>

View File

@ -1,26 +1,26 @@
.spinner-wr {
margin: .5rem 0;
margin: 0.5rem 0;
}
.top-actions {
display: flex;
margin: 0 -.5rem;
margin: 0 -0.5rem;
flex-wrap: wrap;
.keys {
flex: 1;
margin: 0 .5rem;
margin: 0 0.5rem;
min-width: 150px;
}
.language {
margin: 0 .5rem;
margin: 0 0.5rem;
min-width: 150px;
.lighter {
font-size: 12px;
color: var(--grey);
padding: 0 .5rem;
padding: 0 0.5rem;
}
}
}
@ -34,40 +34,66 @@
padding-top: 1rem;
}
.chips {
display: flex;
margin: 0 -.25rem;
.chip {
border-radius: 50vw;
padding: 2px .5rem;
font-size: 12px;
background: #cbf4c9;
color: #0e6245;
margin: .25rem;
.privacy-policy-formfield {
.chips {
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
flex-wrap: wrap;
opacity: 0;
margin: 0 -0.25rem;
transition: all 0.2s ease;
.chip {
border-radius: 50vw;
padding: 4px 0.5rem;
font-size: 12px;
background: #5282c1;
color: white;
margin: 0.25rem;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
* {
transition: all 0.2s ease;
}
i {
opacity: 0.5;
font-size: 1.1rem;
margin-left: 0.5rem;
}
.key {
display: inline-block;
}
}
}
&.cnsl-focused {
.chips {
opacity: 1;
cursor: copy;
}
}
}
.divider {
width: 100%;
height: 1px;
background-color: rgba(#81868a, .5);
background-color: rgba(#81868a, 0.5);
margin: 1.5rem 0 0 0;
}
.actions {
display: flex;
justify-content: flex-end;
margin: 0 -.25rem;
margin: 0 -0.25rem;
.save-button,
.reset-button {
display: block;
margin: 0 .25rem 3rem .25rem;
margin: 0 0.25rem 3rem 0.25rem;
}
.reset-button {

View File

@ -37,11 +37,14 @@ export class PrivacyPolicyComponent implements OnDestroy {
public nextLinks: CnslLinks[] = [];
private sub: Subscription = new Subscription();
public privacyPolicy!: PrivacyPolicy.AsObject;
public privacyPolicy: PrivacyPolicy.AsObject | undefined = undefined;
public form!: FormGroup;
public currentPolicy: GridPolicy = PRIVACY_POLICY;
public InfoSectionType: any = InfoSectionType;
public LANGPLACEHOLDER: string = '{{.Lang}}';
public copied: string = '';
constructor(
private route: ActivatedRoute,
private injector: Injector,
@ -49,70 +52,108 @@ export class PrivacyPolicyComponent implements OnDestroy {
private toast: ToastService,
private fb: FormBuilder,
) {
this.form = this.fb.group({
tosLink: ['', []],
privacyLink: ['', []],
helpLink: ['', []],
});
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.loadData();
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.loadData();
break;
}
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.loadData();
break;
case PolicyComponentServiceType.ADMIN:
this.service = this.injector.get(AdminService as Type<AdminService>);
this.loadData();
break;
}
return this.route.params;
})).subscribe();
return this.route.params;
}),
)
.subscribe();
}
public addChip(formControlName: string, value: string): void {
const c = this.form.get(formControlName)?.value;
this.form.get(formControlName)?.setValue(`${c}${value}`);
}
public async loadData(): Promise<any> {
const getData = ():
Promise<AdminGetPrivacyPolicyResponse.AsObject | GetPrivacyPolicyResponse.AsObject> => {
return (this.service as AdminService).getPrivacyPolicy();
const getData = (): Promise<AdminGetPrivacyPolicyResponse.AsObject | GetPrivacyPolicyResponse.AsObject> => {
return this.service.getPrivacyPolicy();
};
getData().then(resp => {
if (resp.policy) {
this.privacyPolicy = resp.policy;
this.form.patchValue(this.privacyPolicy);
}
});
getData()
.then((resp) => {
if (resp.policy) {
this.privacyPolicy = resp.policy;
this.form.patchValue(this.privacyPolicy);
} else {
this.privacyPolicy = undefined;
this.form.patchValue({
tosLink: '',
privacyLink: '',
helpLink: '',
});
}
})
.catch((error) => {
this.privacyPolicy = undefined;
this.form.patchValue({
tosLink: '',
privacyLink: '',
helpLink: '',
});
});
}
public saveCurrentMessage(): void {
console.log(this.form.get('privacyLink')?.value, this.form.get('tosLink')?.value);
if (this.serviceType === PolicyComponentServiceType.MGMT) {
if ((this.privacyPolicy as PrivacyPolicy.AsObject).isDefault) {
if (!this.privacyPolicy || (this.privacyPolicy as PrivacyPolicy.AsObject).isDefault) {
const req = new AddCustomPrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
(this.service as ManagementService).addCustomPrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
req.setHelpLink(this.form.get('helpLink')?.value);
(this.service as ManagementService)
.addCustomPrivacyPolicy(req)
.then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
this.loadData();
})
.catch((error) => this.toast.showError(error));
} else {
const req = new UpdateCustomPrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
(this.service as ManagementService).updateCustomPrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
}
req.setHelpLink(this.form.get('helpLink')?.value);
(this.service as ManagementService)
.updateCustomPrivacyPolicy(req)
.then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
this.loadData();
})
.catch((error) => this.toast.showError(error));
}
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
const req = new UpdatePrivacyPolicyRequest();
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
req.setHelpLink(this.form.get('helpLink')?.value);
(this.service as AdminService).updatePrivacyPolicy(req).then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
}).catch(error => this.toast.showError(error));
(this.service as AdminService)
.updatePrivacyPolicy(req)
.then(() => {
this.toast.showInfo('POLICY.PRIVACY_POLICY.SAVED', true);
this.loadData();
})
.catch((error) => this.toast.showError(error));
}
}
@ -128,16 +169,19 @@ export class PrivacyPolicyComponent implements OnDestroy {
width: '400px',
});
dialogRef.afterClosed().subscribe(resp => {
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).resetPrivacyPolicyToDefault().then(() => {
setTimeout(() => {
this.loadData();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
(this.service as ManagementService)
.resetPrivacyPolicyToDefault()
.then(() => {
setTimeout(() => {
this.loadData();
}, 1000);
})
.catch((error) => {
this.toast.showError(error);
});
}
}
});

View File

@ -9,6 +9,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.module';
import { HasRoleModule } from '../../../directives/has-role/has-role.module';
import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module';
@ -32,6 +33,7 @@ import { PrivacyPolicyComponent } from './privacy-policy.component';
FormsModule,
InputModule,
FormFieldModule,
CopyToClipboardModule,
MatButtonModule,
HasFeaturePipeModule,
MatIconModule,
@ -49,4 +51,4 @@ import { PrivacyPolicyComponent } from './privacy-policy.component';
InfoSectionModule,
],
})
export class PrivacyPolicyModule { }
export class PrivacyPolicyModule {}

View File

@ -873,6 +873,7 @@
"DESCRIPTION": "Legen Sie Ihre Datenschutzrichtlinien und Nutzungsbedingungen fest",
"TOSLINK": "Link zu den Allgemeinen Geschäftsbedingungen",
"POLICYLINK": "Link zur den Datenschutzrichtlinien",
"HELPLINK": "Link zur Hilfestellung",
"SAVED": "Saved successfully!",
"RESET_TITLE": "Standardwerte wiederherstellen",
"RESET_DESCRIPTION": "Sie sind im Begriff die Standardlinks für die AGBs und Datenschutzrichtlinie wiederherzustellen. Wollen Sie fortfahren?"

View File

@ -873,6 +873,7 @@
"DESCRIPTION": "Set your Privacy Policy and Terms of Service Links",
"TOSLINK": "Link to Terms of Service",
"POLICYLINK": "Link to Privacy Policy",
"HELPLINK": "Link to Help",
"SAVED": "Saved successfully!",
"RESET_TITLE": "Restore Default Values",
"RESET_DESCRIPTION": "You are about to restore the default Links for TOS and Privacy Policy. Do you really want to continue?"

View File

@ -873,6 +873,7 @@
"DESCRIPTION": "Imposta i tuoi link all'informativa sulla privacy e ai termini di servizio",
"TOSLINK": "Link ai termini di servizio",
"POLICYLINK": "Link all'informativa sulla privacy",
"HELPLINK": "link per l'aiuto",
"SAVED": "Salvato con successo!",
"RESET_TITLE": "Ripristina i valori predefiniti",
"RESET_DESCRIPTION": "Stai per ripristinare i link predefiniti per i TOS e l'informativa sulla privacy. Vuoi davvero continuare?"

View File

@ -888,6 +888,7 @@ Returns the privacy policy defined by the administrators of ZITADEL
Updates the default privacy policy of ZITADEL
it impacts all organisations without a customised policy
Variable {{.Lang}} can be set to have different links based on the language
@ -3816,6 +3817,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- |
| tos_link | string | - | |
| privacy_link | string | - | |
| help_link | string | - | |

View File

@ -2095,6 +2095,7 @@ With this policy the privacy relevant things can be configured (e.g tos link)
Add a custom privacy policy for the organisation
With this policy privacy relevant things can be configured (e.g. tos link)
Variable {{.Lang}} can be set to have different links based on the language
@ -2108,6 +2109,7 @@ With this policy privacy relevant things can be configured (e.g. tos link)
Update the privacy complexity policy for the organisation
With this policy privacy relevant things can be configured (e.g. tos link)
Variable {{.Lang}} can be set to have different links based on the language
@ -3113,6 +3115,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- |
| tos_link | string | - | |
| privacy_link | string | - | |
| help_link | string | - | |
@ -7833,6 +7836,7 @@ This is an empty request
| ----- | ---- | ----------- | ----------- |
| tos_link | string | - | |
| privacy_link | string | - | |
| help_link | string | - | |

View File

@ -126,6 +126,7 @@ title: zitadel/policy.proto
| tos_link | string | - | |
| privacy_link | string | - | |
| is_default | bool | - | |
| help_link | string | - | |

View File

@ -93,7 +93,6 @@ title: zitadel/text.proto
| tos | string | - | string.max_len: 200<br /> |
| privacy_policy | string | - | string.max_len: 200<br /> |
| help | string | - | string.max_len: 200<br /> |
| help_link | string | - | string.max_len: 500<br /> |

View File

@ -374,6 +374,7 @@ UserTypeQuery is always equals
| project_name | string | - | |
| project_grant_id | string | - | |
| avatar_url | string | - | |
| preferred_login_name | string | - | |

15
go.mod
View File

@ -44,10 +44,12 @@ require (
github.com/pquerna/otp v1.3.0
github.com/rakyll/statik v0.1.7
github.com/rs/cors v1.8.0
github.com/sirupsen/logrus v1.8.1
github.com/sony/sonyflake v1.0.0
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
github.com/stretchr/testify v1.7.0
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203
github.com/ttacon/libphonenumber v1.2.1
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.27.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0
@ -88,17 +90,28 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc // indirect
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4 // indirect
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb // indirect
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2 // indirect
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d // indirect
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect
github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82 // indirect
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fxamacker/cbor/v2 v2.2.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-errors/errors v1.0.2 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.1.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.0.21 // indirect
@ -107,6 +120,7 @@ require (
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/h2non/filetype v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
@ -139,7 +153,6 @@ require (
github.com/prometheus/procfs v0.6.0 // indirect
github.com/rs/xid v1.2.1 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.8.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect

32
go.sum
View File

@ -203,6 +203,24 @@ github.com/dop251/goja v0.0.0-20211129110639-4739a1d10a51/go.mod h1:R9ET47fwRVRP
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d h1:W1n4DvpzZGOISgp7wWNtraLcHtnmnTwBlJidqtMIuwQ=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc h1:AuzYp98IFVOi0NU/WcZyGDQ6vAh/zkCjxGD3kt8aLzA=
github.com/dsoprea/go-exif v0.0.0-20210131231135-d154f10435cc/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4 h1:Mg7pY7kxDQD2Bkvr1N+XW4BESSIQ7tTTR7Vv+Gi2CsM=
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4=
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2 h1:ULCSN6v0WISNbALxomGPXh4dSjRKPW+7+seYoMz8UTc=
github.com/dsoprea/go-jpeg-image-structure v0.0.0-20210128210355-86b1014917f2/go.mod h1:ZoOP3yUG0HD1T4IUjIFsz/2OAB2yB4YX6NSm4K+uJRg=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c h1:7j5aWACOzROpr+dvMtu8GnI97g9ShLWD72XIELMgn+c=
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E=
github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82 h1:RdwKOEEe2ND/JmoKh6I/EQlR9idKJTDOMffPFK6vN2M=
github.com/dsoprea/go-png-image-structure v0.0.0-20200807080309-a98d4e94ac82/go.mod h1:aDYQkL/5gfRNZkoxiLTSWU4Y8/gV/4MVsy/MU9uwTak=
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME=
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/duo-labs/webauthn v0.0.0-20211216225436-9a12cd078b8a h1:mKoV2b/J8sVVvc6jCl7SxdOrED5cHKdQaHUxjoO5W74=
github.com/duo-labs/webauthn v0.0.0-20211216225436-9a12cd078b8a/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -256,8 +274,9 @@ github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -286,6 +305,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
@ -309,6 +330,9 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ
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/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
@ -434,6 +458,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1 h1:p5m7GOEGXyoq6QWl4/RRMsQ6tWbTpbQmAnkxXgWSprY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.1/go.mod h1:8ZeZajTed/blCOHBbj8Fss8bPHiFKcmJJzuIbUtFCAo=
github.com/h2non/filetype v1.1.1 h1:xvOwnXKAckvtLWsN398qS9QhlxlnVXBjXBydK2/UFB4=
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
@ -535,6 +561,7 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@ -870,6 +897,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203 h1:1SWXcTphBQjYGWRRxLFIAR1LVtQEj4eR7xPtyeOVM/c=
github.com/superseriousbusiness/exifremove v0.0.0-20210330092427-6acd27eac203/go.mod h1:0Xw5cYMOYpgaWs+OOSx41ugycl2qvKTi9tlMMcZhFyY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0=
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
@ -1073,6 +1102,7 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=

View File

@ -1,8 +1,10 @@
package assets
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"
@ -11,6 +13,7 @@ import (
"github.com/caos/logging"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/gorilla/mux"
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
"github.com/caos/zitadel/internal/api/authz"
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
@ -136,7 +139,13 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
return
}
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, file, size)
cleanedFile, cleanedSize, err := removeExif(file, size, contentType)
if err != nil {
s.ErrorHandler()(w, r, fmt.Errorf("remove exif error: %v", err), http.StatusInternalServerError)
return
}
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, cleanedFile, cleanedSize)
if err != nil {
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
return
@ -191,3 +200,25 @@ func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.Respon
w.Write(data)
}
}
func removeExif(file io.Reader, size int64, contentType string) (io.Reader, int64, error) {
if !isAllowedContentType(contentType) {
return file, size, nil
}
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(file)
if err != nil {
return file, 0, err
}
data, err := exifremove.Remove(buf.Bytes())
if err != nil {
return nil, 0, err
}
return bytes.NewReader(data), int64(len(data)), nil
}
func isAllowedContentType(contentType string) bool {
return strings.HasSuffix(contentType, "png") ||
strings.HasSuffix(contentType, "jpg") ||
strings.HasSuffix(contentType, "jpeg")
}

View File

@ -9,5 +9,6 @@ func UpdatePrivacyPolicyToDomain(req *admin_pb.UpdatePrivacyPolicyRequest) *doma
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
}
}

View File

@ -11,7 +11,7 @@ import (
)
func (s *Server) ListMyZitadelPermissions(ctx context.Context, _ *auth_pb.ListMyZitadelPermissionsRequest) (*auth_pb.ListMyZitadelPermissionsResponse, error) {
perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).UserID)
perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).OrgID, authz.GetCtxData(ctx).UserID)
if err != nil {
return nil, err
}

View File

@ -168,6 +168,7 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje
if err != nil {
return nil, err
}
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantProjectID, userGrantUserID}})
if err != nil {
return nil, err

View File

@ -9,6 +9,7 @@ func AddPrivacyPolicyToDomain(req *mgmt_pb.AddCustomPrivacyPolicyRequest) *domai
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
}
}
@ -16,5 +17,6 @@ func UpdatePrivacyPolicyToDomain(req *mgmt_pb.UpdateCustomPrivacyPolicyRequest)
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
}
}

View File

@ -126,11 +126,6 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
if err != nil {
return nil, err
}
ownerQuery, err := query.NewMemberResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
queries = append(queries, ownerQuery)
return &query.ProjectGrantMembersQuery{
MembersQuery: query.MembersQuery{
SearchRequest: query.SearchRequest{
@ -143,6 +138,7 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
},
ProjectID: req.ProjectId,
GrantID: req.GrantId,
OrgID: authz.GetCtxData(ctx).OrgID,
}, nil
}

View File

@ -88,7 +88,7 @@ func OrgToPb(org *query.Org) *org_pb.Org {
Id: org.ID,
Name: org.Name,
PrimaryDomain: org.Domain,
Details: object.AddToDetailsPb(org.Sequence, org.CreationDate, org.ResourceOwner),
Details: object.ToViewDetailsPb(org.Sequence, org.CreationDate, org.ChangeDate, org.ResourceOwner),
State: OrgStateToPb(org.State),
}
}

View File

@ -11,6 +11,7 @@ func ModelPrivacyPolicyToPb(policy *query.PrivacyPolicy) *policy_pb.PrivacyPolic
IsDefault: policy.IsDefault,
TosLink: policy.TOSLink,
PrivacyLink: policy.PrivacyLink,
HelpLink: policy.HelpLink,
Details: object.ToViewDetailsPb(
policy.Sequence,
policy.CreationDate,

View File

@ -457,7 +457,6 @@ func FooterTextToPb(text domain.FooterText) *text_pb.FooterText {
Tos: text.TOS,
PrivacyPolicy: text.PrivacyPolicy,
Help: text.Help,
HelpLink: text.HelpLink,
}
}
@ -947,6 +946,5 @@ func FooterTextPbToDomain(text *text_pb.FooterText) domain.FooterText {
TOS: text.Tos,
PrivacyPolicy: text.PrivacyPolicy,
Help: text.Help,
HelpLink: text.HelpLink,
}
}

View File

@ -21,22 +21,23 @@ func UserGrantsToPb(assetPrefix string, grants []*query.UserGrant) []*user_pb.Us
func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGrant {
return &user_pb.UserGrant{
Id: grant.ID,
UserId: grant.UserID,
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
RoleKeys: grant.Roles,
ProjectId: grant.ProjectID,
OrgId: grant.ResourceOwner,
ProjectGrantId: grant.GrantID,
UserName: grant.Username,
FirstName: grant.FirstName,
LastName: grant.LastName,
Email: grant.Email,
DisplayName: grant.DisplayName,
OrgDomain: grant.OrgPrimaryDomain,
OrgName: grant.OrgName,
ProjectName: grant.ProjectName,
AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL),
Id: grant.ID,
UserId: grant.UserID,
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
RoleKeys: grant.Roles,
ProjectId: grant.ProjectID,
OrgId: grant.ResourceOwner,
ProjectGrantId: grant.GrantID,
UserName: grant.Username,
FirstName: grant.FirstName,
LastName: grant.LastName,
Email: grant.Email,
DisplayName: grant.DisplayName,
OrgDomain: grant.OrgPrimaryDomain,
OrgName: grant.OrgName,
ProjectName: grant.ProjectName,
AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL),
PreferredLoginName: grant.PreferredLoginName,
Details: object.ToViewDetailsPb(
grant.Sequence,
grant.CreationDate,

View File

@ -95,7 +95,7 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest)
applicationID = authReq.ApplicationID
userOrgID = authReq.UserOrgID
}
resp, err := o.command.AddUserToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
if err != nil {
return "", time.Time{}, err
}
@ -123,7 +123,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok
if request, ok := req.(op.RefreshTokenRequest); ok {
request.SetCurrentScopes(scopes)
}
resp, token, err := o.command.AddAccessAndRefreshToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(),
resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(),
refreshToken, req.GetAudience(), scopes, authMethodsReferences, o.defaultAccessTokenLifetime,
o.defaultRefreshTokenIdleExpiration, o.defaultRefreshTokenExpiration, authTime) //PLANNED: lifetime from client
if err != nil {
@ -171,7 +171,10 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin
if len(userIDs) == 0 {
return nil
}
err = o.command.HumansSignOut(ctx, userAgentID, userIDs)
data := authz.CtxData{
UserID: userID,
}
err = o.command.HumansSignOut(authz.SetCtxData(ctx, data), userAgentID, userIDs)
logging.Log("OIDC-Dggt2").OnError(err).Error("error signing out")
return err
}
@ -255,3 +258,10 @@ func (o *OPStorage) assertClientScopesForPAT(ctx context.Context, token *model.T
}
return nil
}
func setContextUserSystem(ctx context.Context) context.Context {
data := authz.CtxData{
UserID: "SYSTEM",
}
return authz.SetCtxData(ctx, data)
}

View File

@ -203,7 +203,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
}
instanceID := authz.GetInstance(r.Context()).ID
err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r))
err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r))
if err != nil {
if errors.IsNotFound(err) {
err = nil

View File

@ -84,7 +84,7 @@ func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, auth
return
}
metadata := externalUser.Metadatas
err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r))
err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r))
if err != nil {
l.jwtExtractionUserNotFound(w, r, authReq, idpConfig, tokens, err)
return

View File

@ -16,6 +16,7 @@ import (
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/renderer"
"github.com/caos/zitadel/internal/static"
)
@ -30,6 +31,10 @@ type Renderer struct {
staticStorage static.Storage
}
type LanguageData struct {
Lang string
}
func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage static.Storage, cookieName string, defaultLanguage language.Tag) *Renderer {
r := &Renderer{
pathPrefix: pathPrefix,
@ -345,24 +350,23 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
CSRF: csrf.TemplateField(r),
Nonce: http_mw.GetNonce(r),
}
var privacyPolicy *domain.PrivacyPolicy
if authReq != nil {
baseData.LoginPolicy = authReq.LoginPolicy
baseData.LabelPolicy = authReq.LabelPolicy
baseData.IDPProviders = authReq.AllowedExternalIDPs
if authReq.PrivacyPolicy != nil {
baseData.TOSLink = authReq.PrivacyPolicy.TOSLink
baseData.PrivacyLink = authReq.PrivacyPolicy.PrivacyLink
if authReq.PrivacyPolicy == nil {
return baseData
}
privacyPolicy = authReq.PrivacyPolicy
} else {
privacyPolicy, err := l.query.DefaultPrivacyPolicy(r.Context())
policy, err := l.query.DefaultPrivacyPolicy(r.Context())
if err != nil {
return baseData
}
if privacyPolicy != nil {
baseData.TOSLink = privacyPolicy.TOSLink
baseData.PrivacyLink = privacyPolicy.PrivacyLink
}
privacyPolicy = policy.ToDomain()
}
baseData = l.setLinksOnBaseData(baseData, privacyPolicy)
return baseData
}
@ -392,6 +396,26 @@ func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData {
}
}
func (l *Login) setLinksOnBaseData(baseData baseData, privacyPolicy *domain.PrivacyPolicy) baseData {
lang := LanguageData{
Lang: baseData.Lang,
}
baseData.TOSLink = privacyPolicy.TOSLink
baseData.PrivacyLink = privacyPolicy.PrivacyLink
baseData.HelpLink = privacyPolicy.HelpLink
if link, err := templates.ParseTemplateText(privacyPolicy.TOSLink, lang); err == nil {
baseData.TOSLink = link
}
if link, err := templates.ParseTemplateText(privacyPolicy.PrivacyLink, lang); err == nil {
baseData.PrivacyLink = link
}
if link, err := templates.ParseTemplateText(privacyPolicy.HelpLink, lang); err == nil {
baseData.HelpLink = link
}
return baseData
}
func (l *Login) getErrorMessage(r *http.Request, err error) (errID, errMsg string) {
caosErr := new(caos_errs.CaosError)
if errors.As(err, &caosErr) {
@ -519,6 +543,7 @@ type baseData struct {
DisplayLoginNameSuffix bool
TOSLink string
PrivacyLink string
HelpLink string
AuthReqID string
CSRF template.HTML
Nonce string

View File

@ -297,7 +297,6 @@ Footer:
Tos: AGB
PrivacyPolicy: Datenschutzerklärung
Help: Hilfe
HelpLink: https://docs.zitadel.ch/docs/manuals/user-login
Errors:
Internal: Es ist ein interner Fehler aufgetreten

View File

@ -298,7 +298,6 @@ Footer:
Tos: TOS
PrivacyPolicy: Privacy policy
Help: Help
HelpLink: https://docs.zitadel.ch/docs/manuals/user-login
Errors:
Internal: An internal error occured

View File

@ -298,7 +298,6 @@ Footer:
Tos: Termini di servizio
PrivacyPolicy: l'informativa sulla privacy
Help: Aiuto
HelpLink: 'https://docs.zitadel.ch/docs/manuals/user-login'
Errors:
Internal: Si è verificato un errore interno

View File

@ -13,6 +13,8 @@
{{ if .PrivacyLink }}
<a href="{{.PrivacyLink}}" rel="noopener noreferrer" target="_blank" alt="Privacy Policy">{{t "Footer.PrivacyPolicy"}}</a>
{{end}}
<a href="{{t "Footer.HelpLink"}}" target="_black" alt="Help">{{t "Footer.Help"}}</a>
{{ if .HelpLink }}
<a href="{{.HelpLink}}" rel="noopener noreferrer" target="_blank" alt="Help">{{t "Footer.Help"}}</a>
{{end}}
</footer>
{{end}}

View File

@ -560,7 +560,7 @@ func (repo *AuthRequestRepo) fillPolicies(ctx context.Context, request *domain.A
return err
}
request.LockoutPolicy = lockoutPolicyToDomain(lockoutPolicy)
privacyPolicy, err := repo.getPrivacyPolicy(ctx, orgID)
privacyPolicy, err := repo.GetPrivacyPolicy(ctx, orgID)
if err != nil {
return err
}
@ -936,8 +936,11 @@ func (repo *AuthRequestRepo) mfaSkippedOrSetUp(user *user_model.UserView, reques
return checkVerificationTime(user.MFAInitSkipped, request.LoginPolicy.MFAInitSkipLifetime)
}
func (repo *AuthRequestRepo) getPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) {
func (repo *AuthRequestRepo) GetPrivacyPolicy(ctx context.Context, orgID string) (*domain.PrivacyPolicy, error) {
policy, err := repo.PrivacyPolicyProvider.PrivacyPolicyByOrg(ctx, orgID)
if errors.IsNotFound(err) {
return new(domain.PrivacyPolicy), nil
}
if err != nil {
return nil, err
}
@ -957,6 +960,7 @@ func privacyPolicyToDomain(p *query.PrivacyPolicy) *domain.PrivacyPolicy {
Default: p.IsDefault,
TOSLink: p.TOSLink,
PrivacyLink: p.PrivacyLink,
HelpLink: p.HelpLink,
}
}

View File

@ -4,8 +4,9 @@ import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1"
v1 "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"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
@ -172,19 +173,12 @@ func (u *User) ProcessUser(event *es_models.Event) (err error) {
}
func (u *User) fillLoginNames(user *view_model.UserView) (err error) {
org, err := u.getOrgByID(context.Background(), user.ResourceOwner)
userLoginMustBeDomain, primaryDomain, domains, err := u.loginNameInformation(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
policy := new(query2.OrgIAMPolicy)
if policy == nil {
policy, err = u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
}
user.SetLoginNames(policy, org.Domains)
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
user.SetLoginNames(userLoginMustBeDomain, domains)
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
return nil
}
@ -204,40 +198,26 @@ func (u *User) ProcessOrg(event *es_models.Event) (err error) {
}
func (u *User) fillLoginNamesOnOrgUsers(event *es_models.Event) error {
org, err := u.getOrgByID(context.Background(), event.ResourceOwner)
userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
policy := new(query2.OrgIAMPolicy)
if policy == nil {
policy, err = u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
}
users, err := u.view.UsersByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, user := range users {
user.SetLoginNames(policy, org.Domains)
user.SetLoginNames(userLoginMustBeDomain, domains)
}
return u.view.PutUsers(users, event)
}
func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error {
org, err := u.getOrgByID(context.Background(), event.ResourceOwner)
userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
policy := new(query2.OrgIAMPolicy)
if policy == nil {
policy, err = u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
}
if !policy.UserLoginMustBeDomain {
if !userLoginMustBeDomain {
return nil
}
users, err := u.view.UsersByOrgID(event.AggregateID)
@ -245,7 +225,7 @@ func (u *User) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error {
return err
}
for _, user := range users {
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
}
return u.view.PutUsers(users, event)
}
@ -281,6 +261,17 @@ func (u *User) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, er
return org_es_model.OrgToModel(esOrg), nil
}
func (u *User) getDefaultOrgIAMPolicy(ctx context.Context) (*query2.OrgIAMPolicy, error) {
return u.queries.DefaultOrgIAMPolicy(ctx)
func (u *User) loginNameInformation(ctx context.Context, orgID string) (userLoginMustBeDomain bool, primaryDomain string, domains []*org_model.OrgDomain, err error) {
org, err := u.getOrgByID(ctx, orgID)
if err != nil {
return false, "", nil, err
}
if org.OrgIamPolicy == nil {
policy, err := u.queries.DefaultOrgIAMPolicy(ctx)
if err != nil {
return false, "", nil, err
}
userLoginMustBeDomain = policy.UserLoginMustBeDomain
}
return userLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil
}

View File

@ -1089,10 +1089,6 @@ func (c *Commands) createFooterTextEvents(ctx context.Context, agg *eventstore.A
if event != nil {
events = append(events, event)
}
event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterHelpLink, existingText.FooterHelpLink, text.Footer.HelpLink, text.Language, defaultText)
if event != nil {
events = append(events, event)
}
return events
}

View File

@ -2508,10 +2508,6 @@ func (wm *CustomLoginTextReadModel) handleFooterTextSetEvent(e *policy.CustomTex
wm.FooterHelp = e.Text
return
}
if e.Key == domain.LoginKeyFooterHelpLink {
wm.FooterHelpLink = e.Text
return
}
}
func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.CustomTextRemovedEvent) {
@ -2527,8 +2523,4 @@ func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.Custom
wm.FooterHelp = ""
return
}
if e.Key == domain.LoginKeyFooterHelpLink {
wm.FooterHelpLink = ""
return
}
}

View File

@ -130,6 +130,7 @@ func writeModelToPrivacyPolicy(wm *PrivacyPolicyWriteModel) *domain.PrivacyPolic
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
TOSLink: wm.TOSLink,
PrivacyLink: wm.PrivacyLink,
HelpLink: wm.HelpLink,
}
}

View File

@ -1143,11 +1143,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextSetEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
},
),
),
@ -1441,7 +1436,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
HelpLink: "HelpLink",
},
},
},
@ -2547,11 +2541,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextSetEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -3645,11 +3634,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextRemovedEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English,
),
),
},
),
),
@ -4797,11 +4781,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextSetEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextRemovedEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English,
@ -5892,11 +5871,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextRemovedEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -6990,11 +6964,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
iam.NewCustomTextSetEvent(context.Background(),
&iam.NewAggregate().Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
},
),
),
@ -7288,7 +7257,6 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
HelpLink: "HelpLink",
},
},
},

View File

@ -52,7 +52,7 @@ func (c *Commands) addDefaultPrivacyPolicy(ctx context.Context, iamAgg *eventsto
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-M00rJ", "Errors.IAM.PrivacyPolicy.AlreadyExists")
}
return iam_repo.NewPrivacyPolicyAddedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink), nil
return iam_repo.NewPrivacyPolicyAddedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink), nil
}
func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) {
@ -65,7 +65,7 @@ func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domai
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}

View File

@ -56,7 +56,8 @@ func (wm *IAMPrivacyPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tosLink,
privacyLink string,
privacyLink,
helpLink string,
) (*iam.PrivacyPolicyChangedEvent, bool) {
changes := make([]policy.PrivacyPolicyChanges, 0)
@ -66,6 +67,9 @@ func (wm *IAMPrivacyPolicyWriteModel) NewChangedEvent(
if wm.PrivacyLink != privacyLink {
changes = append(changes, policy.ChangePrivacyLink(privacyLink))
}
if wm.HelpLink != helpLink {
changes = append(changes, policy.ChangeHelpLink(helpLink))
}
if len(changes) == 0 {
return nil, false
}

View File

@ -2,6 +2,10 @@ package command
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@ -9,8 +13,6 @@ import (
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/policy"
"github.com/stretchr/testify/assert"
"testing"
)
func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
@ -42,6 +44,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -52,6 +55,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -71,6 +75,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
},
@ -82,6 +87,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -92,6 +98,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
},
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
},
@ -108,6 +115,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate,
"",
"",
"",
),
),
},
@ -119,6 +127,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
res: res{
@ -129,6 +138,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
},
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
},
@ -183,6 +193,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -200,6 +211,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -210,6 +222,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -227,6 +240,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
&iam.NewAggregate().Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -236,6 +250,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
newDefaultPrivacyPolicyChangedEvent(context.Background(),
"TOSLinkChanged",
"PrivacyLinkChanged",
"HelpLinkChanged",
),
),
},
@ -247,6 +262,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
},
},
res: res{
@ -257,6 +273,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
},
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
},
},
},
@ -280,12 +297,13 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
}
}
func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink string) *iam.PrivacyPolicyChangedEvent {
func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink, helpLink string) *iam.PrivacyPolicyChangedEvent {
event, _ := iam.NewPrivacyPolicyChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
[]policy.PrivacyPolicyChanges{
policy.ChangeTOSLink(tosLink),
policy.ChangePrivacyLink(privacyLink),
policy.ChangeHelpLink(helpLink),
},
)
return event

View File

@ -1161,11 +1161,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
},
),
),
@ -1460,7 +1455,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
HelpLink: "HelpLink",
},
},
},
@ -2566,11 +2560,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -3664,11 +3653,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English,
),
),
},
),
),
@ -4816,11 +4800,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English,
@ -5911,11 +5890,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -7009,11 +6983,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelpLink, "HelpLink", language.English,
),
),
},
),
),
@ -7308,7 +7277,6 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
HelpLink: "HelpLink",
},
},
},

View File

@ -5,12 +5,13 @@ import (
"testing"
"time"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static/mock"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/repository/user"
"github.com/caos/zitadel/internal/static/mock"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@ -266,6 +267,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helpLink",
),
),
),
@ -470,6 +472,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),
@ -686,6 +689,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),
@ -912,6 +916,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),
@ -1203,6 +1208,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),
@ -1420,6 +1426,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),
@ -1684,6 +1691,7 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) {
&iam.NewAggregate().Aggregate,
"toslink",
"privacylink",
"helplink",
),
),
),

View File

@ -80,7 +80,7 @@ func (c *Commands) RemoveOrgIAMPolicy(ctx context.Context, orgID string) error {
return err
}
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAM.NotFound")
return caos_errs.ThrowNotFound(nil, "ORG-Dvsh3", "Errors.Org.OrgIAMPolicy.NotFound")
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)

View File

@ -48,7 +48,8 @@ func (c *Commands) AddPrivacyPolicy(ctx context.Context, resourceOwner string, p
ctx,
orgAgg,
policy.TOSLink,
policy.PrivacyLink))
policy.PrivacyLink,
policy.HelpLink))
if err != nil {
return nil, err
}
@ -74,7 +75,7 @@ func (c *Commands) ChangePrivacyPolicy(ctx context.Context, resourceOwner string
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4N9fs", "Errors.Org.PrivacyPolicy.NotChanged")
}

View File

@ -57,7 +57,8 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tosLink,
privacyLink string,
privacyLink,
helpLink string,
) (*org.PrivacyPolicyChangedEvent, bool) {
changes := make([]policy.PrivacyPolicyChanges, 0)
@ -67,6 +68,9 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent(
if wm.PrivacyLink != privacyLink {
changes = append(changes, policy.ChangePrivacyLink(privacyLink))
}
if wm.HelpLink != helpLink {
changes = append(changes, policy.ChangeHelpLink(helpLink))
}
if len(changes) == 0 {
return nil, false
}

View File

@ -46,6 +46,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -63,6 +64,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -74,6 +76,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -93,6 +96,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
},
@ -105,6 +109,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -115,6 +120,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
},
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
},
@ -131,6 +137,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"",
"",
"",
),
),
},
@ -143,6 +150,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
res: res{
@ -153,6 +161,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
},
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
},
@ -207,6 +216,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -227,6 +237,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -244,6 +255,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -255,6 +267,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
},
},
res: res{
@ -272,13 +285,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange"),
newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange"),
),
},
),
@ -290,6 +304,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
},
},
res: res{
@ -300,6 +315,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
},
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
},
},
},
@ -314,13 +330,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newPrivacyPolicyChangedEvent(context.Background(), "org1", "", ""),
newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", ""),
),
},
),
@ -332,6 +349,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
policy: &domain.PrivacyPolicy{
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
res: res{
@ -342,6 +360,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
},
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
},
},
},
@ -424,6 +443,7 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) {
&org.NewAggregate("org1", "org1").Aggregate,
"TOSLink",
"PrivacyLink",
"HelpLink",
),
),
),
@ -467,12 +487,13 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) {
}
}
func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink string) *org.PrivacyPolicyChangedEvent {
func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink, helpLink string) *org.PrivacyPolicyChangedEvent {
event, _ := org.NewPrivacyPolicyChangedEvent(ctx,
&org.NewAggregate(orgID, orgID).Aggregate,
[]policy.PrivacyPolicyChanges{
policy.ChangeTOSLink(tosLink),
policy.ChangePrivacyLink(privacyLink),
policy.ChangeHelpLink(helpLink),
},
)
return event

View File

@ -11,6 +11,7 @@ type PrivacyPolicyWriteModel struct {
TOSLink string
PrivacyLink string
HelpLink string
State domain.PolicyState
}
@ -20,6 +21,7 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error {
case *policy.PrivacyPolicyAddedEvent:
wm.TOSLink = e.TOSLink
wm.PrivacyLink = e.PrivacyLink
wm.HelpLink = e.HelpLink
wm.State = domain.PolicyStateActive
case *policy.PrivacyPolicyChangedEvent:
if e.PrivacyLink != nil {
@ -28,6 +30,9 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error {
if e.TOSLink != nil {
wm.TOSLink = *e.TOSLink
}
if e.HelpLink != nil {
wm.HelpLink = *e.HelpLink
}
case *policy.PrivacyPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}

View File

@ -38,7 +38,7 @@ func (c *Commands) ChangeUsername(ctx context.Context, orgID, userID, userName s
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.OrgIAM.NotExisting")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-38fnu", "Errors.Org.OrgIAMPolicy.NotExisting")
}
if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil {
@ -188,7 +188,7 @@ func (c *Commands) RemoveUser(ctx context.Context, userID, resourceOwner string,
orgIAMPolicy, err := c.getOrgIAMPolicy(ctx, existingUser.ResourceOwner)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAM.NotExisting")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-3M9fs", "Errors.Org.OrgIAMPolicy.NotExisting")
}
var events []eventstore.Command
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)

View File

@ -34,7 +34,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
}
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound")
}
events, addedHuman, err := c.addHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
if err != nil {
@ -63,7 +63,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
}
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
if err != nil {
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexity.NotFound")
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexityPolicy.NotFound")
}
events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
if err != nil {
@ -128,7 +128,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
}
pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound")
}
loginPolicy, err := c.getOrgLoginPolicy(ctx, orgID)
if err != nil {

View File

@ -28,7 +28,7 @@ func (c *Commands) AddHumanOTP(ctx context.Context, userID, resourceowner string
orgPolicy, err := c.getOrgIAMPolicy(ctx, org.AggregateID)
if err != nil {
logging.Log("COMMAND-y5zv9").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get org policy for loginname")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAM.NotFound")
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-8ugTs", "Errors.Org.OrgIAMPolicy.NotFound")
}
otpWriteModel, err := c.otpWriteModelByID(ctx, userID, resourceowner)
if err != nil {

View File

@ -294,7 +294,6 @@ const (
LoginKeyFooterTOS = LoginKeyFooter + "Tos"
LoginKeyFooterPrivacyPolicy = LoginKeyFooter + "PrivacyPolicy"
LoginKeyFooterHelp = LoginKeyFooter + "Help"
LoginKeyFooterHelpLink = LoginKeyFooter + "HelpLink"
)
type CustomLoginText struct {
@ -637,7 +636,6 @@ type FooterText struct {
TOS string
PrivacyPolicy string
Help string
HelpLink string
}
type PasswordlessPromptScreenText struct {

View File

@ -12,4 +12,5 @@ type PrivacyPolicy struct {
TOSLink string
PrivacyLink string
HelpLink string
}

View File

@ -894,7 +894,4 @@ func footerKeyToDomain(text *CustomTextView, result *domain.CustomLoginText) {
if text.Key == domain.LoginKeyFooterHelp {
result.Footer.Help = text.Text
}
if text.Key == domain.LoginKeyFooterHelpLink {
result.Footer.HelpLink = text.Text
}
}

View File

@ -3,14 +3,14 @@ package handler
import (
"context"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
org_view "github.com/caos/zitadel/internal/org/repository/view"
query2 "github.com/caos/zitadel/internal/query"
"github.com/caos/logging"
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"
@ -163,23 +163,16 @@ func (u *NotifyUser) ProcessOrg(event *es_models.Event) (err error) {
}
func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error {
org, err := u.getOrgByID(context.Background(), event.ResourceOwner)
userLoginMustBeDomain, _, domains, err := u.loginNameInformation(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
policy := new(query2.OrgIAMPolicy)
if policy == nil {
policy, err = u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
}
users, err := u.view.NotifyUsersByOrgID(event.AggregateID)
if err != nil {
return err
}
for _, user := range users {
user.SetLoginNames(policy, org.Domains)
user.SetLoginNames(userLoginMustBeDomain, domains)
err := u.view.PutNotifyUser(user, event)
if err != nil {
return err
@ -189,16 +182,11 @@ func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *es_models.Event) error {
}
func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) error {
org, err := u.getOrgByID(context.Background(), event.ResourceOwner)
userLoginMustBeDomain, primaryDomain, _, err := u.loginNameInformation(context.Background(), event.ResourceOwner)
if err != nil {
return err
}
policy, err := u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
if !policy.UserLoginMustBeDomain {
if !userLoginMustBeDomain {
return nil
}
users, err := u.view.NotifyUsersByOrgID(event.AggregateID)
@ -206,7 +194,7 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) e
return err
}
for _, user := range users {
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
err := u.view.PutNotifyUser(user, event)
if err != nil {
return err
@ -216,17 +204,12 @@ func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *es_models.Event) e
}
func (u *NotifyUser) fillLoginNames(user *view_model.NotifyUser) (err error) {
org, err := u.getOrgByID(context.Background(), user.ResourceOwner)
userLoginMustBeDomain, primaryDomain, domains, err := u.loginNameInformation(context.Background(), user.ResourceOwner)
if err != nil {
return err
}
policy, err := u.getDefaultOrgIAMPolicy(context.Background())
if err != nil {
return err
}
user.SetLoginNames(policy, org.Domains)
user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain)
user.SetLoginNames(userLoginMustBeDomain, domains)
user.PreferredLoginName = user.GenerateLoginName(primaryDomain, userLoginMustBeDomain)
return nil
}
@ -261,6 +244,17 @@ func (u *NotifyUser) getOrgByID(ctx context.Context, orgID string) (*org_model.O
return org_es_model.OrgToModel(esOrg), nil
}
func (u *NotifyUser) getDefaultOrgIAMPolicy(ctx context.Context) (*query2.OrgIAMPolicy, error) {
return u.queries.DefaultOrgIAMPolicy(ctx)
func (u *NotifyUser) loginNameInformation(ctx context.Context, orgID string) (userLoginMustBeDomain bool, primaryDomain string, domains []*org_model.OrgDomain, err error) {
org, err := u.getOrgByID(ctx, orgID)
if err != nil {
return false, "", nil, err
}
if org.OrgIamPolicy == nil {
policy, err := u.queries.DefaultOrgIAMPolicy(ctx)
if err != nil {
return false, "", nil, err
}
userLoginMustBeDomain = policy.UserLoginMustBeDomain
}
return userLoginMustBeDomain, org.GetPrimaryDomain().Domain, org.Domains, nil
}

View File

@ -1101,7 +1101,4 @@ func footerKeyToDomain(text *CustomText, result *domain.CustomLoginText) {
if text.Key == domain.LoginKeyFooterHelp {
result.Footer.Help = text.Text
}
if text.Key == domain.LoginKeyFooterHelpLink {
result.Footer.HelpLink = text.Text
}
}

View File

@ -24,6 +24,7 @@ type PrivacyPolicy struct {
TOSLink string
PrivacyLink string
HelpLink string
IsDefault bool
}
@ -64,6 +65,10 @@ var (
name: projection.PrivacyPolicyTOSLinkCol,
table: privacyTable,
}
PrivacyColHelpLink = Column{
name: projection.PrivacyPolicyHelpLinkCol,
table: privacyTable,
}
PrivacyColIsDefault = Column{
name: projection.PrivacyPolicyIsDefaultCol,
table: privacyTable,
@ -125,6 +130,7 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli
PrivacyColResourceOwner.identifier(),
PrivacyColPrivacyLink.identifier(),
PrivacyColTOSLink.identifier(),
PrivacyColHelpLink.identifier(),
PrivacyColIsDefault.identifier(),
PrivacyColState.identifier(),
).
@ -139,6 +145,7 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli
&policy.ResourceOwner,
&policy.PrivacyLink,
&policy.TOSLink,
&policy.HelpLink,
&policy.IsDefault,
&policy.State,
)
@ -151,3 +158,12 @@ func preparePrivacyPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*PrivacyPoli
return policy, nil
}
}
func (p *PrivacyPolicy) ToDomain() *domain.PrivacyPolicy {
return &domain.PrivacyPolicy{
TOSLink: p.TOSLink,
PrivacyLink: p.PrivacyLink,
HelpLink: p.HelpLink,
Default: p.IsDefault,
}
}

View File

@ -35,6 +35,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
` projections.privacy_policies.resource_owner,`+
` projections.privacy_policies.privacy_link,`+
` projections.privacy_policies.tos_link,`+
` projections.privacy_policies.help_link,`+
` projections.privacy_policies.is_default,`+
` projections.privacy_policies.state`+
` FROM projections.privacy_policies`),
@ -43,7 +44,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
),
err: func(err error) (error, bool) {
if !errs.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
return fmt.Errorf("err should be NotFoundError got: %w", err), false
}
return nil, true
},
@ -62,6 +63,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
` projections.privacy_policies.resource_owner,`+
` projections.privacy_policies.privacy_link,`+
` projections.privacy_policies.tos_link,`+
` projections.privacy_policies.help_link,`+
` projections.privacy_policies.is_default,`+
` projections.privacy_policies.state`+
` FROM projections.privacy_policies`),
@ -73,6 +75,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
"resource_owner",
"privacy_link",
"tos_link",
"help_link",
"is_default",
"state",
},
@ -84,6 +87,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
"ro",
"privacy.ch",
"tos.ch",
"help.ch",
true,
domain.PolicyStateActive,
},
@ -98,6 +102,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
State: domain.PolicyStateActive,
PrivacyLink: "privacy.ch",
TOSLink: "tos.ch",
HelpLink: "help.ch",
IsDefault: true,
},
},
@ -113,6 +118,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
` projections.privacy_policies.resource_owner,`+
` projections.privacy_policies.privacy_link,`+
` projections.privacy_policies.tos_link,`+
` projections.privacy_policies.help_link,`+
` projections.privacy_policies.is_default,`+
` projections.privacy_policies.state`+
` FROM projections.privacy_policies`),

View File

@ -58,15 +58,21 @@ var (
type ProjectGrantMembersQuery struct {
MembersQuery
ProjectID, GrantID string
ProjectID, GrantID, OrgID string
}
func (q *ProjectGrantMembersQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
return q.MembersQuery.
toQuery(query).
Where(sq.Eq{
ProjectGrantMemberProjectID.identifier(): q.ProjectID,
ProjectGrantMemberGrantID.identifier(): q.GrantID,
Where(sq.And{
sq.Eq{
ProjectGrantMemberProjectID.identifier(): q.ProjectID,
ProjectGrantMemberGrantID.identifier(): q.GrantID,
},
sq.Or{
sq.Eq{ProjectGrantColumnResourceOwner.identifier(): q.OrgID},
sq.Eq{ProjectGrantColumnGrantedOrgID.identifier(): q.OrgID},
},
})
}
@ -117,6 +123,7 @@ func prepareProjectGrantMembersQuery() (sq.SelectBuilder, func(*sql.Rows) (*Memb
LeftJoin(join(HumanUserIDCol, ProjectGrantMemberUserID)).
LeftJoin(join(MachineUserIDCol, ProjectGrantMemberUserID)).
LeftJoin(join(LoginNameUserIDCol, ProjectGrantMemberUserID)).
LeftJoin(join(ProjectGrantColumnGrantID, ProjectGrantMemberGrantID)).
Where(
sq.Eq{LoginNameIsPrimaryCol.identifier(): true},
).PlaceholderFormat(sq.Dollar),

View File

@ -34,6 +34,8 @@ var (
"ON members.user_id = projections.users_machines.user_id " +
"LEFT JOIN projections.login_names " +
"ON members.user_id = projections.login_names.user_id " +
"LEFT JOIN projections.project_grants " +
"ON members.grant_id = projections.project_grants.grant_id " +
"WHERE projections.login_names.is_primary = $1")
projectGrantMembersColumns = []string{
"creation_date",

View File

@ -76,6 +76,7 @@ func (p *FlowProjection) reduceTriggerActionsSetEventType(event eventstore.Event
[]handler.Condition{
handler.NewCond(FlowTypeCol, e.FlowType),
handler.NewCond(FlowTriggerTypeCol, e.TriggerType),
handler.NewCond(FlowResourceOwnerCol, e.Aggregate().ResourceOwner),
},
)
for i, id := range e.ActionIDs {
@ -104,6 +105,7 @@ func (p *FlowProjection) reduceFlowClearedEventType(event eventstore.Event) (*ha
e,
[]handler.Condition{
handler.NewCond(FlowTypeCol, e.FlowType),
handler.NewCond(FlowResourceOwnerCol, e.Aggregate().ResourceOwner),
},
), nil
}

View File

@ -39,10 +39,11 @@ func TestFlowProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (trigger_type = $2)",
expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (trigger_type = $2) AND (resource_owner = $3)",
expectedArgs: []interface{}{
domain.FlowTypeExternalAuthentication,
domain.TriggerTypePostAuthentication,
"ro-id",
},
},
{
@ -93,9 +94,10 @@ func TestFlowProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1)",
expectedStmt: "DELETE FROM projections.flows_triggers WHERE (flow_type = $1) AND (resource_owner = $2)",
expectedArgs: []interface{}{
domain.FlowTypeExternalAuthentication,
"ro-id",
},
},
},

View File

@ -26,6 +26,7 @@ const (
PrivacyPolicyInstanceIDCol = "instance_id"
PrivacyPolicyPrivacyLinkCol = "privacy_link"
PrivacyPolicyTOSLinkCol = "tos_link"
PrivacyPolicyHelpLinkCol = "help_link"
)
type PrivacyPolicyProjection struct {
@ -48,6 +49,7 @@ func NewPrivacyPolicyProjection(ctx context.Context, config crdb.StatementHandle
crdb.NewColumn(PrivacyPolicyInstanceIDCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyPrivacyLinkCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyTOSLinkCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyHelpLinkCol, crdb.ColumnTypeText),
},
crdb.NewPrimaryKey(PrivacyPolicyInstanceIDCol, PrivacyPolicyIDCol),
),
@ -114,6 +116,7 @@ func (p *PrivacyPolicyProjection) reduceAdded(event eventstore.Event) (*handler.
handler.NewCol(PrivacyPolicyStateCol, domain.PolicyStateActive),
handler.NewCol(PrivacyPolicyPrivacyLinkCol, policyEvent.PrivacyLink),
handler.NewCol(PrivacyPolicyTOSLinkCol, policyEvent.TOSLink),
handler.NewCol(PrivacyPolicyHelpLinkCol, policyEvent.HelpLink),
handler.NewCol(PrivacyPolicyIsDefaultCol, isDefault),
handler.NewCol(PrivacyPolicyResourceOwnerCol, policyEvent.Aggregate().ResourceOwner),
handler.NewCol(PrivacyPolicyInstanceIDCol, policyEvent.Aggregate().InstanceID),
@ -140,6 +143,9 @@ func (p *PrivacyPolicyProjection) reduceChanged(event eventstore.Event) (*handle
if policyEvent.TOSLink != nil {
cols = append(cols, handler.NewCol(PrivacyPolicyTOSLinkCol, *policyEvent.TOSLink))
}
if policyEvent.HelpLink != nil {
cols = append(cols, handler.NewCol(PrivacyPolicyHelpLinkCol, *policyEvent.HelpLink))
}
return crdb.NewUpdateStatement(
&policyEvent,
cols,

View File

@ -30,7 +30,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
org.AggregateType,
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link"
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
), org.PrivacyPolicyAddedEventMapper),
},
@ -43,7 +44,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -52,6 +53,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
domain.PolicyStateActive,
"http://privacy.link",
"http://tos.link",
"http://help.link",
false,
"ro-id",
"instance-id",
@ -70,7 +72,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
org.AggregateType,
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link"
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
), org.PrivacyPolicyChangedEventMapper),
},
@ -82,12 +85,13 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link) = ($1, $2, $3, $4) WHERE (id = $5)",
expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"http://privacy.link",
"http://tos.link",
"http://help.link",
"agg-id",
},
},
@ -131,7 +135,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
iam.AggregateType,
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link"
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
), iam.PrivacyPolicyAddedEventMapper),
},
@ -143,7 +148,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
expectedStmt: "INSERT INTO projections.privacy_policies (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -152,6 +157,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
domain.PolicyStateActive,
"http://privacy.link",
"http://tos.link",
"http://help.link",
true,
"ro-id",
"instance-id",
@ -170,7 +176,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
iam.AggregateType,
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link"
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
), iam.PrivacyPolicyChangedEventMapper),
},
@ -182,12 +189,13 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link) = ($1, $2, $3, $4) WHERE (id = $5)",
expectedStmt: "UPDATE projections.privacy_policies SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"http://privacy.link",
"http://tos.link",
"http://help.link",
"agg-id",
},
},

View File

@ -25,15 +25,16 @@ type UserGrant struct {
GrantID string
State domain.UserGrantState
UserID string
Username string
UserType domain.UserType
UserResourceOwner string
FirstName string
LastName string
Email string
DisplayName string
AvatarURL string
UserID string
Username string
UserType domain.UserType
UserResourceOwner string
FirstName string
LastName string
Email string
DisplayName string
AvatarURL string
PreferredLoginName string
ResourceOwner string
OrgName string
@ -255,6 +256,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro
HumanEmailCol.identifier(),
HumanDisplayNameCol.identifier(),
HumanAvatarURLCol.identifier(),
LoginNameNameCol.identifier(),
UserGrantResourceOwner.identifier(),
OrgColumnName.identifier(),
@ -268,20 +270,24 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
PlaceholderFormat(sq.Dollar),
LeftJoin(join(LoginNameUserIDCol, UserGrantUserID)).
Where(
sq.Eq{LoginNameIsPrimaryCol.identifier(): true},
).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*UserGrant, error) {
g := new(UserGrant)
var (
roles = pq.StringArray{}
username sql.NullString
firstName sql.NullString
userType sql.NullInt32
userOwner sql.NullString
lastName sql.NullString
email sql.NullString
displayName sql.NullString
avatarURL sql.NullString
roles = pq.StringArray{}
username sql.NullString
firstName sql.NullString
userType sql.NullInt32
userOwner sql.NullString
lastName sql.NullString
email sql.NullString
displayName sql.NullString
avatarURL sql.NullString
preferredLoginName sql.NullString
orgName sql.NullString
orgDomain sql.NullString
@ -307,6 +313,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro
&email,
&displayName,
&avatarURL,
&preferredLoginName,
&g.ResourceOwner,
&orgName,
@ -331,6 +338,7 @@ func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, erro
g.Email = email.String
g.DisplayName = displayName.String
g.AvatarURL = avatarURL.String
g.PreferredLoginName = preferredLoginName.String
g.OrgName = orgName.String
g.OrgPrimaryDomain = orgDomain.String
g.ProjectName = projectName.String
@ -358,6 +366,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e
HumanEmailCol.identifier(),
HumanDisplayNameCol.identifier(),
HumanAvatarURLCol.identifier(),
LoginNameNameCol.identifier(),
UserGrantResourceOwner.identifier(),
OrgColumnName.identifier(),
@ -373,7 +382,10 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
PlaceholderFormat(sq.Dollar),
LeftJoin(join(LoginNameUserIDCol, UserGrantUserID)).
Where(
sq.Eq{LoginNameIsPrimaryCol.identifier(): true},
).PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*UserGrants, error) {
userGrants := make([]*UserGrant, 0)
var count uint64
@ -381,15 +393,16 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e
g := new(UserGrant)
var (
roles = pq.StringArray{}
username sql.NullString
userType sql.NullInt32
userOwner sql.NullString
firstName sql.NullString
lastName sql.NullString
email sql.NullString
displayName sql.NullString
avatarURL sql.NullString
roles = pq.StringArray{}
username sql.NullString
userType sql.NullInt32
userOwner sql.NullString
firstName sql.NullString
lastName sql.NullString
email sql.NullString
displayName sql.NullString
avatarURL sql.NullString
preferredLoginName sql.NullString
orgName sql.NullString
orgDomain sql.NullString
@ -415,6 +428,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e
&email,
&displayName,
&avatarURL,
&preferredLoginName,
&g.ResourceOwner,
&orgName,
@ -438,6 +452,7 @@ func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, e
g.Email = email.String
g.DisplayName = displayName.String
g.AvatarURL = avatarURL.String
g.PreferredLoginName = preferredLoginName.String
g.OrgName = orgName.String
g.OrgPrimaryDomain = orgDomain.String
g.ProjectName = projectName.String

View File

@ -32,6 +32,7 @@ var (
", projections.users_humans.email" +
", projections.users_humans.display_name" +
", projections.users_humans.avatar_key" +
", projections.login_names.login_name" +
", projections.user_grants.resource_owner" +
", projections.orgs.name" +
", projections.orgs.primary_domain" +
@ -41,7 +42,9 @@ var (
" LEFT JOIN projections.users ON projections.user_grants.user_id = projections.users.id" +
" LEFT JOIN projections.users_humans ON projections.user_grants.user_id = projections.users_humans.user_id" +
" LEFT JOIN projections.orgs ON projections.user_grants.resource_owner = projections.orgs.id" +
" LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id")
" LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id" +
" LEFT JOIN projections.login_names ON projections.user_grants.user_id = projections.login_names.user_id" +
" WHERE projections.login_names.is_primary = $1")
userGrantCols = []string{
"id",
"creation_date",
@ -59,6 +62,7 @@ var (
"email",
"display_name",
"avatar_key",
"login_name",
"resource_owner", //user_grant resource owner
"name", //org name
"primary_domain",
@ -82,6 +86,7 @@ var (
", projections.users_humans.email" +
", projections.users_humans.display_name" +
", projections.users_humans.avatar_key" +
", projections.login_names.login_name" +
", projections.user_grants.resource_owner" +
", projections.orgs.name" +
", projections.orgs.primary_domain" +
@ -92,7 +97,9 @@ var (
" LEFT JOIN projections.users ON projections.user_grants.user_id = projections.users.id" +
" LEFT JOIN projections.users_humans ON projections.user_grants.user_id = projections.users_humans.user_id" +
" LEFT JOIN projections.orgs ON projections.user_grants.resource_owner = projections.orgs.id" +
" LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id")
" LEFT JOIN projections.projects ON projections.user_grants.project_id = projections.projects.id" +
" LEFT JOIN projections.login_names ON projections.user_grants.user_id = projections.login_names.user_id" +
" WHERE projections.login_names.is_primary = $1")
userGrantsCols = append(
userGrantCols,
"count",
@ -152,6 +159,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -161,27 +169,28 @@ func Test_UserGrantPrepares(t *testing.T) {
),
},
object: &UserGrant{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
{
@ -208,6 +217,7 @@ func Test_UserGrantPrepares(t *testing.T) {
nil,
nil,
nil,
"login-name",
"ro",
"org-name",
"primary-domain",
@ -217,27 +227,28 @@ func Test_UserGrantPrepares(t *testing.T) {
),
},
object: &UserGrant{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "",
LastName: "",
Email: "",
DisplayName: "",
AvatarURL: "",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "",
LastName: "",
Email: "",
DisplayName: "",
AvatarURL: "",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
{
@ -264,6 +275,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
nil,
nil,
@ -273,27 +285,28 @@ func Test_UserGrantPrepares(t *testing.T) {
),
},
object: &UserGrant{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "",
OrgPrimaryDomain: "",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "",
OrgPrimaryDomain: "",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
{
@ -320,6 +333,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -329,27 +343,86 @@ func Test_UserGrantPrepares(t *testing.T) {
),
},
object: &UserGrant{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "",
},
},
{
name: "prepareUserGrantQuery (no loginname) found",
prepare: prepareUserGrantQuery,
want: want{
sqlExpectations: mockQuery(
userGrantStmt,
userGrantCols,
[]driver.Value{
"id",
testNow,
testNow,
20211111,
"grant-id",
pq.StringArray{"role-key"},
domain.UserGrantStateActive,
"user-id",
"username",
domain.UserTypeHuman,
"resource-owner",
"first-name",
"last-name",
"email",
"display-name",
"avatar-key",
nil,
"ro",
"org-name",
"primary-domain",
"project-id",
"project-name",
},
),
},
object: &UserGrant{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
{
@ -406,6 +479,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -421,27 +495,28 @@ func Test_UserGrantPrepares(t *testing.T) {
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
},
@ -471,6 +546,7 @@ func Test_UserGrantPrepares(t *testing.T) {
nil,
nil,
nil,
"login-name",
"ro",
"org-name",
"primary-domain",
@ -486,27 +562,28 @@ func Test_UserGrantPrepares(t *testing.T) {
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "",
LastName: "",
Email: "",
DisplayName: "",
AvatarURL: "",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "",
LastName: "",
Email: "",
DisplayName: "",
AvatarURL: "",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
},
@ -536,6 +613,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
nil,
nil,
@ -551,27 +629,28 @@ func Test_UserGrantPrepares(t *testing.T) {
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "",
OrgPrimaryDomain: "",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeMachine,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "",
OrgPrimaryDomain: "",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
},
@ -601,6 +680,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -616,27 +696,95 @@ func Test_UserGrantPrepares(t *testing.T) {
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "",
},
},
},
},
{
name: "prepareUserGrantsQuery one grant (no loginname)",
prepare: prepareUserGrantsQuery,
want: want{
sqlExpectations: mockQueries(
userGrantsStmt,
userGrantsCols,
[][]driver.Value{
{
"id",
testNow,
testNow,
20211111,
"grant-id",
pq.StringArray{"role-key"},
domain.UserGrantStateActive,
"user-id",
"username",
domain.UserTypeHuman,
"resource-owner",
"first-name",
"last-name",
"email",
"display-name",
"avatar-key",
nil,
"ro",
"org-name",
"primary-domain",
"project-id",
"project-name",
},
},
),
},
object: &UserGrants{
SearchResponse: SearchResponse{
Count: 1,
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
},
@ -666,6 +814,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -689,6 +838,7 @@ func Test_UserGrantPrepares(t *testing.T) {
"email",
"display-name",
"avatar-key",
"login-name",
"ro",
"org-name",
"primary-domain",
@ -704,50 +854,52 @@ func Test_UserGrantPrepares(t *testing.T) {
},
UserGrants: []*UserGrant{
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
{
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
ID: "id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211111,
Roles: []string{"role-key"},
GrantID: "grant-id",
State: domain.UserGrantStateActive,
UserID: "user-id",
Username: "username",
UserType: domain.UserTypeHuman,
UserResourceOwner: "resource-owner",
FirstName: "first-name",
LastName: "last-name",
Email: "email",
DisplayName: "display-name",
AvatarURL: "avatar-key",
PreferredLoginName: "login-name",
ResourceOwner: "ro",
OrgName: "org-name",
OrgPrimaryDomain: "primary-domain",
ProjectID: "project-id",
ProjectName: "project-name",
},
},
},

View File

@ -70,6 +70,14 @@ func NewMembershipOrgIDQuery(value string) (SearchQuery, error) {
return NewTextQuery(membershipOrgID, value, TextEquals)
}
func NewMembershipResourceOwnersSearchQuery(ids ...string) (SearchQuery, error) {
list := make([]interface{}, len(ids))
for i, value := range ids {
list[i] = value
}
return NewListQuery(membershipResourceOwner, list, ListIn)
}
func NewMembershipProjectIDQuery(value string) (SearchQuery, error) {
return NewTextQuery(membershipProjectID, value, TextEquals)
}

View File

@ -6,13 +6,17 @@ import (
"github.com/caos/zitadel/internal/domain"
)
func (q *Queries) MyZitadelPermissions(ctx context.Context, userID string) (*domain.Permissions, error) {
func (q *Queries) MyZitadelPermissions(ctx context.Context, orgID, userID string) (*domain.Permissions, error) {
userIDQuery, err := NewMembershipUserIDQuery(userID)
if err != nil {
return nil, err
}
orgIDsQuery, err := NewMembershipResourceOwnersSearchQuery(orgID, domain.IAMID)
if err != nil {
return nil, err
}
memberships, err := q.Memberships(ctx, &MembershipSearchQuery{
Queries: []SearchQuery{userIDQuery},
Queries: []SearchQuery{userIDQuery, orgIDsQuery},
})
if err != nil {
return nil, err

View File

@ -22,7 +22,8 @@ func NewPrivacyPolicyAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tosLink,
privacyLink string,
privacyLink,
helpLink string,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent(
@ -31,7 +32,8 @@ func NewPrivacyPolicyAddedEvent(
aggregate,
PrivacyPolicyAddedEventType),
tosLink,
privacyLink),
privacyLink,
helpLink),
}
}

View File

@ -23,7 +23,8 @@ func NewPrivacyPolicyAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
tosLink,
privacyLink string,
privacyLink,
helpLink string,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent(
@ -32,7 +33,8 @@ func NewPrivacyPolicyAddedEvent(
aggregate,
PrivacyPolicyAddedEventType),
tosLink,
privacyLink),
privacyLink,
helpLink),
}
}

View File

@ -20,6 +20,7 @@ type PrivacyPolicyAddedEvent struct {
TOSLink string `json:"tosLink,omitempty"`
PrivacyLink string `json:"privacyLink,omitempty"`
HelpLink string `json:"helpLink,omitempty"`
}
func (e *PrivacyPolicyAddedEvent) Data() interface{} {
@ -33,12 +34,14 @@ func (e *PrivacyPolicyAddedEvent) UniqueConstraints() []*eventstore.EventUniqueC
func NewPrivacyPolicyAddedEvent(
base *eventstore.BaseEvent,
tosLink,
privacyLink string,
privacyLink,
helpLink string,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
BaseEvent: *base,
TOSLink: tosLink,
PrivacyLink: privacyLink,
HelpLink: helpLink,
}
}
@ -59,6 +62,7 @@ type PrivacyPolicyChangedEvent struct {
TOSLink *string `json:"tosLink,omitempty"`
PrivacyLink *string `json:"privacyLink,omitempty"`
HelpLink *string `json:"helpLink,omitempty"`
}
func (e *PrivacyPolicyChangedEvent) Data() interface{} {
@ -99,6 +103,12 @@ func ChangePrivacyLink(privacyLink string) func(*PrivacyPolicyChangedEvent) {
}
}
func ChangeHelpLink(helpLink string) func(*PrivacyPolicyChangedEvent) {
return func(e *PrivacyPolicyChangedEvent) {
e.HelpLink = &helpLink
}
}
func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &PrivacyPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@ -193,22 +193,22 @@ Errors:
NotChanged: Default Message Text wurde nicht verändert
AlreadyExists: Default Message Text existiert bereits
Invalid: Default Message Text ist ungültig
PasswordComplexity:
PasswordComplexityPolicy:
NotFound: Password Komplexitäts Policy konnte nicht gefunden werden
Empty: Passwort Komplexitäts Policy ist leer
NotExisting: Passwort Komplexitäts Policy existiert nicht
AlreadyExists: Passwort Komplexitäts Policy existiert bereits
PasswordLockout:
PasswordLockoutPolicy:
NotFound: Password Lockout Policy konnte nicht gefunden werden
Empty: Passwort Lockout Policy ist leer
NotExisting: Passwort Lockout Policy existiert nicht
AlreadyExists: Passwort Lockout Policy existiert bereits
PasswordAge:
PasswordAgePolicy:
NotFound: Password Age Policy konnte nicht gefunden werden
Empty: Passwort Age Policy ist leer
NotExisting: Passwort Age Policy existiert nicht
AlreadyExists: Passwort Age Policy existiert bereits
OrgIAM:
OrgIAMPolicy:
Empty: Org IAM Policy ist leer
NotExisting: Org IAM Policy existiert nicht
AlreadyExists: Org IAM Policy existiert bereits
@ -589,6 +589,10 @@ EventTypes:
cascade:
removed: Berechtigung entfernt
changed: Berechtigung geändert
metadata:
set: Benutzer Metadaten gesetzt
removed: Benutzer Metadaten gelöscht
removed.all: Alle Benutzer Metadaten gelöscht
org:
added: Organisation hinzugefügt
changed: Organisation geändert

View File

@ -193,22 +193,22 @@ Errors:
NotChanged: Default Message Text has not been changed
AlreadyExists: Default Message Text already exists
Invalid: Default Message Text is invalid
PasswordComplexity:
PasswordComplexityPolicy:
NotFound: Password Complexity Policy not found
Empty: Password Complexity Policy is empty
NotExisting: Password Complexity Policy doesn't exist
AlreadyExists: Password Complexity Policy already exists
PasswordLockout:
PasswordLockoutPolicy:
NotFound: Password Lockout Policy not found
Empty: Password Lockout Policy is empty
NotExisting: Password Lockout Policy doesn't exist
AlreadyExists: Password Lockout Policy already exists
PasswordAge:
PasswordAgePolicy:
NotFound: Password Age Policy not found
Empty: Password Age Policy is empty
NotExisting: Password Age Policy doesn't exist
AlreadyExists: Password Age Policy already exists
OrgIAM:
OrgIAMPolicy:
Empty: Org IAM Policy is empty
NotExisting: Org IAM Policy doesn't exist
AlreadyExists: Org IAM Policy already exists
@ -589,6 +589,10 @@ EventTypes:
cascade:
removed: Authorization removed
changed: Authorization changed
metadata:
set: User metadata set
removed: User metadata removed
removed.all: All user metadata removed
org:
added: Organization added
changed: Organization changed

View File

@ -191,22 +191,22 @@ Errors:
NotChanged: Il testo predefinito non è stato cambiato
AlreadyExists: Il testo predefinito già eistente
Invalid: Il testo predefinito non è valido
PasswordComplexity:
PasswordComplexityPolicy:
NotFound: Impostazioni di complessità della password non trovati
Empty: Mancano le impostazioni di complessità della password
NotExisting: Impostazioni di complessità della password non esistenti
AlreadyExists: Impostazioni di complessità della password sono già esistenti
PasswordLockout:
PasswordLockoutPolicy:
NotFound: Impostazioni di blocco della password non trovati
Empty: Mancano le impostazioni di blocco della password
NotExisting: Le impostazioni di blocco della password non esistenti
AlreadyExists: Le impostazioni di blocco della password sono già esistenti
PasswordAge:
PasswordAgePolicy:
NotFound: Impostazioni di validità della password
Empty: Impostazioni di validità della password mancanti
NotExisting: Impostazioni di validità della password non esistenti
AlreadyExists: Impostazioni di validità della password sono già esistenti
OrgIAM:
OrgIAMPolicy:
Empty: Mancano le impostazioni Org IAM
NotExisting: Impostazioni Org IAM non esistenti
AlreadyExists: Impostazioni Org IAM già esistenti
@ -587,6 +587,10 @@ EventTypes:
cascade:
removed: Autorizzazione rimossa
changed: Autorizzazione cambiata
metadata:
set: Set di metadati utente
removed: Metadati utente rimossi
removed.all: Tutti i metadati utente rimossi
org:
added: Organizzazione aggiunta
changed: Organizzazione cambiata

View File

@ -4,8 +4,6 @@ import (
"encoding/json"
"time"
"github.com/caos/zitadel/internal/query"
"github.com/caos/logging"
"github.com/lib/pq"
@ -100,14 +98,14 @@ func (u *NotifyUser) GenerateLoginName(domain string, appendDomain bool) string
return u.UserName + "@" + domain
}
func (u *NotifyUser) SetLoginNames(policy *query.OrgIAMPolicy, domains []*org_model.OrgDomain) {
func (u *NotifyUser) SetLoginNames(userLoginMustBeDomain bool, domains []*org_model.OrgDomain) {
loginNames := make([]string, 0)
for _, d := range domains {
if d.Verified {
loginNames = append(loginNames, u.GenerateLoginName(d.Domain, true))
}
}
if !policy.UserLoginMustBeDomain {
if !userLoginMustBeDomain {
loginNames = append(loginNames, u.UserName)
}
u.LoginNames = loginNames

View File

@ -8,8 +8,6 @@ import (
"github.com/caos/logging"
"github.com/lib/pq"
"github.com/caos/zitadel/internal/query"
req_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@ -230,14 +228,14 @@ func (u *UserView) GenerateLoginName(domain string, appendDomain bool) string {
return u.UserName + "@" + domain
}
func (u *UserView) SetLoginNames(policy *query.OrgIAMPolicy, domains []*org_model.OrgDomain) {
func (u *UserView) SetLoginNames(userLoginMustBeDomain bool, domains []*org_model.OrgDomain) {
loginNames := make([]string, 0)
for _, d := range domains {
if d.Verified {
loginNames = append(loginNames, u.GenerateLoginName(d.Domain, true))
}
}
if !policy.UserLoginMustBeDomain {
if !userLoginMustBeDomain {
loginNames = append(loginNames, u.UserName)
}
u.LoginNames = loginNames

View File

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"github.com/caos/logging"
"github.com/duo-labs/webauthn/protocol"
"github.com/duo-labs/webauthn/webauthn"
@ -107,6 +108,8 @@ func (w *WebAuthN) FinishRegistration(user *domain.Human, webAuthN *domain.WebAu
}
credentialData, err := protocol.ParseCredentialCreationResponseBody(bytes.NewReader(credData))
if err != nil {
e := *err.(*protocol.Error)
logging.WithFields("error", e).Error("webauthn credential could not be parsed")
return nil, caos_errs.ThrowInternal(err, "WEBAU-sEr8c", "Errors.User.WebAuthN.ErrorOnParseCredential")
}
sessionData := WebAuthNToSessionData(webAuthN)

View File

@ -1771,6 +1771,7 @@ service AdminService {
//Updates the default privacy policy of ZITADEL
// it impacts all organisations without a customised policy
// Variable {{.Lang}} can be set to have different links based on the language
rpc UpdatePrivacyPolicy(UpdatePrivacyPolicyRequest) returns (UpdatePrivacyPolicyResponse) {
option (google.api.http) = {
put: "/policies/privacy";
@ -3765,6 +3766,7 @@ message GetPrivacyPolicyResponse {
message UpdatePrivacyPolicyRequest {
string tos_link = 1;
string privacy_link = 2;
string help_link = 3;
}
message UpdatePrivacyPolicyResponse {

View File

@ -2133,6 +2133,7 @@ service ManagementService {
// Add a custom privacy policy for the organisation
// With this policy privacy relevant things can be configured (e.g. tos link)
// Variable {{.Lang}} can be set to have different links based on the language
rpc AddCustomPrivacyPolicy(AddCustomPrivacyPolicyRequest) returns (AddCustomPrivacyPolicyResponse) {
option (google.api.http) = {
post: "/policies/privacy"
@ -2147,6 +2148,7 @@ service ManagementService {
// Update the privacy complexity policy for the organisation
// With this policy privacy relevant things can be configured (e.g. tos link)
// Variable {{.Lang}} can be set to have different links based on the language
rpc UpdateCustomPrivacyPolicy(UpdateCustomPrivacyPolicyRequest) returns (UpdateCustomPrivacyPolicyResponse) {
option (google.api.http) = {
put: "/policies/privacy"
@ -4646,6 +4648,7 @@ message GetDefaultPrivacyPolicyResponse {
message AddCustomPrivacyPolicyRequest {
string tos_link = 1;
string privacy_link = 2;
string help_link = 3;
}
message AddCustomPrivacyPolicyResponse {
@ -4655,6 +4658,7 @@ message AddCustomPrivacyPolicyResponse {
message UpdateCustomPrivacyPolicyRequest {
string tos_link = 1;
string privacy_link = 2;
string help_link = 3;
}
message UpdateCustomPrivacyPolicyResponse {

View File

@ -229,4 +229,5 @@ message PrivacyPolicy {
string tos_link = 2;
string privacy_link = 3;
bool is_default = 4;
}
string help_link = 5;
}

View File

@ -382,12 +382,11 @@ message LogoutDoneScreenText {
}
message FooterText {
reserved 2, 4;
reserved "tos_link", "privacy_policy_link";
reserved 2, 4, 6;
reserved "tos_link", "privacy_policy_link", "help_link";
string tos = 1 [(validate.rules).string = {max_len: 200}];
string privacy_policy = 3 [(validate.rules).string = {max_len: 200}];
string help = 5 [(validate.rules).string = {max_len: 200}];
string help_link = 6 [(validate.rules).string = {max_len: 500}];
}
message PasswordlessPromptScreenText {

View File

@ -681,6 +681,11 @@ message UserGrant {
example: "\"https://api.zitadel.ch/assets/v1/avatar-32432jkh4kj32\"";
}
];
string preferred_login_name = 18 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"gigi@caos.ch\"";
}
];
}
enum UserGrantState {