mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-17 23:08:20 +00:00
Merge branch 'v2-alpha' into v2-alpha-run-e2e-tests
This commit is contained in:
commit
2cb47b7877
@ -3,7 +3,7 @@ module.exports = {
|
||||
{name: 'main'},
|
||||
{name: '1.x.x', range: '1.x.x', channel: '1.x.x'},
|
||||
{name: 'v2-alpha', prerelease: true},
|
||||
{name: 'notify-users', prerelease: true},
|
||||
{name: 'auth-users', prerelease: true},
|
||||
],
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer"
|
||||
|
@ -22,9 +22,17 @@
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
||||
],
|
||||
"allowedCommonJsDependencies": [
|
||||
"@angular/common/locales/de",
|
||||
"codemirror/mode/javascript/javascript",
|
||||
@ -122,15 +130,27 @@
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
||||
"styles": ["./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", "src/styles.scss"],
|
||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"]
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
|
11242
console/package-lock.json
generated
11242
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,18 +12,18 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.0.1",
|
||||
"@angular/cdk": "^14.0.1",
|
||||
"@angular/common": "^14.0.1",
|
||||
"@angular/compiler": "^14.0.1",
|
||||
"@angular/core": "^14.0.1",
|
||||
"@angular/forms": "^14.0.1",
|
||||
"@angular/material": "^14.0.1",
|
||||
"@angular/material-moment-adapter": "^14.0.1",
|
||||
"@angular/platform-browser": "^14.0.1",
|
||||
"@angular/platform-browser-dynamic": "^14.0.1",
|
||||
"@angular/router": "^14.0.1",
|
||||
"@angular/service-worker": "^14.0.1",
|
||||
"@angular/animations": "^14.0.4",
|
||||
"@angular/cdk": "^14.0.4",
|
||||
"@angular/common": "^14.0.4",
|
||||
"@angular/compiler": "^14.0.4",
|
||||
"@angular/core": "^14.0.4",
|
||||
"@angular/forms": "^14.0.4",
|
||||
"@angular/material": "^14.0.4",
|
||||
"@angular/material-moment-adapter": "^14.0.4",
|
||||
"@angular/platform-browser": "^14.0.4",
|
||||
"@angular/platform-browser-dynamic": "^14.0.4",
|
||||
"@angular/router": "^14.0.4",
|
||||
"@angular/service-worker": "^14.0.4",
|
||||
"@ctrl/ngx-codemirror": "^5.1.1",
|
||||
"@grpc/grpc-js": "^1.5.7",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
@ -36,7 +36,7 @@
|
||||
"codemirror": "^5.65.0",
|
||||
"cors": "^2.8.5",
|
||||
"file-saver": "^2.0.5",
|
||||
"google-proto-files": "^2.5.0",
|
||||
"google-proto-files": "^3.0.0",
|
||||
"google-protobuf": "^3.19.4",
|
||||
"grpc-web": "^1.3.0",
|
||||
"libphonenumber-js": "^1.10.6",
|
||||
@ -52,32 +52,32 @@
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.0.1",
|
||||
"@angular-eslint/builder": "^14.0.0-alpha.3",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0-alpha.3",
|
||||
"@angular-eslint/eslint-plugin-template": "^14.0.0-alpha.3",
|
||||
"@angular-eslint/schematics": "^14.0.0-alpha.3",
|
||||
"@angular-eslint/template-parser": "^14.0.0-alpha.3",
|
||||
"@angular/cli": "^14.0.1",
|
||||
"@angular/compiler-cli": "^14.0.1",
|
||||
"@angular/language-service": "^14.0.1",
|
||||
"@angular-devkit/build-angular": "^14.0.4",
|
||||
"@angular-eslint/builder": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^14.0.0",
|
||||
"@angular-eslint/schematics": "^14.0.0",
|
||||
"@angular-eslint/template-parser": "^14.0.0",
|
||||
"@angular/cli": "^14.0.4",
|
||||
"@angular/compiler-cli": "^14.0.4",
|
||||
"@angular/language-service": "^14.0.4",
|
||||
"@types/jasmine": "~4.0.3",
|
||||
"@types/jasminewd2": "~2.0.10",
|
||||
"@types/jsonwebtoken": "^8.5.5",
|
||||
"@types/node": "^17.0.42",
|
||||
"@typescript-eslint/eslint-plugin": "5.25.0",
|
||||
"@typescript-eslint/parser": "5.27.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.4",
|
||||
"@typescript-eslint/parser": "5.30.4",
|
||||
"codelyzer": "^6.0.0",
|
||||
"cypress": "^10.1.0",
|
||||
"cypress-terminal-report": "^4.0.1",
|
||||
"eslint": "^8.17.0",
|
||||
"jasmine-core": "~4.1.1",
|
||||
"eslint": "^8.18.0",
|
||||
"jasmine-core": "~4.2.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"karma": "~6.3.16",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~5.0.1",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "^2.0.0",
|
||||
"mochawesome": "^7.1.2",
|
||||
"prettier": "^2.4.1",
|
||||
|
@ -13,6 +13,10 @@ const routes: Routes = [
|
||||
loadChildren: () => import('./pages/home/home.module').then((m) => m.HomeModule),
|
||||
canActivate: [AuthGuard],
|
||||
},
|
||||
{
|
||||
path: 'signedout',
|
||||
loadChildren: () => import('./pages/signedout/signedout.module').then((m) => m.SignedoutModule),
|
||||
},
|
||||
{
|
||||
path: 'orgs',
|
||||
loadChildren: () => import('./pages/org-list/org-list.module').then((m) => m.OrgListModule),
|
||||
@ -38,13 +42,8 @@ const routes: Routes = [
|
||||
{
|
||||
path: 'users',
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () => import('src/app/pages/users/users.module').then((m) => m.UsersModule),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'instance',
|
||||
loadChildren: () => import('./pages/instance/instance.module').then((m) => m.InstanceModule),
|
||||
@ -170,10 +169,6 @@ const routes: Routes = [
|
||||
roles: ['policy.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'signedout',
|
||||
loadChildren: () => import('./pages/signedout/signedout.module').then((m) => m.SignedoutModule),
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: '/',
|
||||
|
@ -1,10 +1,9 @@
|
||||
<ng-container *ngIf="(authService.user | async) || undefined as user">
|
||||
<ng-container *ngIf="['iam.read$', 'iam.write$'] | hasRole as iamuser$">
|
||||
<div class="main-container">
|
||||
<div class="main-container">
|
||||
<ng-container *ngIf="(authService.user | async) || {} as user">
|
||||
<cnsl-header
|
||||
*ngIf="user"
|
||||
*ngIf="user && user !== {}"
|
||||
[org]="org"
|
||||
[user]="user"
|
||||
[user]="$any(user)"
|
||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||
[labelpolicy]="labelpolicy"
|
||||
(changedActiveOrg)="changedOrg($event)"
|
||||
@ -14,12 +13,14 @@
|
||||
id="mainnav"
|
||||
class="nav"
|
||||
[ngClass]="{ shadow: yoffset > 60 }"
|
||||
*ngIf="user"
|
||||
*ngIf="user && user !== {}"
|
||||
[org]="org"
|
||||
[user]="user"
|
||||
[user]="$any(user)"
|
||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||
[labelpolicy]="labelpolicy"
|
||||
></cnsl-nav>
|
||||
</ng-container>
|
||||
|
||||
<div class="router-container" [@routeAnimations]="prepareRoute(outlet)">
|
||||
<div class="outlet">
|
||||
<router-outlet class="outlet" #outlet="outlet"></router-outlet>
|
||||
@ -27,6 +28,4 @@
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<cnsl-footer [privateLabelPolicy]="labelpolicy"></cnsl-footer>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
@ -69,6 +69,7 @@ export class AppComponent implements OnDestroy {
|
||||
private activatedRoute: ActivatedRoute,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {
|
||||
this.themeService.loadPrivateLabelling(true);
|
||||
console.log(
|
||||
'%cWait!',
|
||||
'text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; color: #5469D4; font-size: 50px',
|
||||
|
@ -26,7 +26,6 @@ import { HeaderModule } from './modules/header/header.module';
|
||||
import { KeyboardShortcutsModule } from './modules/keyboard-shortcuts/keyboard-shortcuts.module';
|
||||
import { NavModule } from './modules/nav/nav.module';
|
||||
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.module';
|
||||
import { SignedoutComponent } from './pages/signedout/signedout.component';
|
||||
import { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { AdminService } from './services/admin.service';
|
||||
import { AuthenticationService } from './services/authentication.service';
|
||||
@ -79,7 +78,7 @@ const authConfig: AuthConfig = {
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, SignedoutComponent],
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
AppRoutingModule,
|
||||
CommonModule,
|
||||
|
@ -4,13 +4,12 @@ import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { AuthenticationService } from '../services/authentication.service';
|
||||
import { GrpcAuthService } from '../services/grpc-auth.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(private auth: AuthenticationService, private authService: GrpcAuthService) {}
|
||||
constructor(private auth: AuthenticationService) {}
|
||||
|
||||
public canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
|
@ -196,6 +196,9 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ng-container
|
||||
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||
>
|
||||
<div class="account-card-wrapper">
|
||||
<button
|
||||
cdkOverlayOrigin
|
||||
@ -206,9 +209,6 @@
|
||||
>
|
||||
<cnsl-avatar
|
||||
id="avatartoggle"
|
||||
*ngIf="
|
||||
user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))
|
||||
"
|
||||
class="avatar-toggle dontcloseonclick"
|
||||
[active]="showAccount"
|
||||
[avatarUrl]="user.human?.profile?.avatarUrl || ''"
|
||||
@ -246,5 +246,6 @@
|
||||
>
|
||||
</cnsl-accounts-card>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
@ -5,7 +5,25 @@
|
||||
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE"
|
||||
[hasContributors]="true"
|
||||
stateTooltip="{{ 'ORG.STATE.' + org?.state | translate }}"
|
||||
[hasActions]="['org.write:' + org?.id, 'org.write$'] | hasRole | async"
|
||||
>
|
||||
<ng-template topActions cnslHasRole [hasRole]="['org.write:' + org?.id, 'org.write$']">
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="org?.state === OrgState.ORG_STATE_ACTIVE"
|
||||
(click)="changeState(OrgState.ORG_STATE_INACTIVE)"
|
||||
>
|
||||
{{ 'ORG.PAGES.DEACTIVATE' | translate }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="org?.state === OrgState.ORG_STATE_INACTIVE"
|
||||
(click)="changeState(OrgState.ORG_STATE_ACTIVE)"
|
||||
>
|
||||
{{ 'ORG.PAGES.REACTIVATE' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
<cnsl-contributors
|
||||
topContributors
|
||||
[totalResult]="totalMemberResult"
|
||||
|
@ -7,6 +7,7 @@ import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-m
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
@ -62,6 +63,56 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
public changeState(newState: OrgState): void {
|
||||
if (newState === OrgState.ORG_STATE_ACTIVE) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.REACTIVATE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'ORG.DIALOG.REACTIVATE.TITLE',
|
||||
descriptionKey: 'ORG.DIALOG.REACTIVATE.DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
this.mgmtService
|
||||
.reactivateOrg()
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.REACTIVATED', true);
|
||||
this.org.state = OrgState.ORG_STATE_ACTIVE;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (newState === OrgState.ORG_STATE_INACTIVE) {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DEACTIVATE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'ORG.DIALOG.DEACTIVATE.TITLE',
|
||||
descriptionKey: 'ORG.DIALOG.DEACTIVATE.DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
this.mgmtService
|
||||
.deactivateOrg()
|
||||
.then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.DEACTIVATED', true);
|
||||
this.org.state = OrgState.ORG_STATE_INACTIVE;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async getData(): Promise<void> {
|
||||
this.mgmtService
|
||||
.getMyOrg()
|
||||
|
@ -14,4 +14,4 @@ const routes: Routes = [
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class SignedoutRoutingModule { }
|
||||
export class SignedoutRoutingModule {}
|
||||
|
@ -5,11 +5,18 @@
|
||||
<ng-template #lighttheme>
|
||||
<img alt="zitadel logo" src="../../../assets/images/zitadel-logo-dark.svg" />
|
||||
</ng-template>
|
||||
<p class="cnsl-secondary-text">{{'USER.SIGNEDOUT' | translate}}</p>
|
||||
<p class="cnsl-secondary-text">{{ 'USER.SIGNEDOUT' | translate }}</p>
|
||||
|
||||
<button matTooltip="{{'ACTIONS.LOGIN' | translate}}" color="primary" mat-raised-button
|
||||
[routerLink]="[ '/users/me' ]">{{'USER.SIGNEDOUT_BTN' |
|
||||
translate}} <i class="las la-sign-in-alt"></i></button>
|
||||
<button
|
||||
class="cnsl-action-button"
|
||||
matTooltip="{{ 'ACTIONS.LOGIN' | translate }}"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
[routerLink]="['/users/me']"
|
||||
>
|
||||
<i class="las la-sign-in-alt"></i>
|
||||
<span>{{ 'USER.SIGNEDOUT_BTN' | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -9,6 +9,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 50px;
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
@ -24,16 +25,6 @@
|
||||
img {
|
||||
height: 100px;
|
||||
max-width: 170px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
padding: 0.5rem 4rem;
|
||||
|
||||
i {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ThemeService } from 'src/app/services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-signedout',
|
||||
@ -8,7 +9,9 @@ import { Component } from '@angular/core';
|
||||
export class SignedoutComponent {
|
||||
public dark: boolean = true;
|
||||
|
||||
constructor() {
|
||||
constructor(themeService: ThemeService) {
|
||||
themeService.loadPrivateLabelling();
|
||||
|
||||
const theme = localStorage.getItem('theme');
|
||||
this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true;
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
|
||||
import { SignedoutRoutingModule } from './signedout-routing.module';
|
||||
import { SignedoutComponent } from './signedout.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SignedoutRoutingModule,
|
||||
SharedModule,
|
||||
],
|
||||
declarations: [SignedoutComponent],
|
||||
imports: [CommonModule, SignedoutRoutingModule, MatButtonModule, MatTooltipModule, TranslateModule, SharedModule],
|
||||
})
|
||||
export class SignedoutModule { }
|
||||
export class SignedoutModule {}
|
||||
|
@ -49,10 +49,10 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
||||
switchMap((url: string) => {
|
||||
if (url.includes('?login_hint=')) {
|
||||
const newUrl = this.removeParam('login_hint', url);
|
||||
const urlWithoutBasePath = newUrl.startsWith('/ui/console') ? newUrl.replace('/ui/console', '') : newUrl;
|
||||
const urlWithoutBasePath = newUrl.includes('/ui/console') ? newUrl.replace('/ui/console', '') : newUrl;
|
||||
return of(this.processor.createState(urlWithoutBasePath));
|
||||
} else if (url) {
|
||||
const urlWithoutBasePath = url.startsWith('/ui/console') ? url.replace('/ui/console', '') : url;
|
||||
const urlWithoutBasePath = url.includes('/ui/console') ? url.replace('/ui/console', '') : url;
|
||||
return of(this.processor.createState(urlWithoutBasePath));
|
||||
} else {
|
||||
return of(undefined);
|
||||
|
@ -140,9 +140,10 @@ export class ThemeService {
|
||||
this.saveTextColor(lightText, false);
|
||||
};
|
||||
|
||||
public loadPrivateLabelling(): void {
|
||||
public loadPrivateLabelling(forceDefault: boolean = false): void {
|
||||
if (forceDefault) {
|
||||
this.setDefaultColors();
|
||||
|
||||
} else {
|
||||
const isDark = (color: string) => this.isDark(color);
|
||||
const isLight = (color: string) => this.isLight(color);
|
||||
|
||||
@ -206,4 +207,5 @@ export class ThemeService {
|
||||
this.setDefaultColors();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -757,6 +757,8 @@
|
||||
"LISTDESCRIPTION": "Wähle eine Organisation aus.",
|
||||
"ACTIVE": "Aktiv",
|
||||
"CREATE": "Organisation erstellen",
|
||||
"DEACTIVATE": "Organisation deaktivieren",
|
||||
"REACTIVATE": "Organisation reaktivieren",
|
||||
"NOPERMISSION": "Sie haben keine Berechtigung, auf Einstellungen der Organisation zuzugreifen.",
|
||||
"USERSELFACCOUNT": "Verwenden Sie Ihr persönliches Konto als Organisationsinhaber",
|
||||
"ORGDETAIL_TITLE": "Gebe den Namen und die Domain für die neue Organisation ein.",
|
||||
@ -821,6 +823,16 @@
|
||||
"MEMBERREMOVED": "Manager entfernt.",
|
||||
"MEMBERCHANGED": "Manager geändert.",
|
||||
"SETPRIMARY": "Primäre Domain gesetzt."
|
||||
},
|
||||
"DIALOG": {
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Organisation deaktivieren",
|
||||
"DESCRIPTION": "Sie sind im Begriff Ihre Organisation zu deaktivieren. User können Sich danach nicht mehr anmelden? Wollen Sie fortfahren?"
|
||||
},
|
||||
"REACTIVATE": {
|
||||
"TITLE": "Organisation reaktivieren",
|
||||
"DESCRIPTION": "Sie sind im Begriff Ihre Organisation zu reaktivieren. User können Sich danach wieder anmelden? Wollen Sie fortfahren?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SETTINGS": {
|
||||
|
@ -757,6 +757,8 @@
|
||||
"LISTDESCRIPTION": "Choose an organization.",
|
||||
"ACTIVE": "Active",
|
||||
"CREATE": "Create Organization",
|
||||
"DEACTIVATE": "Deactivate Organization",
|
||||
"REACTIVATE": "Reactivate Organization",
|
||||
"NOPERMISSION": "You don't have the permission to access organization settings.",
|
||||
"USERSELFACCOUNT": "Use your personal account as organization owner",
|
||||
"ORGDETAIL_TITLE": "Enter the name and domain of your new organization.",
|
||||
@ -821,6 +823,16 @@
|
||||
"MEMBERREMOVED": "Manager removed.",
|
||||
"MEMBERCHANGED": "Manager changed.",
|
||||
"SETPRIMARY": "Primary domain set."
|
||||
},
|
||||
"DIALOG": {
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Deactivate organization",
|
||||
"DESCRIPTION": "You are about to deactivate your organization. Users won't be able to login afterwards. Are you sure to proceed?"
|
||||
},
|
||||
"REACTIVATE": {
|
||||
"TITLE": "Reactivate organization",
|
||||
"DESCRIPTION": "You are about to reactivate your organization. Users will be able to login again. Are you sure to proceed?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SETTINGS": {
|
||||
|
@ -757,6 +757,8 @@
|
||||
"LISTDESCRIPTION": "Choisissez une organisation.",
|
||||
"ACTIVE": "Actif",
|
||||
"CREATE": "Créer une organisation",
|
||||
"DEACTIVATE": "Désactiver l'organisation",
|
||||
"REACTIVATE": "Réactiver l'organisation",
|
||||
"NOPERMISSION": "Vous n'avez pas la permission d'accéder aux paramètres de l'organisation.",
|
||||
"USERSELFACCOUNT": "Utilisez votre compte personnel comme propriétaire de l'organisation",
|
||||
"ORGDETAIL_TITLE": "Saisissez le nom et le domaine de votre nouvelle organisation.",
|
||||
@ -821,6 +823,16 @@
|
||||
"MEMBERREMOVED": "Gestionnaire supprimé.",
|
||||
"MEMBERCHANGED": "Gestionnaire modifié.",
|
||||
"SETPRIMARY": "Domaine primaire défini."
|
||||
},
|
||||
"DIALOG": {
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Désactiver l'organisation",
|
||||
"DESCRIPTION": "Vous êtes sur le point de désactiver votre organisation. Les utilisateurs ne peuvent plus se connecter ? Voulez-vous continuer ?"
|
||||
},
|
||||
"REACTIVATE": {
|
||||
"TITLE": "Réactiver l'organisation",
|
||||
"DESCRIPTION": "Vous êtes sur le point de réactiver votre organisation. Les utilisateurs peuvent ensuite se reconnecter ? Voulez-vous continuer ?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SETTINGS": {
|
||||
|
@ -757,6 +757,8 @@
|
||||
"LISTDESCRIPTION": "Scegli un'organizzazione.",
|
||||
"ACTIVE": "Attivo",
|
||||
"CREATE": "Creare un'organizzazione",
|
||||
"DEACTIVATE": "Disattiva organizzazione",
|
||||
"REACTIVATE": "Riattiva organizzazione",
|
||||
"NOPERMISSION": "Non hai l'autorizzazione per accedere alle impostazioni dell'organizzazione.",
|
||||
"USERSELFACCOUNT": "Usa il tuo account personale come proprietario dell'organizzazione",
|
||||
"ORGDETAIL_TITLE": "Inserisci il nome e il dominio della tua nuova organizzazione.",
|
||||
@ -821,6 +823,16 @@
|
||||
"MEMBERREMOVED": "Manager rimosso con successo",
|
||||
"MEMBERCHANGED": "Manager cambiato con successo",
|
||||
"SETPRIMARY": "Dominio primario cambiato con successo"
|
||||
},
|
||||
"DIALOG": {
|
||||
"DEACTIVATE": {
|
||||
"TITLE": "Disattivare l'organizzazione",
|
||||
"DESCRIPTION": "Stai per disattivate la tua organizzazione. Utenti dell' organizzazione non possono più accedere in seguito. Sei sicuro di procedere?"
|
||||
},
|
||||
"REACTIVATE": {
|
||||
"TITLE": "Riattivare l'organizzazione",
|
||||
"DESCRIPTION": "Stai per riattivare la tua organizzazione. Utenti dell' organizzazione possono accedere nuovamente dopo l'attivazione. Vuoi procedere?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SETTINGS": {
|
||||
|
@ -1,34 +1,31 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" />
|
||||
<title>Console</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
|
||||
<link rel="stylesheet" href="./assets/icons/line-awesome/css/line-awesome.min.css" />
|
||||
<link rel="manifest" href="manifest.webmanifest">
|
||||
<meta name="theme-color" content="#e6768b">
|
||||
<link rel="manifest" href="manifest.webmanifest" />
|
||||
<meta name="theme-color" content="#e6768b" />
|
||||
|
||||
<meta property="description" content="Console Management Platform for ZITADEL IAM" />
|
||||
<meta property="og:url" content="https://console.zitadel.ch" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="ZITADEL Console" />
|
||||
<meta property="og:description" content="Console Management Platform for ZITADEL IAM" />
|
||||
<meta property="description" content="Management Platform for ZITADEL IAM" />
|
||||
<meta property="og:image" content="https://www.zitadel.ch/zitadel-social-preview25.png" />
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@zitadel_ch">
|
||||
<meta property="og:image" content="https://www.zitadel.com/images/preview.png" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content="@zitadel" />
|
||||
<meta name="twitter:title" content="ZITADEL Console" />
|
||||
<meta name="twitter:description" content="Management Platform for ZITADEL IAM" />
|
||||
<meta name="twitter:image" content="https://www.zitadel.ch/zitadel-social-preview25.png">
|
||||
<meta name="twitter:image" content="https://www.zitadel.com/images/preview.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<cnsl-root></cnsl-root>
|
||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||
</body>
|
||||
|
||||
</html>
|
4
docs/docs/guides/installation/run/defaultuser.mdx
Normal file
4
docs/docs/guides/installation/run/defaultuser.mdx
Normal file
@ -0,0 +1,4 @@
|
||||
Open your favorite internet browser and navigate to [http://localhost:8080/ui/console](http://localhost:8080/ui/console).
|
||||
This is the default IAM admin users login:
|
||||
- **username**: *zitadel-admin@<span></span>zitadel.localhost*
|
||||
- **password**: *Password1!*
|
@ -1,10 +1,7 @@
|
||||
## Disclaimer
|
||||
This guide is for development / demonstration purpose only and does NOT reflect a production setup.
|
||||
|
||||
## New Knative environment
|
||||
### Download and run Knative quickstart
|
||||
|
||||
Follow the Knative quickstart guide to get a local kind/minikube environment with Knative capabilities.
|
||||
Follow the [Knative quickstart guide](https://knative.dev/docs/getting-started/quickstart-install/) to get a local kind/minikube environment with Knative capabilities.
|
||||
|
||||
It is basically 4 commands on Mac:
|
||||
|
||||
@ -24,10 +21,6 @@ kn quickstart kind
|
||||
That will get you a ready to go knative/kubernetes environment.
|
||||
|
||||
|
||||
See Knative documentation here:
|
||||
https://knative.dev/docs/install/quickstart-install/
|
||||
|
||||
|
||||
## Database
|
||||
start a single-node cockroachdb as statefulset
|
||||
|
||||
@ -35,14 +28,6 @@ start a single-node cockroachdb as statefulset
|
||||
kubectl apply -f https://raw.githubusercontent.com/zitadel/zitadel/v2-alpha/deploy/knative/cockroachdb-statefulset-single-node.yaml
|
||||
```
|
||||
|
||||
## Secret for TLS termination
|
||||
create a secret with your certificates for TLS termination
|
||||
|
||||
```bash
|
||||
#describe happy path
|
||||
kubectl apply secret -f certs.yaml
|
||||
```
|
||||
|
||||
|
||||
## Start ZITADEL with Knative
|
||||
```bash
|
||||
@ -58,9 +43,7 @@ kn service create zitadel \
|
||||
--env ZITADEL_TLS_ENABLED=false \
|
||||
--env ZITADEL_EXTERNALDOMAIN=zitadel.default.127.0.0.1.sslip.io \
|
||||
--env ZITADEL_S3DEFAULTINSTANCE_CUSTOMDOMAIN=zitadel.default.127.0.0.1.sslip.io \
|
||||
--arg "admin" --arg "start-from-init" --arg "--masterkey" --arg "MasterkeyNeedsToHave32Characters" \
|
||||
--mount /tls.secret=secret:certs/tls.secret \
|
||||
--mount /tls.key=secret:certs/tls.key
|
||||
--arg "start-from-init" --arg "--masterkey" --arg "MasterkeyNeedsToHave32Characters"
|
||||
```
|
||||
|
||||
or use the knative service yaml
|
||||
@ -83,6 +66,6 @@ http://zitadel.default.127.0.0.1.sslip.io/ui/console
|
||||
|
||||
If you didn't configure something else, this is the default IAM admin users login:
|
||||
|
||||
* username: zitadel-admin@zitadel.zitadel.default.127.0.0.1.sslip.io
|
||||
* username: zitadel-admin@<span></span>zitadel.zitadel.default.127.0.0.1.sslip.io
|
||||
* password: Password1!
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
Open your favorite internet browser and navigate to [http://localhost:8080/ui/console](http://localhost:8080/ui/console).
|
||||
This is the default IAM admin users login:
|
||||
- **username**: *zitadel-admin@<span></span>zitadel.localhost*
|
||||
- **password**: *Password1!*
|
||||
|
||||
## What's next
|
||||
|
||||
|
@ -11,6 +11,7 @@ import Compose from './compose.mdx'
|
||||
import Helm from './helm.mdx'
|
||||
import Knative from './knative.mdx'
|
||||
import NextSelfHosted from './nextselfhosted.mdx'
|
||||
import DefaultUser from './defaultuser.mdx'
|
||||
|
||||
# Run ZITADEL
|
||||
|
||||
@ -18,6 +19,16 @@ Choose your platform and run ZITADEL with the most minimal configuration possibl
|
||||
For an easy self-hosted production setup, we recommend running ZITADEL on [Kubernetes](https://kubernetes.io/docs/home/), using our official [Helm](https://helm.sh/docs/) chart.
|
||||
By default, it runs a highly available ZITADEL instance along with a secure and highly available [CockroachDB](https://www.cockroachlabs.com/docs/stable/) instance.
|
||||
|
||||
## Disclaimer
|
||||
This guide is for development / demonstration purpose only and does NOT reflect a production setup.
|
||||
|
||||
Things such as TLS termination and email verification will not be available unless you
|
||||
|
||||
- use an API gateway with valid certificates in front of the service
|
||||
- configure an appropriate email server
|
||||
|
||||
see loadbalancing example [here](/docs/guides/installation/loadbalancing-example)
|
||||
|
||||
<!-- TODO: Destroy -->
|
||||
|
||||
<Tabs
|
||||
@ -37,21 +48,26 @@ By default, it runs a highly available ZITADEL instance along with a secure and
|
||||
</TabItem>
|
||||
<TabItem value="linux">
|
||||
<Linux/>
|
||||
<DefaultUser/>
|
||||
<NextSelfHosted/>
|
||||
</TabItem>
|
||||
<TabItem value="macos">
|
||||
<MacOS/>
|
||||
<DefaultUser/>
|
||||
<NextSelfHosted/>
|
||||
</TabItem>
|
||||
<TabItem value="compose">
|
||||
<Compose/>
|
||||
<DefaultUser/>
|
||||
<NextSelfHosted/>
|
||||
</TabItem>
|
||||
<TabItem value="k8s">
|
||||
<Helm/>
|
||||
<DefaultUser/>
|
||||
<NextSelfHosted/>
|
||||
</TabItem>
|
||||
<TabItem value="knative">
|
||||
<Knative/>
|
||||
<NextSelfHosted/>
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
@ -12,11 +12,11 @@ At the end of the guide, your application has login functionality and has access
|
||||
## Setup Application and Get Keys
|
||||
|
||||
Before we can start building our application, we have to do a few configuration steps in ZITADEL Console.
|
||||
You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your [Project](https://console.zitadel.ch/projects), then add a new application at the top of the page.
|
||||
You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project, then add a new application at the top of the page.
|
||||
Select Web application type and continue.
|
||||
We recommend you use [Authorization Code](../../apis/openidoauth/grant-types#authorization-code) in combination with [Proof Key for Code Exchange (PKCE)](../../apis/openidoauth/grant-types#proof-key-for-code-exchange) for all web applications.
|
||||
|
||||

|
||||

|
||||
|
||||
### Redirect URIs
|
||||
|
||||
@ -28,7 +28,7 @@ If you want to redirect the users back to a route on your application after they
|
||||
|
||||
Continue and create the application.
|
||||
|
||||
### Client ID and Secret
|
||||
### Client ID
|
||||
|
||||
After successful app creation, a pop-up will appear, showing the app's client ID. Copy the client ID, as you will need it to configure your Angular client.
|
||||
|
||||
@ -58,7 +58,7 @@ const authConfig: AuthConfig = {
|
||||
responseType: 'code',
|
||||
oidc: true,
|
||||
clientId: 'YOUR-CLIENT-ID', // replace with your appid
|
||||
issuer: 'https://issuer.zitadel.ch',
|
||||
issuer: 'https:/[your-domain]-[random-string].zitadel.cloud', // replace with your instance
|
||||
redirectUri: 'http://localhost:4200/auth/callback',
|
||||
postLogoutRedirectUri: 'http://localhost:4200/signedout', // optional
|
||||
requireHttps: false // required for running locally
|
||||
@ -89,26 +89,25 @@ ng g service services/authentication
|
||||
Copy the following code to your service. This code provides a function `authenticate()` which redirects the user to ZITADEL. After successful login, ZITADEL redirects the user back to the redirect URI configured in _AuthModule_ and ZITADEL Console. Make sure both correspond, otherwise ZITADEL throws an error.
|
||||
|
||||
```ts
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { AuthConfig, OAuthService } from "angular-oauth2-oidc";
|
||||
import { BehaviorSubject, from, Observable } from "rxjs";
|
||||
|
||||
import { StatehandlerService } from './statehandler.service';
|
||||
import { StatehandlerService } from "./statehandler.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: "root",
|
||||
})
|
||||
export class AuthenticationService {
|
||||
private _authenticated: boolean = false;
|
||||
private readonly _authenticationChanged: BehaviorSubject<
|
||||
boolean
|
||||
> = new BehaviorSubject(this.authenticated);
|
||||
private readonly _authenticationChanged: BehaviorSubject<boolean> =
|
||||
new BehaviorSubject(this.authenticated);
|
||||
|
||||
constructor(
|
||||
private oauthService: OAuthService,
|
||||
private authConfig: AuthConfig,
|
||||
private statehandler: StatehandlerService,
|
||||
) { }
|
||||
private statehandler: StatehandlerService
|
||||
) {}
|
||||
|
||||
public get authenticated(): boolean {
|
||||
return this._authenticated;
|
||||
@ -122,9 +121,7 @@ export class AuthenticationService {
|
||||
return from(this.oauthService.loadUserProfile());
|
||||
}
|
||||
|
||||
public async authenticate(
|
||||
setState: boolean = true,
|
||||
): Promise<boolean> {
|
||||
public async authenticate(setState: boolean = true): Promise<boolean> {
|
||||
this.oauthService.configure(this.authConfig);
|
||||
|
||||
this.oauthService.strictDiscoveryDocumentValidation = false;
|
||||
@ -133,7 +130,9 @@ export class AuthenticationService {
|
||||
this._authenticated = this.oauthService.hasValidAccessToken();
|
||||
|
||||
if (!this.oauthService.hasValidIdToken() || !this.authenticated) {
|
||||
const newState = setState ? await this.statehandler.createState().toPromise() : undefined;
|
||||
const newState = setState
|
||||
? await this.statehandler.createState().toPromise()
|
||||
: undefined;
|
||||
this.oauthService.initCodeFlow(newState);
|
||||
}
|
||||
this._authenticationChanged.next(this.authenticated);
|
||||
@ -215,21 +214,30 @@ ng g guard guards/auth
|
||||
This code shows the _AuthGuard_ used in ZITADEL Console.
|
||||
|
||||
```ts
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthenticationService } from '../services/authentication.service';
|
||||
import { Injectable } from "@angular/core";
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
RouterStateSnapshot,
|
||||
UrlTree,
|
||||
} from "@angular/router";
|
||||
import { Observable } from "rxjs";
|
||||
import { AuthenticationService } from "../services/authentication.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: "root",
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
|
||||
constructor(private auth: AuthenticationService) { }
|
||||
constructor(private auth: AuthenticationService) {}
|
||||
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
state: RouterStateSnapshot
|
||||
):
|
||||
| Observable<boolean | UrlTree>
|
||||
| Promise<boolean | UrlTree>
|
||||
| boolean
|
||||
| UrlTree {
|
||||
if (!this.auth.authenticated) {
|
||||
return this.auth.authenticate();
|
||||
}
|
||||
@ -267,10 +275,10 @@ const routes: Routes = [
|
||||
Call `auth.signout()` for logging the current user out. Note that you can also configure a logout redirect URI if you want your users to be redirected after logout.
|
||||
|
||||
```ts
|
||||
import { AuthenticationService } from 'src/app/services/authentication.service';
|
||||
import { AuthenticationService } from "src/app/services/authentication.service";
|
||||
|
||||
export class SomeComponentWithLogout {
|
||||
constructor(private authService: AuthenticationService){}
|
||||
constructor(private authService: AuthenticationService) {}
|
||||
|
||||
public signout(): Promise<void> {
|
||||
return this.authService.signout();
|
||||
|
@ -45,14 +45,14 @@ You may want to change the Flutter SDK version in `pubspec.yaml` from
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
sdk: '>=2.7.0 <3.0.0'
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
```
|
||||
|
||||
With this, you'll enable "nullable by default" mode in Flutter, as well as new language features.
|
||||
@ -257,7 +257,7 @@ Future<void> _authenticate() async {
|
||||
AuthorizationTokenRequest(
|
||||
'<<CLIENT_ID>>', // Client ID of the native application
|
||||
'<<CALLBACK_URL>>', // The registered url from zitadel (e.g. ch.myexample.app://signin)
|
||||
issuer: '<<ISSUER>>', // most of the cases: https://issuer.zitadel.ch
|
||||
issuer: '<<ISSUER>>', // most of the cases: https:/[your-domain]-[random-string].zitadel.cloud
|
||||
scopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
@ -268,7 +268,7 @@ Future<void> _authenticate() async {
|
||||
);
|
||||
|
||||
final userInfoResponse = await get(
|
||||
Uri.parse('https://api.zitadel.ch/oauth/v2/userinfo'),
|
||||
Uri.parse('https://[your-instance].zitadel.cloud/oauth/v2/userinfo'),
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
||||
HttpHeaders.acceptHeader: 'application/json; charset=UTF-8'
|
||||
@ -333,7 +333,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
);
|
||||
|
||||
final userInfoResponse = await get(
|
||||
Uri.parse('https://api.zitadel.ch/oauth/v2/userinfo'),
|
||||
Uri.parse('https:/[your-domain]-[random-string].zitadel.cloud/oauth/v2/userinfo'), // replace with your instance
|
||||
headers: {
|
||||
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
||||
HttpHeaders.acceptHeader: 'application/json; charset=UTF-8'
|
||||
@ -407,10 +407,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
|
||||
If you run this application, you can authenticate with a valid ZITADEL user.
|
||||
|
||||
<div style={{display:'flex', 'justify-content': 'center'}}>
|
||||
<div style={{display:'flex', 'align-items': 'center'}}>
|
||||
<div style={{display: 'grid', 'grid-column-gap': '1rem', 'grid-template-columns': '1fr 1fr'}}>
|
||||
<img src="/img/flutter/not-authed.png" alt="Unauthenticated" height="500px" />
|
||||
<span style={{padding:'1rem'}}>becomes</span>
|
||||
<img src="/img/flutter/authed.png" alt="Flutter Authenticated" height="500px" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,47 +40,47 @@ NextAuth.js exposes a REST API which is used by your client.
|
||||
To setup your configuration, create a file called [...nextauth].tsx in `pages/api/auth`.
|
||||
|
||||
```ts
|
||||
import NextAuth from 'next-auth';
|
||||
import NextAuth from "next-auth";
|
||||
|
||||
export const ZITADEL = {
|
||||
id: "zitadel",
|
||||
name: "zitadel",
|
||||
type: "oauth",
|
||||
version: "2.0",
|
||||
scope: "openid profile email",
|
||||
params: { response_type: "code", grant_type: "authorization_code" },
|
||||
authorizationParams: { grant_type: "authorization_code", response_type: "code" },
|
||||
accessTokenUrl: "https://api.zitadel.dev/oauth/v2/token",
|
||||
requestTokenUrl: "https://api.zitadel.dev/oauth/v2/token",
|
||||
authorizationUrl: "https://accounts.zitadel.dev/oauth/v2/authorize",
|
||||
profileUrl: "https://api.zitadel.dev/oauth/v2/userinfo",
|
||||
protection: "pkce",
|
||||
async profile(profile, tokens) {
|
||||
console.log(profile, tokens);
|
||||
version: "2",
|
||||
wellKnown: process.env.ZITADEL_ISSUER,
|
||||
authorization: {
|
||||
params: {
|
||||
scope: "openid email profile",
|
||||
},
|
||||
},
|
||||
idToken: true,
|
||||
checks: ["pkce", "state"],
|
||||
client: {
|
||||
token_endpoint_auth_method: "none",
|
||||
},
|
||||
async profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name,
|
||||
firstName: profile.given_name,
|
||||
lastName: profile.family_name,
|
||||
email: profile.email,
|
||||
image: profile.picture
|
||||
loginName: profile.preferred_username,
|
||||
image: profile.picture,
|
||||
};
|
||||
},
|
||||
clientId: process.env.ZITADEL_CLIENT_ID,
|
||||
session: {
|
||||
jwt: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default NextAuth({
|
||||
providers: [
|
||||
ZITADEL
|
||||
],
|
||||
providers: [ZITADEL],
|
||||
});
|
||||
```
|
||||
|
||||
Replace the endpoints `https://api.zitadel.dev/` with `https://api.zitadel.ch/` if your using a ZITADEL CLOUD tier or your own endpoint if your using a self hosted ENTERPRISE tier respectively.
|
||||
Replace the endpoints `https:/[your-domain]-[random-string].zitadel.cloud` with your instance or if your using a ZITADEL CLOUD tier or your own endpoint if your using a self hosted ENTERPRISE tier respectively.
|
||||
|
||||
We recommend using the Authentication Code flow secured by PKCE for the Authentication flow.
|
||||
To be able to connect to ZITADEL, navigate to your [Console Projects](https://console.zitadel.ch/projects) create or select an existing project and add your app selecting WEB, then PKCE, and then add `http://localhost:3000/api/auth/callback/zitadel` as redirect url to your app.
|
||||
To be able to connect to ZITADEL, navigate to your Console Projects, create or select an existing project and add your app selecting WEB, then PKCE, and then add `http://localhost:3000/api/auth/callback/zitadel` as redirect url to your app.
|
||||
|
||||
For simplicity reasons we set the default to the one that next-auth provides us. You'll be able to change the redirect later if you want to.
|
||||
|
||||
@ -95,6 +95,7 @@ Create a file `.env` in the root of the project and add the following keys to it
|
||||
```
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
ZITADEL_CLIENT_ID=[yourClientId]
|
||||
ZITADEL_ISSUER=https:/[your-domain]-[random-string].zitadel.cloud
|
||||
```
|
||||
|
||||
# User interface
|
||||
@ -107,7 +108,7 @@ Note that signIn method requires the id of the provider we provided earlier, and
|
||||
import { signIn, signOut, useSession } from 'next-auth/client';
|
||||
|
||||
export default function Page() {
|
||||
const [session, loading] = useSession();
|
||||
const { data: session } = useSession();
|
||||
...
|
||||
{!session && <>
|
||||
Not signed in <br />
|
||||
@ -129,13 +130,14 @@ To allow session state to be shared between pages - which improves performance,
|
||||
Take a loot at the template `_app.tsx`.
|
||||
|
||||
```ts
|
||||
import { Provider } from 'next-auth/client';
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return <Provider
|
||||
session={pageProps.session} >
|
||||
return (
|
||||
<SessionProvider session={pageProps.session}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>;
|
||||
</SessionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
||||
@ -143,17 +145,19 @@ export default MyApp;
|
||||
|
||||
Last thing: create a `profile.tsx` in /pages which renders the callback page.
|
||||
|
||||
## Learn More
|
||||
```ts
|
||||
import Link from "next/link";
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
import styles from "../styles/Home.module.css";
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
export default function Profile() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1>Login successful</h1>
|
||||
<Link href="/">
|
||||
<button>Back to Home</button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
@ -10,7 +10,7 @@ At the end of the guide you should have an application able to login a user and
|
||||
## Setup Application and get Keys
|
||||
|
||||
Before we can start building our application we have to do a few configuration steps in ZITADEL Console.
|
||||
You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your [Project](https://console.zitadel.ch/projects) and add a new application at the top of the page.
|
||||
You will need to provide some information about your app. We recommend creating a new app to start from scratch. Navigate to your Project and add a new application at the top of the page.
|
||||
Select User Agent and continue. More about the different app types can you find [here](https://docs.zitadel.com/docs/guides/authorization/oauth-recommended-flows#different-client-profiles).
|
||||
We recommend that you use [Authorization Code](../../apis/openidoauth/grant-types#authorization-code) in combination with [Proof Key for Code Exchange](../../apis/openidoauth/grant-types#proof-key-for-code-exchange) for all web applications.
|
||||
|
||||
@ -54,26 +54,28 @@ This library helps integrating ZITADEL Authentication in your React Application.
|
||||
With the installed oidc pakage you will need an AuthProvider which should contain the OIDC configuration.
|
||||
|
||||
The oidc configuration should contain **openid**, **profile** and **email** as scope and **code** as responseType.
|
||||
In the code below the authority is already set to the issuer of zitadel.ch you can find this in the ZITADEL Console on you application.
|
||||
In the code below make sure to change the issuer to your instance url. You can find this in the ZITADEL Console on you application.
|
||||
Replace the clientId value 'YOUR-CLIENT-ID' with the generated client id of you application in ZITADEL Console.
|
||||
|
||||
|
||||
```ts
|
||||
|
||||
import React from 'react';
|
||||
import { AuthProvider } from 'oidc-react';
|
||||
import './App.css';
|
||||
import React from "react";
|
||||
import { AuthProvider } from "oidc-react";
|
||||
import "./App.css";
|
||||
const oidcConfig = {
|
||||
onSignIn: async (response: any) => {
|
||||
alert('You logged in :' + response.profile.given_name + ' ' + response.profile.family_name);
|
||||
window.location.hash = '';
|
||||
alert(
|
||||
"You logged in :" +
|
||||
response.profile.given_name +
|
||||
" " +
|
||||
response.profile.family_name
|
||||
);
|
||||
window.location.hash = "";
|
||||
},
|
||||
authority: 'https://issuer.zitadel.ch',
|
||||
clientId:
|
||||
'YOUR-CLIENT-ID',
|
||||
responseType: 'code',
|
||||
redirectUri: 'http://localhost:3000/',
|
||||
scope: 'openid profile email'
|
||||
authority: "https:/[your-domain]-[random-string].zitadel.cloud", // replace with your instance
|
||||
clientId: "YOUR-CLIENT-ID",
|
||||
responseType: "code",
|
||||
redirectUri: "http://localhost:3000/",
|
||||
scope: "openid profile email",
|
||||
};
|
||||
|
||||
function App() {
|
||||
@ -85,7 +87,7 @@ function App() {
|
||||
</header>
|
||||
</div>
|
||||
</AuthProvider>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@ -100,7 +102,7 @@ npm start
|
||||
```
|
||||
|
||||
Your browser should automatically open the app site or just go to `http://localhost:3000/`.
|
||||
On opening the app in the browser you will be redirected to the login of zitadel.ch
|
||||
On opening the app in the browser you will be redirected to the login of your instance.
|
||||
After successfully authenticating your user, you will get back to you application.
|
||||
It should show a popup which says: **You logged in {FirstName} {LastName}**
|
||||
|
||||
|
BIN
docs/static/img/angular/app-create-light.png
vendored
BIN
docs/static/img/angular/app-create-light.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 175 KiB |
BIN
docs/static/img/angular/app-create.png
vendored
Normal file
BIN
docs/static/img/angular/app-create.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 259 KiB |
@ -44,7 +44,7 @@ func (s *Server) GetUserByLoginNameGlobal(ctx context.Context, req *mgmt_pb.GetU
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := s.query.GetUser(ctx, loginName)
|
||||
user, err := s.query.GetUser(ctx, true, loginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
@ -13,7 +14,11 @@ import (
|
||||
func TranslationHandler() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
resp, err := handler(ctx, req)
|
||||
translator := newZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
translator, translatorError := newZitadelTranslator(authz.GetInstance(ctx).DefaultLanguage())
|
||||
if translatorError != nil {
|
||||
logging.New().WithError(translatorError).Error("could not load translator")
|
||||
return resp, err
|
||||
}
|
||||
if loc, ok := resp.(localizers); ok && resp != nil {
|
||||
translateFields(ctx, loc, translator)
|
||||
}
|
||||
|
@ -40,16 +40,13 @@ func translateError(ctx context.Context, err error, translator *i18n.Translator)
|
||||
return err
|
||||
}
|
||||
|
||||
func newZitadelTranslator(defaultLanguage language.Tag) *i18n.Translator {
|
||||
func newZitadelTranslator(defaultLanguage language.Tag) (*i18n.Translator, error) {
|
||||
return translatorFromNamespace("zitadel", defaultLanguage)
|
||||
}
|
||||
|
||||
func translatorFromNamespace(namespace string, defaultLanguage language.Tag) *i18n.Translator {
|
||||
func translatorFromNamespace(namespace string, defaultLanguage language.Tag) (*i18n.Translator, error) {
|
||||
dir, err := fs.NewWithNamespace(namespace)
|
||||
logging.LogWithFields("ERROR-7usEW", "namespace", namespace).OnError(err).Panic("unable to get namespace")
|
||||
logging.WithFields("namespace", namespace).OnError(err).Panic("unable to get namespace")
|
||||
|
||||
translator, err := i18n.NewTranslator(dir, defaultLanguage, "")
|
||||
logging.Log("ERROR-Sk8sf").OnError(err).Panic("unable to get i18n translator")
|
||||
|
||||
return translator
|
||||
return i18n.NewTranslator(dir, defaultLanguage, "")
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
}
|
||||
user, err := l.query.GetUser(setContext(r.Context(), userOrg), loginName)
|
||||
user, err := l.query.GetUser(setContext(r.Context(), userOrg), false, loginName)
|
||||
if err != nil {
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
|
@ -23,7 +23,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||
return
|
||||
}
|
||||
user, err := l.query.GetUser(setContext(r.Context(), authReq.UserOrgID), loginName)
|
||||
user, err := l.query.GetUser(setContext(r.Context(), authReq.UserOrgID), false, loginName)
|
||||
if err != nil {
|
||||
l.renderPasswordResetDone(w, r, authReq, err)
|
||||
return
|
||||
|
@ -12,14 +12,14 @@ Login:
|
||||
NextButtonText: suivant
|
||||
|
||||
SelectAccount:
|
||||
Title: Select account
|
||||
Description: Use your ZITADEL-Account
|
||||
TitleLinking: Select account for user linking
|
||||
DescriptionLinking: Select your account to link with your external user.
|
||||
OtherUser: Other User
|
||||
SessionState0: active
|
||||
SessionState1: inactive
|
||||
MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organisation.
|
||||
Title: Sélectionner un compte
|
||||
Description: Utilisez votre compte ZITADEL
|
||||
TitleLinking: Sélectionnez le compte pour le lien avec l'utilisateur
|
||||
DescriptionLinking: Sélectionnez votre compte pour établir un lien avec votre utilisateur externe.
|
||||
OtherUser: Autre utilisateur
|
||||
SessionState0: actif
|
||||
SessionState1: inactif
|
||||
MustBeMemberOfOrg: L'utilisateur doit être membre de l'organisation {{.OrgName}}.
|
||||
|
||||
Password:
|
||||
Title: Mot de passe
|
||||
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--zitadel-color-font-500);
|
||||
color: var(--zitadel-color-text-500);
|
||||
}
|
||||
|
||||
.lgn-logo-watermark {
|
||||
|
@ -50,17 +50,17 @@
|
||||
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
||||
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
||||
|
||||
--zitadel-color-font-50: rgb(0, 0, 0);
|
||||
--zitadel-color-font-100: rgb(0, 0, 0);
|
||||
--zitadel-color-font-200: rgb(0, 0, 0);
|
||||
--zitadel-color-font-300: rgb(0, 0, 0);
|
||||
--zitadel-color-font-400: rgb(0, 0, 0);
|
||||
--zitadel-color-font-500: rgb(0, 0, 0);
|
||||
--zitadel-color-font-600: rgb(0, 0, 0);
|
||||
--zitadel-color-font-700: rgb(0, 0, 0);
|
||||
--zitadel-color-font-800: rgb(0, 0, 0);
|
||||
--zitadel-color-font-900: rgb(0, 0, 0);
|
||||
--zitadel-color-font-contrast: rgb(255, 255, 255);
|
||||
--zitadel-color-text-50: rgb(0, 0, 0);
|
||||
--zitadel-color-text-100: rgb(0, 0, 0);
|
||||
--zitadel-color-text-200: rgb(0, 0, 0);
|
||||
--zitadel-color-text-300: rgb(0, 0, 0);
|
||||
--zitadel-color-text-400: rgb(0, 0, 0);
|
||||
--zitadel-color-text-500: rgb(0, 0, 0);
|
||||
--zitadel-color-text-600: rgb(0, 0, 0);
|
||||
--zitadel-color-text-700: rgb(0, 0, 0);
|
||||
--zitadel-color-text-800: rgb(0, 0, 0);
|
||||
--zitadel-color-text-900: rgb(0, 0, 0);
|
||||
--zitadel-color-text-contrast: rgb(255, 255, 255);
|
||||
--zitadel-color-label: #0000008a;
|
||||
|
||||
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
|
||||
|
@ -44,17 +44,17 @@
|
||||
--zitadel-color-input-border-hover: #1a1b1b;
|
||||
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
||||
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
||||
--zitadel-color-font-50: rgb(0, 0, 0);
|
||||
--zitadel-color-font-100: rgb(0, 0, 0);
|
||||
--zitadel-color-font-200: rgb(0, 0, 0);
|
||||
--zitadel-color-font-300: rgb(0, 0, 0);
|
||||
--zitadel-color-font-400: rgb(0, 0, 0);
|
||||
--zitadel-color-font-500: rgb(0, 0, 0);
|
||||
--zitadel-color-font-600: rgb(0, 0, 0);
|
||||
--zitadel-color-font-700: rgb(0, 0, 0);
|
||||
--zitadel-color-font-800: rgb(0, 0, 0);
|
||||
--zitadel-color-font-900: rgb(0, 0, 0);
|
||||
--zitadel-color-font-contrast: rgb(255, 255, 255);
|
||||
--zitadel-color-text-50: rgb(0, 0, 0);
|
||||
--zitadel-color-text-100: rgb(0, 0, 0);
|
||||
--zitadel-color-text-200: rgb(0, 0, 0);
|
||||
--zitadel-color-text-300: rgb(0, 0, 0);
|
||||
--zitadel-color-text-400: rgb(0, 0, 0);
|
||||
--zitadel-color-text-500: rgb(0, 0, 0);
|
||||
--zitadel-color-text-600: rgb(0, 0, 0);
|
||||
--zitadel-color-text-700: rgb(0, 0, 0);
|
||||
--zitadel-color-text-800: rgb(0, 0, 0);
|
||||
--zitadel-color-text-900: rgb(0, 0, 0);
|
||||
--zitadel-color-text-contrast: rgb(255, 255, 255);
|
||||
--zitadel-color-label: #0000008a;
|
||||
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
|
||||
--zitadel-color-account-selector-active: rgba(0, 0, 0, 0.05);
|
||||
@ -2819,7 +2819,7 @@ footer {
|
||||
}
|
||||
}
|
||||
footer a {
|
||||
color: var(--zitadel-color-font-500);
|
||||
color: var(--zitadel-color-text-500);
|
||||
}
|
||||
footer .lgn-logo-watermark {
|
||||
background: var(--zitadel-logo-powered-by) no-repeat;
|
||||
|
@ -40,7 +40,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
|
||||
}
|
||||
idGenerator := id.SonyFlakeGenerator()
|
||||
|
||||
view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator)
|
||||
view, err := auth_view.StartView(dbClient, oidcEncryption, queries, idGenerator, es)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
usr_model "github.com/zitadel/zitadel/internal/user/model"
|
||||
"github.com/zitadel/zitadel/internal/user/repository/view"
|
||||
"github.com/zitadel/zitadel/internal/user/repository/view/model"
|
||||
@ -18,15 +22,77 @@ func (v *View) UserByID(userID, instanceID string) (*model.UserView, error) {
|
||||
}
|
||||
|
||||
func (v *View) UserByUsername(userName, instanceID string) (*model.UserView, error) {
|
||||
return view.UserByUserName(v.Db, userTable, userName, instanceID)
|
||||
query, err := query.NewUserUsernameSearchQuery(userName, query.TextEquals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v.userByID(instanceID, query)
|
||||
}
|
||||
|
||||
func (v *View) UserByLoginName(loginName, instanceID string) (*model.UserView, error) {
|
||||
return view.UserByLoginName(v.Db, userTable, loginName, instanceID)
|
||||
loginNameQuery, err := query.NewUserLoginNamesSearchQuery(loginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v.userByID(instanceID, loginNameQuery)
|
||||
}
|
||||
|
||||
func (v *View) UserByLoginNameAndResourceOwner(loginName, resourceOwner, instanceID string) (*model.UserView, error) {
|
||||
return view.UserByLoginNameAndResourceOwner(v.Db, userTable, loginName, resourceOwner, instanceID)
|
||||
loginNameQuery, err := query.NewUserLoginNamesSearchQuery(loginName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceOwnerQuery, err := query.NewUserResourceOwnerSearchQuery(resourceOwner, query.TextEquals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v.userByID(instanceID, loginNameQuery, resourceOwnerQuery)
|
||||
}
|
||||
|
||||
func (v *View) userByID(instanceID string, queries ...query.SearchQuery) (*model.UserView, error) {
|
||||
ctx := authz.WithInstanceID(context.Background(), instanceID)
|
||||
|
||||
queriedUser, err := v.query.GetUser(ctx, true, queries...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := view.UserByID(v.Db, userTable, queriedUser.ID, instanceID)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
user = new(model.UserView)
|
||||
}
|
||||
|
||||
query, err := view.UserByIDQuery(queriedUser.ID, instanceID, user.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events, err := v.es.FilterEvents(ctx, query)
|
||||
if err != nil && user.Sequence == 0 {
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
userCopy := *user
|
||||
|
||||
for _, event := range events {
|
||||
if err := user.AppendEvent(event); err != nil {
|
||||
return &userCopy, nil
|
||||
}
|
||||
}
|
||||
|
||||
if user.State == int32(usr_model.UserStateDeleted) {
|
||||
return nil, errors.ThrowNotFound(nil, "VIEW-r4y8r", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (v *View) UsersByOrgID(orgID, instanceID string) ([]*model.UserView, error) {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/jinzhu/gorm"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
eventstore "github.com/zitadel/zitadel/internal/eventstore/v1"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
@ -15,9 +16,10 @@ type View struct {
|
||||
keyAlgorithm crypto.EncryptionAlgorithm
|
||||
idGenerator id.Generator
|
||||
query *query.Queries
|
||||
es eventstore.Eventstore
|
||||
}
|
||||
|
||||
func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, idGenerator id.Generator) (*View, error) {
|
||||
func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, idGenerator id.Generator, es eventstore.Eventstore) (*View, error) {
|
||||
gorm, err := gorm.Open("postgres", sqlClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -27,6 +29,7 @@ func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, queri
|
||||
keyAlgorithm: keyAlgorithm,
|
||||
idGenerator: idGenerator,
|
||||
query: queries,
|
||||
es: es,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ func (wm *OrgWriteModel) Reduce() error {
|
||||
wm.State = domain.OrgStateInactive
|
||||
case *org.OrgReactivatedEvent:
|
||||
wm.State = domain.OrgStateActive
|
||||
case *org.OrgRemovedEvent:
|
||||
wm.State = domain.OrgStateRemoved
|
||||
case *org.OrgChangedEvent:
|
||||
wm.Name = e.Name
|
||||
case *org.DomainPrimarySetEvent:
|
||||
@ -51,6 +53,9 @@ func (wm *OrgWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
EventTypes(
|
||||
org.OrgAddedEventType,
|
||||
org.OrgChangedEventType,
|
||||
org.OrgDeactivatedEventType,
|
||||
org.OrgReactivatedEventType,
|
||||
org.OrgRemovedEventType,
|
||||
org.OrgDomainPrimarySetEventType).
|
||||
Builder()
|
||||
}
|
||||
|
@ -292,8 +292,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggered bool, userID string, queries ...SearchQuery) (*User, error) {
|
||||
if shouldTriggered {
|
||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, queries ...SearchQuery) (*User, error) {
|
||||
if shouldTriggerBulk {
|
||||
projection.UserProjection.TriggerBulk(ctx)
|
||||
}
|
||||
|
||||
@ -314,7 +314,11 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggered bool, userID
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, queries ...SearchQuery) (*User, error) {
|
||||
func (q *Queries) GetUser(ctx context.Context, shouldTriggerBulk bool, queries ...SearchQuery) (*User, error) {
|
||||
if shouldTriggerBulk {
|
||||
projection.UserProjection.TriggerBulk(ctx)
|
||||
}
|
||||
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
query, scan := prepareUserQuery(instanceID)
|
||||
for _, q := range queries {
|
||||
|
Loading…
x
Reference in New Issue
Block a user