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: 'main'},
|
||||||
{name: '1.x.x', range: '1.x.x', channel: '1.x.x'},
|
{name: '1.x.x', range: '1.x.x', channel: '1.x.x'},
|
||||||
{name: 'v2-alpha', prerelease: true},
|
{name: 'v2-alpha', prerelease: true},
|
||||||
{name: 'notify-users', prerelease: true},
|
{name: 'auth-users', prerelease: true},
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
"@semantic-release/commit-analyzer"
|
"@semantic-release/commit-analyzer"
|
||||||
|
@ -22,9 +22,17 @@
|
|||||||
"main": "src/main.ts",
|
"main": "src/main.ts",
|
||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
"assets": [
|
||||||
"styles": ["src/styles.scss"],
|
"src/favicon.ico",
|
||||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
|
"src/assets",
|
||||||
|
"src/manifest.webmanifest"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
||||||
|
],
|
||||||
"allowedCommonJsDependencies": [
|
"allowedCommonJsDependencies": [
|
||||||
"@angular/common/locales/de",
|
"@angular/common/locales/de",
|
||||||
"codemirror/mode/javascript/javascript",
|
"codemirror/mode/javascript/javascript",
|
||||||
@ -122,15 +130,27 @@
|
|||||||
"polyfills": "src/polyfills.ts",
|
"polyfills": "src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.spec.json",
|
"tsConfig": "tsconfig.spec.json",
|
||||||
"karmaConfig": "karma.conf.js",
|
"karmaConfig": "karma.conf.js",
|
||||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
|
"assets": [
|
||||||
"styles": ["./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", "src/styles.scss"],
|
"src/favicon.ico",
|
||||||
"scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"]
|
"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": {
|
"lint": {
|
||||||
"builder": "@angular-eslint/builder:lint",
|
"builder": "@angular-eslint/builder:lint",
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
"lintFilePatterns": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.html"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e2e": {
|
"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,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^14.0.1",
|
"@angular/animations": "^14.0.4",
|
||||||
"@angular/cdk": "^14.0.1",
|
"@angular/cdk": "^14.0.4",
|
||||||
"@angular/common": "^14.0.1",
|
"@angular/common": "^14.0.4",
|
||||||
"@angular/compiler": "^14.0.1",
|
"@angular/compiler": "^14.0.4",
|
||||||
"@angular/core": "^14.0.1",
|
"@angular/core": "^14.0.4",
|
||||||
"@angular/forms": "^14.0.1",
|
"@angular/forms": "^14.0.4",
|
||||||
"@angular/material": "^14.0.1",
|
"@angular/material": "^14.0.4",
|
||||||
"@angular/material-moment-adapter": "^14.0.1",
|
"@angular/material-moment-adapter": "^14.0.4",
|
||||||
"@angular/platform-browser": "^14.0.1",
|
"@angular/platform-browser": "^14.0.4",
|
||||||
"@angular/platform-browser-dynamic": "^14.0.1",
|
"@angular/platform-browser-dynamic": "^14.0.4",
|
||||||
"@angular/router": "^14.0.1",
|
"@angular/router": "^14.0.4",
|
||||||
"@angular/service-worker": "^14.0.1",
|
"@angular/service-worker": "^14.0.4",
|
||||||
"@ctrl/ngx-codemirror": "^5.1.1",
|
"@ctrl/ngx-codemirror": "^5.1.1",
|
||||||
"@grpc/grpc-js": "^1.5.7",
|
"@grpc/grpc-js": "^1.5.7",
|
||||||
"@ngx-translate/core": "^14.0.0",
|
"@ngx-translate/core": "^14.0.0",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
"codemirror": "^5.65.0",
|
"codemirror": "^5.65.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"google-proto-files": "^2.5.0",
|
"google-proto-files": "^3.0.0",
|
||||||
"google-protobuf": "^3.19.4",
|
"google-protobuf": "^3.19.4",
|
||||||
"grpc-web": "^1.3.0",
|
"grpc-web": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.10.6",
|
"libphonenumber-js": "^1.10.6",
|
||||||
@ -52,32 +52,32 @@
|
|||||||
"zone.js": "~0.11.4"
|
"zone.js": "~0.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^14.0.1",
|
"@angular-devkit/build-angular": "^14.0.4",
|
||||||
"@angular-eslint/builder": "^14.0.0-alpha.3",
|
"@angular-eslint/builder": "^14.0.0",
|
||||||
"@angular-eslint/eslint-plugin": "^14.0.0-alpha.3",
|
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "^14.0.0-alpha.3",
|
"@angular-eslint/eslint-plugin-template": "^14.0.0",
|
||||||
"@angular-eslint/schematics": "^14.0.0-alpha.3",
|
"@angular-eslint/schematics": "^14.0.0",
|
||||||
"@angular-eslint/template-parser": "^14.0.0-alpha.3",
|
"@angular-eslint/template-parser": "^14.0.0",
|
||||||
"@angular/cli": "^14.0.1",
|
"@angular/cli": "^14.0.4",
|
||||||
"@angular/compiler-cli": "^14.0.1",
|
"@angular/compiler-cli": "^14.0.4",
|
||||||
"@angular/language-service": "^14.0.1",
|
"@angular/language-service": "^14.0.4",
|
||||||
"@types/jasmine": "~4.0.3",
|
"@types/jasmine": "~4.0.3",
|
||||||
"@types/jasminewd2": "~2.0.10",
|
"@types/jasminewd2": "~2.0.10",
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/jsonwebtoken": "^8.5.5",
|
||||||
"@types/node": "^17.0.42",
|
"@types/node": "^17.0.42",
|
||||||
"@typescript-eslint/eslint-plugin": "5.25.0",
|
"@typescript-eslint/eslint-plugin": "5.30.4",
|
||||||
"@typescript-eslint/parser": "5.27.0",
|
"@typescript-eslint/parser": "5.30.4",
|
||||||
"codelyzer": "^6.0.0",
|
"codelyzer": "^6.0.0",
|
||||||
"cypress": "^10.1.0",
|
"cypress": "^10.1.0",
|
||||||
"cypress-terminal-report": "^4.0.1",
|
"cypress-terminal-report": "^4.0.1",
|
||||||
"eslint": "^8.17.0",
|
"eslint": "^8.18.0",
|
||||||
"jasmine-core": "~4.1.1",
|
"jasmine-core": "~4.2.0",
|
||||||
"jasmine-spec-reporter": "~7.0.0",
|
"jasmine-spec-reporter": "~7.0.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"karma": "~6.3.16",
|
"karma": "~6.4.0",
|
||||||
"karma-chrome-launcher": "~3.1.0",
|
"karma-chrome-launcher": "~3.1.0",
|
||||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||||
"karma-jasmine": "~5.0.1",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "^2.0.0",
|
"karma-jasmine-html-reporter": "^2.0.0",
|
||||||
"mochawesome": "^7.1.2",
|
"mochawesome": "^7.1.2",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
|
@ -13,6 +13,10 @@ const routes: Routes = [
|
|||||||
loadChildren: () => import('./pages/home/home.module').then((m) => m.HomeModule),
|
loadChildren: () => import('./pages/home/home.module').then((m) => m.HomeModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'signedout',
|
||||||
|
loadChildren: () => import('./pages/signedout/signedout.module').then((m) => m.SignedoutModule),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'orgs',
|
path: 'orgs',
|
||||||
loadChildren: () => import('./pages/org-list/org-list.module').then((m) => m.OrgListModule),
|
loadChildren: () => import('./pages/org-list/org-list.module').then((m) => m.OrgListModule),
|
||||||
@ -38,12 +42,7 @@ const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'users',
|
path: 'users',
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
children: [
|
loadChildren: () => import('src/app/pages/users/users.module').then((m) => m.UsersModule),
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
loadChildren: () => import('src/app/pages/users/users.module').then((m) => m.UsersModule),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'instance',
|
path: 'instance',
|
||||||
@ -170,10 +169,6 @@ const routes: Routes = [
|
|||||||
roles: ['policy.read'],
|
roles: ['policy.read'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'signedout',
|
|
||||||
loadChildren: () => import('./pages/signedout/signedout.module').then((m) => m.SignedoutModule),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '**',
|
path: '**',
|
||||||
redirectTo: '/',
|
redirectTo: '/',
|
||||||
|
@ -1,32 +1,31 @@
|
|||||||
<ng-container *ngIf="(authService.user | async) || undefined as user">
|
<div class="main-container">
|
||||||
<ng-container *ngIf="['iam.read$', 'iam.write$'] | hasRole as iamuser$">
|
<ng-container *ngIf="(authService.user | async) || {} as user">
|
||||||
<div class="main-container">
|
<cnsl-header
|
||||||
<cnsl-header
|
*ngIf="user && user !== {}"
|
||||||
*ngIf="user"
|
[org]="org"
|
||||||
[org]="org"
|
[user]="$any(user)"
|
||||||
[user]="user"
|
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
[labelpolicy]="labelpolicy"
|
||||||
[labelpolicy]="labelpolicy"
|
(changedActiveOrg)="changedOrg($event)"
|
||||||
(changedActiveOrg)="changedOrg($event)"
|
></cnsl-header>
|
||||||
></cnsl-header>
|
|
||||||
|
|
||||||
<cnsl-nav
|
<cnsl-nav
|
||||||
id="mainnav"
|
id="mainnav"
|
||||||
class="nav"
|
class="nav"
|
||||||
[ngClass]="{ shadow: yoffset > 60 }"
|
[ngClass]="{ shadow: yoffset > 60 }"
|
||||||
*ngIf="user"
|
*ngIf="user && user !== {}"
|
||||||
[org]="org"
|
[org]="org"
|
||||||
[user]="user"
|
[user]="$any(user)"
|
||||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||||
[labelpolicy]="labelpolicy"
|
[labelpolicy]="labelpolicy"
|
||||||
></cnsl-nav>
|
></cnsl-nav>
|
||||||
<div class="router-container" [@routeAnimations]="prepareRoute(outlet)">
|
|
||||||
<div class="outlet">
|
|
||||||
<router-outlet class="outlet" #outlet="outlet"></router-outlet>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="fill-space"></span>
|
|
||||||
<cnsl-footer [privateLabelPolicy]="labelpolicy"></cnsl-footer>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
|
||||||
|
<div class="router-container" [@routeAnimations]="prepareRoute(outlet)">
|
||||||
|
<div class="outlet">
|
||||||
|
<router-outlet class="outlet" #outlet="outlet"></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="fill-space"></span>
|
||||||
|
<cnsl-footer [privateLabelPolicy]="labelpolicy"></cnsl-footer>
|
||||||
|
</div>
|
||||||
|
@ -69,6 +69,7 @@ export class AppComponent implements OnDestroy {
|
|||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
@Inject(DOCUMENT) private document: Document,
|
@Inject(DOCUMENT) private document: Document,
|
||||||
) {
|
) {
|
||||||
|
this.themeService.loadPrivateLabelling(true);
|
||||||
console.log(
|
console.log(
|
||||||
'%cWait!',
|
'%cWait!',
|
||||||
'text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; color: #5469D4; font-size: 50px',
|
'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 { KeyboardShortcutsModule } from './modules/keyboard-shortcuts/keyboard-shortcuts.module';
|
||||||
import { NavModule } from './modules/nav/nav.module';
|
import { NavModule } from './modules/nav/nav.module';
|
||||||
import { WarnDialogModule } from './modules/warn-dialog/warn-dialog.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 { HasRolePipeModule } from './pipes/has-role-pipe/has-role-pipe.module';
|
||||||
import { AdminService } from './services/admin.service';
|
import { AdminService } from './services/admin.service';
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
import { AuthenticationService } from './services/authentication.service';
|
||||||
@ -79,7 +78,7 @@ const authConfig: AuthConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent, SignedoutComponent],
|
declarations: [AppComponent],
|
||||||
imports: [
|
imports: [
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -4,13 +4,12 @@ import { AuthConfig } from 'angular-oauth2-oidc';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { AuthenticationService } from '../services/authentication.service';
|
import { AuthenticationService } from '../services/authentication.service';
|
||||||
import { GrpcAuthService } from '../services/grpc-auth.service';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
constructor(private auth: AuthenticationService, private authService: GrpcAuthService) {}
|
constructor(private auth: AuthenticationService) {}
|
||||||
|
|
||||||
public canActivate(
|
public canActivate(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
|
@ -196,55 +196,56 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="account-card-wrapper">
|
<ng-container
|
||||||
<button
|
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||||
cdkOverlayOrigin
|
|
||||||
#accounttrigger="cdkOverlayOrigin"
|
|
||||||
class="icon-container"
|
|
||||||
(click)="showAccount = !showAccount"
|
|
||||||
[ngClass]="{ 'iam-user': (['iam.write$'] | hasRole | async) }"
|
|
||||||
>
|
|
||||||
<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 || ''"
|
|
||||||
[forColor]="user?.preferredLoginName || ''"
|
|
||||||
[name]="
|
|
||||||
user.human?.profile?.displayName
|
|
||||||
? user.human?.profile?.displayName ?? ''
|
|
||||||
: user.human?.profile?.firstName + ' ' + user.human?.profile?.lastName
|
|
||||||
"
|
|
||||||
[size]="38"
|
|
||||||
>
|
|
||||||
</cnsl-avatar>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ng-template
|
|
||||||
cdkConnectedOverlay
|
|
||||||
[cdkConnectedOverlayOrigin]="accounttrigger"
|
|
||||||
[flexibleDimensions]="true"
|
|
||||||
[lockPosition]="true"
|
|
||||||
[cdkConnectedOverlayOffsetY]="10"
|
|
||||||
[cdkConnectedOverlayHasBackdrop]="true"
|
|
||||||
[cdkConnectedOverlayPositions]="accountCardPositions"
|
|
||||||
cdkConnectedOverlayBackdropClass="transparent-backdrop"
|
|
||||||
[cdkConnectedOverlayOpen]="showAccount"
|
|
||||||
(backdropClick)="showAccount = false"
|
|
||||||
(detach)="showAccount = false"
|
|
||||||
>
|
>
|
||||||
<cnsl-accounts-card
|
<div class="account-card-wrapper">
|
||||||
@accounts
|
<button
|
||||||
class="a_card"
|
cdkOverlayOrigin
|
||||||
*ngIf="showAccount"
|
#accounttrigger="cdkOverlayOrigin"
|
||||||
(closedCard)="showAccount = false"
|
class="icon-container"
|
||||||
[user]="user"
|
(click)="showAccount = !showAccount"
|
||||||
[iamuser]="['iam.write$'] | hasRole | async"
|
[ngClass]="{ 'iam-user': (['iam.write$'] | hasRole | async) }"
|
||||||
|
>
|
||||||
|
<cnsl-avatar
|
||||||
|
id="avatartoggle"
|
||||||
|
class="avatar-toggle dontcloseonclick"
|
||||||
|
[active]="showAccount"
|
||||||
|
[avatarUrl]="user.human?.profile?.avatarUrl || ''"
|
||||||
|
[forColor]="user?.preferredLoginName || ''"
|
||||||
|
[name]="
|
||||||
|
user.human?.profile?.displayName
|
||||||
|
? user.human?.profile?.displayName ?? ''
|
||||||
|
: user.human?.profile?.firstName + ' ' + user.human?.profile?.lastName
|
||||||
|
"
|
||||||
|
[size]="38"
|
||||||
|
>
|
||||||
|
</cnsl-avatar>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ng-template
|
||||||
|
cdkConnectedOverlay
|
||||||
|
[cdkConnectedOverlayOrigin]="accounttrigger"
|
||||||
|
[flexibleDimensions]="true"
|
||||||
|
[lockPosition]="true"
|
||||||
|
[cdkConnectedOverlayOffsetY]="10"
|
||||||
|
[cdkConnectedOverlayHasBackdrop]="true"
|
||||||
|
[cdkConnectedOverlayPositions]="accountCardPositions"
|
||||||
|
cdkConnectedOverlayBackdropClass="transparent-backdrop"
|
||||||
|
[cdkConnectedOverlayOpen]="showAccount"
|
||||||
|
(backdropClick)="showAccount = false"
|
||||||
|
(detach)="showAccount = false"
|
||||||
>
|
>
|
||||||
</cnsl-accounts-card>
|
<cnsl-accounts-card
|
||||||
</ng-template>
|
@accounts
|
||||||
|
class="a_card"
|
||||||
|
*ngIf="showAccount"
|
||||||
|
(closedCard)="showAccount = false"
|
||||||
|
[user]="user"
|
||||||
|
[iamuser]="['iam.write$'] | hasRole | async"
|
||||||
|
>
|
||||||
|
</cnsl-accounts-card>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
|
@ -5,7 +5,25 @@
|
|||||||
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE"
|
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE"
|
||||||
[hasContributors]="true"
|
[hasContributors]="true"
|
||||||
stateTooltip="{{ 'ORG.STATE.' + org?.state | translate }}"
|
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
|
<cnsl-contributors
|
||||||
topContributors
|
topContributors
|
||||||
[totalResult]="totalMemberResult"
|
[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 { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
import { InfoSectionType } from 'src/app/modules/info-section/info-section.component';
|
||||||
import { PolicyComponentServiceType } from 'src/app/modules/policies/policy-component-types.enum';
|
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 { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||||
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
import { Org, OrgState } from 'src/app/proto/generated/zitadel/org_pb';
|
||||||
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
import { User } from 'src/app/proto/generated/zitadel/user_pb';
|
||||||
@ -62,6 +63,56 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
this.destroy$.complete();
|
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> {
|
private async getData(): Promise<void> {
|
||||||
this.mgmtService
|
this.mgmtService
|
||||||
.getMyOrg()
|
.getMyOrg()
|
||||||
|
@ -4,14 +4,14 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
import { SignedoutComponent } from './signedout.component';
|
import { SignedoutComponent } from './signedout.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: SignedoutComponent,
|
component: SignedoutComponent,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
exports: [RouterModule],
|
exports: [RouterModule],
|
||||||
})
|
})
|
||||||
export class SignedoutRoutingModule { }
|
export class SignedoutRoutingModule {}
|
||||||
|
@ -5,11 +5,18 @@
|
|||||||
<ng-template #lighttheme>
|
<ng-template #lighttheme>
|
||||||
<img alt="zitadel logo" src="../../../assets/images/zitadel-logo-dark.svg" />
|
<img alt="zitadel logo" src="../../../assets/images/zitadel-logo-dark.svg" />
|
||||||
</ng-template>
|
</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
|
<button
|
||||||
[routerLink]="[ '/users/me' ]">{{'USER.SIGNEDOUT_BTN' |
|
class="cnsl-action-button"
|
||||||
translate}} <i class="las la-sign-in-alt"></i></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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
@ -24,16 +25,6 @@
|
|||||||
img {
|
img {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
max-width: 170px;
|
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 { Component } from '@angular/core';
|
||||||
|
import { ThemeService } from 'src/app/services/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'cnsl-signedout',
|
selector: 'cnsl-signedout',
|
||||||
@ -8,7 +9,9 @@ import { Component } from '@angular/core';
|
|||||||
export class SignedoutComponent {
|
export class SignedoutComponent {
|
||||||
public dark: boolean = true;
|
public dark: boolean = true;
|
||||||
|
|
||||||
constructor() {
|
constructor(themeService: ThemeService) {
|
||||||
|
themeService.loadPrivateLabelling();
|
||||||
|
|
||||||
const theme = localStorage.getItem('theme');
|
const theme = localStorage.getItem('theme');
|
||||||
this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true;
|
this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
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 { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
|
|
||||||
import { SignedoutRoutingModule } from './signedout-routing.module';
|
import { SignedoutRoutingModule } from './signedout-routing.module';
|
||||||
|
import { SignedoutComponent } from './signedout.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [SignedoutComponent],
|
||||||
imports: [
|
imports: [CommonModule, SignedoutRoutingModule, MatButtonModule, MatTooltipModule, TranslateModule, SharedModule],
|
||||||
CommonModule,
|
|
||||||
SignedoutRoutingModule,
|
|
||||||
SharedModule,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class SignedoutModule { }
|
export class SignedoutModule {}
|
||||||
|
@ -49,10 +49,10 @@ export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
|||||||
switchMap((url: string) => {
|
switchMap((url: string) => {
|
||||||
if (url.includes('?login_hint=')) {
|
if (url.includes('?login_hint=')) {
|
||||||
const newUrl = this.removeParam('login_hint', url);
|
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));
|
return of(this.processor.createState(urlWithoutBasePath));
|
||||||
} else if (url) {
|
} 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));
|
return of(this.processor.createState(urlWithoutBasePath));
|
||||||
} else {
|
} else {
|
||||||
return of(undefined);
|
return of(undefined);
|
||||||
|
@ -140,70 +140,72 @@ export class ThemeService {
|
|||||||
this.saveTextColor(lightText, false);
|
this.saveTextColor(lightText, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
public loadPrivateLabelling(): void {
|
public loadPrivateLabelling(forceDefault: boolean = false): void {
|
||||||
this.setDefaultColors();
|
if (forceDefault) {
|
||||||
|
this.setDefaultColors();
|
||||||
|
} else {
|
||||||
|
const isDark = (color: string) => this.isDark(color);
|
||||||
|
const isLight = (color: string) => this.isLight(color);
|
||||||
|
|
||||||
const isDark = (color: string) => this.isDark(color);
|
this.authService
|
||||||
const isLight = (color: string) => this.isLight(color);
|
.getMyLabelPolicy()
|
||||||
|
.then((lpresp) => {
|
||||||
|
const labelpolicy = lpresp.policy;
|
||||||
|
|
||||||
this.authService
|
const darkPrimary = labelpolicy?.primaryColorDark || '#bbbafa';
|
||||||
.getMyLabelPolicy()
|
const lightPrimary = labelpolicy?.primaryColor || '#5469d4';
|
||||||
.then((lpresp) => {
|
|
||||||
const labelpolicy = lpresp.policy;
|
|
||||||
|
|
||||||
const darkPrimary = labelpolicy?.primaryColorDark || '#bbbafa';
|
const darkWarn = labelpolicy?.warnColorDark || '#ff3b5b';
|
||||||
const lightPrimary = labelpolicy?.primaryColor || '#5469d4';
|
const lightWarn = labelpolicy?.warnColor || '#cd3d56';
|
||||||
|
|
||||||
const darkWarn = labelpolicy?.warnColorDark || '#ff3b5b';
|
let darkBackground = labelpolicy?.backgroundColorDark;
|
||||||
const lightWarn = labelpolicy?.warnColor || '#cd3d56';
|
let lightBackground = labelpolicy?.backgroundColor;
|
||||||
|
|
||||||
let darkBackground = labelpolicy?.backgroundColorDark;
|
let darkText = labelpolicy?.fontColorDark ?? '#ffffff';
|
||||||
let lightBackground = labelpolicy?.backgroundColor;
|
let lightText = labelpolicy?.fontColor ?? '#000000';
|
||||||
|
|
||||||
let darkText = labelpolicy?.fontColorDark ?? '#ffffff';
|
this.savePrimaryColor(darkPrimary, true);
|
||||||
let lightText = labelpolicy?.fontColor ?? '#000000';
|
this.savePrimaryColor(lightPrimary, false);
|
||||||
|
|
||||||
this.savePrimaryColor(darkPrimary, true);
|
this.saveWarnColor(darkWarn, true);
|
||||||
this.savePrimaryColor(lightPrimary, false);
|
this.saveWarnColor(lightWarn, false);
|
||||||
|
|
||||||
this.saveWarnColor(darkWarn, true);
|
if (darkBackground && !isDark(darkBackground)) {
|
||||||
this.saveWarnColor(lightWarn, false);
|
console.info(
|
||||||
|
`Background (${darkBackground}) is not dark enough for a dark theme. Falling back to zitadel background`,
|
||||||
|
);
|
||||||
|
darkBackground = '#111827';
|
||||||
|
}
|
||||||
|
this.saveBackgroundColor(darkBackground || '#111827', true);
|
||||||
|
|
||||||
if (darkBackground && !isDark(darkBackground)) {
|
if (lightBackground && !isLight(lightBackground)) {
|
||||||
console.info(
|
console.info(
|
||||||
`Background (${darkBackground}) is not dark enough for a dark theme. Falling back to zitadel background`,
|
`Background (${lightBackground}) is not light enough for a light theme. Falling back to zitadel background`,
|
||||||
);
|
);
|
||||||
darkBackground = '#111827';
|
lightBackground = '#fafafa';
|
||||||
}
|
}
|
||||||
this.saveBackgroundColor(darkBackground || '#111827', true);
|
this.saveBackgroundColor(lightBackground || '#fafafa', false);
|
||||||
|
|
||||||
if (lightBackground && !isLight(lightBackground)) {
|
if (darkText && !isLight(darkText)) {
|
||||||
console.info(
|
console.info(
|
||||||
`Background (${lightBackground}) is not light enough for a light theme. Falling back to zitadel background`,
|
`Text color (${darkText}) is not light enough for a dark theme. Falling back to zitadel's text color`,
|
||||||
);
|
);
|
||||||
lightBackground = '#fafafa';
|
darkText = '#ffffff';
|
||||||
}
|
}
|
||||||
this.saveBackgroundColor(lightBackground || '#fafafa', false);
|
this.saveTextColor(darkText || '#ffffff', true);
|
||||||
|
|
||||||
if (darkText && !isLight(darkText)) {
|
if (lightText && !isDark(lightText)) {
|
||||||
console.info(
|
console.info(
|
||||||
`Text color (${darkText}) is not light enough for a dark theme. Falling back to zitadel's text color`,
|
`Text color (${lightText}) is not dark enough for a light theme. Falling back to zitadel's text color`,
|
||||||
);
|
);
|
||||||
darkText = '#ffffff';
|
lightText = '#000000';
|
||||||
}
|
}
|
||||||
this.saveTextColor(darkText || '#ffffff', true);
|
this.saveTextColor(lightText || '#000000', false);
|
||||||
|
})
|
||||||
if (lightText && !isDark(lightText)) {
|
.catch((error) => {
|
||||||
console.info(
|
console.error('could not load private labelling policy!', error);
|
||||||
`Text color (${lightText}) is not dark enough for a light theme. Falling back to zitadel's text color`,
|
this.setDefaultColors();
|
||||||
);
|
});
|
||||||
lightText = '#000000';
|
}
|
||||||
}
|
|
||||||
this.saveTextColor(lightText || '#000000', false);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('could not load private labelling policy!', error);
|
|
||||||
this.setDefaultColors();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -757,6 +757,8 @@
|
|||||||
"LISTDESCRIPTION": "Wähle eine Organisation aus.",
|
"LISTDESCRIPTION": "Wähle eine Organisation aus.",
|
||||||
"ACTIVE": "Aktiv",
|
"ACTIVE": "Aktiv",
|
||||||
"CREATE": "Organisation erstellen",
|
"CREATE": "Organisation erstellen",
|
||||||
|
"DEACTIVATE": "Organisation deaktivieren",
|
||||||
|
"REACTIVATE": "Organisation reaktivieren",
|
||||||
"NOPERMISSION": "Sie haben keine Berechtigung, auf Einstellungen der Organisation zuzugreifen.",
|
"NOPERMISSION": "Sie haben keine Berechtigung, auf Einstellungen der Organisation zuzugreifen.",
|
||||||
"USERSELFACCOUNT": "Verwenden Sie Ihr persönliches Konto als Organisationsinhaber",
|
"USERSELFACCOUNT": "Verwenden Sie Ihr persönliches Konto als Organisationsinhaber",
|
||||||
"ORGDETAIL_TITLE": "Gebe den Namen und die Domain für die neue Organisation ein.",
|
"ORGDETAIL_TITLE": "Gebe den Namen und die Domain für die neue Organisation ein.",
|
||||||
@ -821,6 +823,16 @@
|
|||||||
"MEMBERREMOVED": "Manager entfernt.",
|
"MEMBERREMOVED": "Manager entfernt.",
|
||||||
"MEMBERCHANGED": "Manager geändert.",
|
"MEMBERCHANGED": "Manager geändert.",
|
||||||
"SETPRIMARY": "Primäre Domain gesetzt."
|
"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": {
|
"SETTINGS": {
|
||||||
|
@ -757,6 +757,8 @@
|
|||||||
"LISTDESCRIPTION": "Choose an organization.",
|
"LISTDESCRIPTION": "Choose an organization.",
|
||||||
"ACTIVE": "Active",
|
"ACTIVE": "Active",
|
||||||
"CREATE": "Create Organization",
|
"CREATE": "Create Organization",
|
||||||
|
"DEACTIVATE": "Deactivate Organization",
|
||||||
|
"REACTIVATE": "Reactivate Organization",
|
||||||
"NOPERMISSION": "You don't have the permission to access organization settings.",
|
"NOPERMISSION": "You don't have the permission to access organization settings.",
|
||||||
"USERSELFACCOUNT": "Use your personal account as organization owner",
|
"USERSELFACCOUNT": "Use your personal account as organization owner",
|
||||||
"ORGDETAIL_TITLE": "Enter the name and domain of your new organization.",
|
"ORGDETAIL_TITLE": "Enter the name and domain of your new organization.",
|
||||||
@ -821,6 +823,16 @@
|
|||||||
"MEMBERREMOVED": "Manager removed.",
|
"MEMBERREMOVED": "Manager removed.",
|
||||||
"MEMBERCHANGED": "Manager changed.",
|
"MEMBERCHANGED": "Manager changed.",
|
||||||
"SETPRIMARY": "Primary domain set."
|
"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": {
|
"SETTINGS": {
|
||||||
|
@ -757,6 +757,8 @@
|
|||||||
"LISTDESCRIPTION": "Choisissez une organisation.",
|
"LISTDESCRIPTION": "Choisissez une organisation.",
|
||||||
"ACTIVE": "Actif",
|
"ACTIVE": "Actif",
|
||||||
"CREATE": "Créer une organisation",
|
"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.",
|
"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",
|
"USERSELFACCOUNT": "Utilisez votre compte personnel comme propriétaire de l'organisation",
|
||||||
"ORGDETAIL_TITLE": "Saisissez le nom et le domaine de votre nouvelle organisation.",
|
"ORGDETAIL_TITLE": "Saisissez le nom et le domaine de votre nouvelle organisation.",
|
||||||
@ -821,6 +823,16 @@
|
|||||||
"MEMBERREMOVED": "Gestionnaire supprimé.",
|
"MEMBERREMOVED": "Gestionnaire supprimé.",
|
||||||
"MEMBERCHANGED": "Gestionnaire modifié.",
|
"MEMBERCHANGED": "Gestionnaire modifié.",
|
||||||
"SETPRIMARY": "Domaine primaire défini."
|
"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": {
|
"SETTINGS": {
|
||||||
|
@ -757,6 +757,8 @@
|
|||||||
"LISTDESCRIPTION": "Scegli un'organizzazione.",
|
"LISTDESCRIPTION": "Scegli un'organizzazione.",
|
||||||
"ACTIVE": "Attivo",
|
"ACTIVE": "Attivo",
|
||||||
"CREATE": "Creare un'organizzazione",
|
"CREATE": "Creare un'organizzazione",
|
||||||
|
"DEACTIVATE": "Disattiva organizzazione",
|
||||||
|
"REACTIVATE": "Riattiva organizzazione",
|
||||||
"NOPERMISSION": "Non hai l'autorizzazione per accedere alle impostazioni dell'organizzazione.",
|
"NOPERMISSION": "Non hai l'autorizzazione per accedere alle impostazioni dell'organizzazione.",
|
||||||
"USERSELFACCOUNT": "Usa il tuo account personale come proprietario dell'organizzazione",
|
"USERSELFACCOUNT": "Usa il tuo account personale come proprietario dell'organizzazione",
|
||||||
"ORGDETAIL_TITLE": "Inserisci il nome e il dominio della tua nuova organizzazione.",
|
"ORGDETAIL_TITLE": "Inserisci il nome e il dominio della tua nuova organizzazione.",
|
||||||
@ -821,6 +823,16 @@
|
|||||||
"MEMBERREMOVED": "Manager rimosso con successo",
|
"MEMBERREMOVED": "Manager rimosso con successo",
|
||||||
"MEMBERCHANGED": "Manager cambiato con successo",
|
"MEMBERCHANGED": "Manager cambiato con successo",
|
||||||
"SETPRIMARY": "Dominio primario 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": {
|
"SETTINGS": {
|
||||||
|
@ -1,34 +1,31 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<title>Console</title>
|
<title>Console</title>
|
||||||
<base href="/">
|
<base href="/" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<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="stylesheet" href="./assets/icons/line-awesome/css/line-awesome.min.css" />
|
||||||
<link rel="manifest" href="manifest.webmanifest">
|
<link rel="manifest" href="manifest.webmanifest" />
|
||||||
<meta name="theme-color" content="#e6768b">
|
<meta name="theme-color" content="#e6768b" />
|
||||||
|
|
||||||
<meta property="description" content="Console Management Platform for ZITADEL IAM" />
|
<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:type" content="website" />
|
||||||
<meta property="og:title" content="ZITADEL Console" />
|
<meta property="og:title" content="ZITADEL Console" />
|
||||||
<meta property="og:description" content="Console Management Platform for ZITADEL IAM" />
|
<meta property="og:description" content="Console Management Platform for ZITADEL IAM" />
|
||||||
<meta property="description" content="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 property="og:image" content="https://www.zitadel.com/images/preview.png" />
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary" />
|
||||||
<meta name="twitter:site" content="@zitadel_ch">
|
<meta name="twitter:site" content="@zitadel" />
|
||||||
<meta name="twitter:title" content="ZITADEL Console" />
|
<meta name="twitter:title" content="ZITADEL Console" />
|
||||||
<meta name="twitter:description" content="Management Platform for ZITADEL IAM" />
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<cnsl-root></cnsl-root>
|
<cnsl-root></cnsl-root>
|
||||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||||
</body>
|
</body>
|
||||||
|
</html>
|
||||||
</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
|
## New Knative environment
|
||||||
### Download and run Knative quickstart
|
### 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:
|
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.
|
That will get you a ready to go knative/kubernetes environment.
|
||||||
|
|
||||||
|
|
||||||
See Knative documentation here:
|
|
||||||
https://knative.dev/docs/install/quickstart-install/
|
|
||||||
|
|
||||||
|
|
||||||
## Database
|
## Database
|
||||||
start a single-node cockroachdb as statefulset
|
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
|
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
|
## Start ZITADEL with Knative
|
||||||
```bash
|
```bash
|
||||||
@ -58,9 +43,7 @@ kn service create zitadel \
|
|||||||
--env ZITADEL_TLS_ENABLED=false \
|
--env ZITADEL_TLS_ENABLED=false \
|
||||||
--env ZITADEL_EXTERNALDOMAIN=zitadel.default.127.0.0.1.sslip.io \
|
--env ZITADEL_EXTERNALDOMAIN=zitadel.default.127.0.0.1.sslip.io \
|
||||||
--env ZITADEL_S3DEFAULTINSTANCE_CUSTOMDOMAIN=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" \
|
--arg "start-from-init" --arg "--masterkey" --arg "MasterkeyNeedsToHave32Characters"
|
||||||
--mount /tls.secret=secret:certs/tls.secret \
|
|
||||||
--mount /tls.key=secret:certs/tls.key
|
|
||||||
```
|
```
|
||||||
|
|
||||||
or use the knative service yaml
|
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:
|
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!
|
* 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
|
## What's next
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import Compose from './compose.mdx'
|
|||||||
import Helm from './helm.mdx'
|
import Helm from './helm.mdx'
|
||||||
import Knative from './knative.mdx'
|
import Knative from './knative.mdx'
|
||||||
import NextSelfHosted from './nextselfhosted.mdx'
|
import NextSelfHosted from './nextselfhosted.mdx'
|
||||||
|
import DefaultUser from './defaultuser.mdx'
|
||||||
|
|
||||||
# Run ZITADEL
|
# 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.
|
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.
|
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 -->
|
<!-- TODO: Destroy -->
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
@ -37,21 +48,26 @@ By default, it runs a highly available ZITADEL instance along with a secure and
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="linux">
|
<TabItem value="linux">
|
||||||
<Linux/>
|
<Linux/>
|
||||||
|
<DefaultUser/>
|
||||||
<NextSelfHosted/>
|
<NextSelfHosted/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="macos">
|
<TabItem value="macos">
|
||||||
<MacOS/>
|
<MacOS/>
|
||||||
|
<DefaultUser/>
|
||||||
<NextSelfHosted/>
|
<NextSelfHosted/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="compose">
|
<TabItem value="compose">
|
||||||
<Compose/>
|
<Compose/>
|
||||||
|
<DefaultUser/>
|
||||||
<NextSelfHosted/>
|
<NextSelfHosted/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="k8s">
|
<TabItem value="k8s">
|
||||||
<Helm/>
|
<Helm/>
|
||||||
|
<DefaultUser/>
|
||||||
<NextSelfHosted/>
|
<NextSelfHosted/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="knative">
|
<TabItem value="knative">
|
||||||
<Knative/>
|
<Knative/>
|
||||||
|
<NextSelfHosted/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -3,32 +3,32 @@ title: Angular
|
|||||||
---
|
---
|
||||||
|
|
||||||
This integration guide shows you the recommended way to integrate ZITADEL into your Angular application.
|
This integration guide shows you the recommended way to integrate ZITADEL into your Angular application.
|
||||||
It shows how to add user login to your application and fetch some data from the user info endpoint.
|
It shows how to add user login to your application and fetch some data from the user info endpoint.
|
||||||
|
|
||||||
At the end of the guide, your application has login functionality and has access to the current user's profile.
|
At the end of the guide, your application has login functionality and has access to the current user's profile.
|
||||||
|
|
||||||
> This documentation refers to our [example](https://github.com/zitadel/zitadel-examples/tree/main/angular) in GitHub. Note that we've written ZITADEL Console in Angular, so you can also use that as a reference.
|
> This documentation refers to our [example](https://github.com/zitadel/zitadel-examples/tree/main/angular) in GitHub. Note that we've written ZITADEL Console in Angular, so you can also use that as a reference.
|
||||||
|
|
||||||
## Setup Application and Get Keys
|
## Setup Application and Get Keys
|
||||||
|
|
||||||
Before we can start building our application, we have to do a few configuration steps in ZITADEL Console.
|
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.
|
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.
|
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
|
### Redirect URIs
|
||||||
|
|
||||||
With the Redirect URIs field, you tell ZITADEL where it is allowed to redirect users to after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
|
With the Redirect URIs field, you tell ZITADEL where it is allowed to redirect users to after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
|
||||||
|
|
||||||
> If you are following along with the [example](https://github.com/zitadel/zitadel-examples/tree/main/angular), set dev mode to `true` and the Redirect URIs to <http://localhost:4200/auth/callback>.
|
> If you are following along with the [example](https://github.com/zitadel/zitadel-examples/tree/main/angular), set dev mode to `true` and the Redirect URIs to <http://localhost:4200/auth/callback>.
|
||||||
|
|
||||||
If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the Post Logout URIs field.
|
If you want to redirect the users back to a route on your application after they have logged out, add an optional redirect in the Post Logout URIs field.
|
||||||
|
|
||||||
Continue and create the application.
|
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.
|
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',
|
responseType: 'code',
|
||||||
oidc: true,
|
oidc: true,
|
||||||
clientId: 'YOUR-CLIENT-ID', // replace with your appid
|
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',
|
redirectUri: 'http://localhost:4200/auth/callback',
|
||||||
postLogoutRedirectUri: 'http://localhost:4200/signedout', // optional
|
postLogoutRedirectUri: 'http://localhost:4200/signedout', // optional
|
||||||
requireHttps: false // required for running locally
|
requireHttps: false // required for running locally
|
||||||
@ -68,14 +68,14 @@ const authConfig: AuthConfig = {
|
|||||||
...
|
...
|
||||||
imports: [
|
imports: [
|
||||||
OAuthModule.forRoot(),
|
OAuthModule.forRoot(),
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
...
|
...
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: AuthConfig,
|
provide: AuthConfig,
|
||||||
useValue: authConfig
|
useValue: authConfig
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Set _openid_, _profile_ and _email_ as scope, _code_ as responseType, and oidc to _true_. Then create an authentication service to provide the functions to authenticate your user.
|
Set _openid_, _profile_ and _email_ as scope, _code_ as responseType, and oidc to _true_. Then create an authentication service to provide the functions to authenticate your user.
|
||||||
@ -89,63 +89,62 @@ 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.
|
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
|
```ts
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
import { AuthConfig, OAuthService } from "angular-oauth2-oidc";
|
||||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
import { BehaviorSubject, from, Observable } from "rxjs";
|
||||||
|
|
||||||
import { StatehandlerService } from './statehandler.service';
|
import { StatehandlerService } from "./statehandler.service";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
private _authenticated: boolean = false;
|
private _authenticated: boolean = false;
|
||||||
private readonly _authenticationChanged: BehaviorSubject<
|
private readonly _authenticationChanged: BehaviorSubject<boolean> =
|
||||||
boolean
|
new BehaviorSubject(this.authenticated);
|
||||||
> = new BehaviorSubject(this.authenticated);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private oauthService: OAuthService,
|
private oauthService: OAuthService,
|
||||||
private authConfig: AuthConfig,
|
private authConfig: AuthConfig,
|
||||||
private statehandler: StatehandlerService,
|
private statehandler: StatehandlerService
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
public get authenticated(): boolean {
|
public get authenticated(): boolean {
|
||||||
return this._authenticated;
|
return this._authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authenticationChanged(): Observable<boolean> {
|
||||||
|
return this._authenticationChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getOIDCUser(): Observable<any> {
|
||||||
|
return from(this.oauthService.loadUserProfile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async authenticate(setState: boolean = true): Promise<boolean> {
|
||||||
|
this.oauthService.configure(this.authConfig);
|
||||||
|
|
||||||
|
this.oauthService.strictDiscoveryDocumentValidation = false;
|
||||||
|
await this.oauthService.loadDiscoveryDocumentAndTryLogin();
|
||||||
|
|
||||||
|
this._authenticated = this.oauthService.hasValidAccessToken();
|
||||||
|
|
||||||
|
if (!this.oauthService.hasValidIdToken() || !this.authenticated) {
|
||||||
|
const newState = setState
|
||||||
|
? await this.statehandler.createState().toPromise()
|
||||||
|
: undefined;
|
||||||
|
this.oauthService.initCodeFlow(newState);
|
||||||
}
|
}
|
||||||
|
this._authenticationChanged.next(this.authenticated);
|
||||||
|
|
||||||
public get authenticationChanged(): Observable<boolean> {
|
return this.authenticated;
|
||||||
return this._authenticationChanged;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public getOIDCUser(): Observable<any> {
|
public signout(): void {
|
||||||
return from(this.oauthService.loadUserProfile());
|
this.oauthService.logOut();
|
||||||
}
|
this._authenticated = false;
|
||||||
|
this._authenticationChanged.next(false);
|
||||||
public async authenticate(
|
}
|
||||||
setState: boolean = true,
|
|
||||||
): Promise<boolean> {
|
|
||||||
this.oauthService.configure(this.authConfig);
|
|
||||||
|
|
||||||
this.oauthService.strictDiscoveryDocumentValidation = false;
|
|
||||||
await this.oauthService.loadDiscoveryDocumentAndTryLogin();
|
|
||||||
|
|
||||||
this._authenticated = this.oauthService.hasValidAccessToken();
|
|
||||||
|
|
||||||
if (!this.oauthService.hasValidIdToken() || !this.authenticated) {
|
|
||||||
const newState = setState ? await this.statehandler.createState().toPromise() : undefined;
|
|
||||||
this.oauthService.initCodeFlow(newState);
|
|
||||||
}
|
|
||||||
this._authenticationChanged.next(this.authenticated);
|
|
||||||
|
|
||||||
return this.authenticated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public signout(): void {
|
|
||||||
this.oauthService.logOut();
|
|
||||||
this._authenticated = false;
|
|
||||||
this._authenticationChanged.next(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -215,26 +214,35 @@ ng g guard guards/auth
|
|||||||
This code shows the _AuthGuard_ used in ZITADEL Console.
|
This code shows the _AuthGuard_ used in ZITADEL Console.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from "@angular/core";
|
||||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
|
import {
|
||||||
import { Observable } from 'rxjs';
|
ActivatedRouteSnapshot,
|
||||||
import { AuthenticationService } from '../services/authentication.service';
|
CanActivate,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
UrlTree,
|
||||||
|
} from "@angular/router";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
import { AuthenticationService } from "../services/authentication.service";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(private auth: AuthenticationService) {}
|
||||||
|
|
||||||
constructor(private auth: AuthenticationService) { }
|
|
||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
state: RouterStateSnapshot
|
||||||
if (!this.auth.authenticated) {
|
):
|
||||||
return this.auth.authenticate();
|
| Observable<boolean | UrlTree>
|
||||||
}
|
| Promise<boolean | UrlTree>
|
||||||
return this.auth.authenticated;
|
| boolean
|
||||||
}
|
| UrlTree {
|
||||||
|
if (!this.auth.authenticated) {
|
||||||
|
return this.auth.authenticate();
|
||||||
|
}
|
||||||
|
return this.auth.authenticated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -267,14 +275,14 @@ 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.
|
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
|
```ts
|
||||||
import { AuthenticationService } from 'src/app/services/authentication.service';
|
import { AuthenticationService } from "src/app/services/authentication.service";
|
||||||
|
|
||||||
export class SomeComponentWithLogout {
|
export class SomeComponentWithLogout {
|
||||||
constructor(private authService: AuthenticationService){}
|
constructor(private authService: AuthenticationService) {}
|
||||||
|
|
||||||
public signout(): Promise<void> {
|
public signout(): Promise<void> {
|
||||||
return this.authService.signout();
|
return this.authService.signout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -297,7 +305,7 @@ And in your HTML file:
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<div *ngIf="user$ | async as user">
|
<div *ngIf="user$ | async as user">
|
||||||
<p>{{user | json}}</p>
|
<p>{{user | json}}</p>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -45,14 +45,14 @@ You may want to change the Flutter SDK version in `pubspec.yaml` from
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.7.0 <3.0.0'
|
sdk: ">=2.7.0 <3.0.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
to
|
to
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
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.
|
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(
|
AuthorizationTokenRequest(
|
||||||
'<<CLIENT_ID>>', // Client ID of the native application
|
'<<CLIENT_ID>>', // Client ID of the native application
|
||||||
'<<CALLBACK_URL>>', // The registered url from zitadel (e.g. ch.myexample.app://signin)
|
'<<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: [
|
scopes: [
|
||||||
'openid',
|
'openid',
|
||||||
'profile',
|
'profile',
|
||||||
@ -268,7 +268,7 @@ Future<void> _authenticate() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final userInfoResponse = await get(
|
final userInfoResponse = await get(
|
||||||
Uri.parse('https://api.zitadel.ch/oauth/v2/userinfo'),
|
Uri.parse('https://[your-instance].zitadel.cloud/oauth/v2/userinfo'),
|
||||||
headers: {
|
headers: {
|
||||||
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
||||||
HttpHeaders.acceptHeader: 'application/json; charset=UTF-8'
|
HttpHeaders.acceptHeader: 'application/json; charset=UTF-8'
|
||||||
@ -333,7 +333,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final userInfoResponse = await get(
|
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: {
|
headers: {
|
||||||
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
HttpHeaders.authorizationHeader: 'Bearer ${result.accessToken}',
|
||||||
HttpHeaders.acceptHeader: 'application/json; charset=UTF-8'
|
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.
|
If you run this application, you can authenticate with a valid ZITADEL user.
|
||||||
|
|
||||||
<div style={{display:'flex', 'justify-content': 'center'}}>
|
<div style={{display: 'grid', 'grid-column-gap': '1rem', 'grid-template-columns': '1fr 1fr'}}>
|
||||||
<div style={{display:'flex', 'align-items': 'center'}}>
|
|
||||||
<img src="/img/flutter/not-authed.png" alt="Unauthenticated" height="500px" />
|
<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" />
|
<img src="/img/flutter/authed.png" alt="Flutter Authenticated" height="500px" />
|
||||||
</div>
|
|
||||||
</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`.
|
To setup your configuration, create a file called [...nextauth].tsx in `pages/api/auth`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import NextAuth from 'next-auth';
|
import NextAuth from "next-auth";
|
||||||
|
|
||||||
export const ZITADEL = {
|
export const ZITADEL = {
|
||||||
id: "zitadel",
|
id: "zitadel",
|
||||||
name: "zitadel",
|
name: "zitadel",
|
||||||
type: "oauth",
|
type: "oauth",
|
||||||
version: "2.0",
|
version: "2",
|
||||||
scope: "openid profile email",
|
wellKnown: process.env.ZITADEL_ISSUER,
|
||||||
params: { response_type: "code", grant_type: "authorization_code" },
|
authorization: {
|
||||||
authorizationParams: { grant_type: "authorization_code", response_type: "code" },
|
params: {
|
||||||
accessTokenUrl: "https://api.zitadel.dev/oauth/v2/token",
|
scope: "openid email profile",
|
||||||
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);
|
|
||||||
return {
|
|
||||||
id: profile.sub,
|
|
||||||
name: profile.name,
|
|
||||||
email: profile.email,
|
|
||||||
image: profile.picture
|
|
||||||
};
|
|
||||||
},
|
|
||||||
clientId: process.env.ZITADEL_CLIENT_ID,
|
|
||||||
session: {
|
|
||||||
jwt: true,
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
loginName: profile.preferred_username,
|
||||||
|
image: profile.picture,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
clientId: process.env.ZITADEL_CLIENT_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NextAuth({
|
export default NextAuth({
|
||||||
providers: [
|
providers: [ZITADEL],
|
||||||
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.
|
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.
|
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
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
ZITADEL_CLIENT_ID=[yourClientId]
|
ZITADEL_CLIENT_ID=[yourClientId]
|
||||||
|
ZITADEL_ISSUER=https:/[your-domain]-[random-string].zitadel.cloud
|
||||||
```
|
```
|
||||||
|
|
||||||
# User interface
|
# 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';
|
import { signIn, signOut, useSession } from 'next-auth/client';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const [session, loading] = useSession();
|
const { data: session } = useSession();
|
||||||
...
|
...
|
||||||
{!session && <>
|
{!session && <>
|
||||||
Not signed in <br />
|
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`.
|
Take a loot at the template `_app.tsx`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Provider } from 'next-auth/client';
|
import { SessionProvider } from "next-auth/react";
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
function MyApp({ Component, pageProps }) {
|
||||||
return <Provider
|
return (
|
||||||
session={pageProps.session} >
|
<SessionProvider session={pageProps.session}>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</Provider>;
|
</SessionProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
@ -143,17 +145,19 @@ export default MyApp;
|
|||||||
|
|
||||||
Last thing: create a `profile.tsx` in /pages which renders the callback page.
|
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.
|
export default function Profile() {
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
<h1>Login successful</h1>
|
||||||
|
<Link href="/">
|
||||||
## Deploy on Vercel
|
<button>Back to Home</button>
|
||||||
|
</Link>
|
||||||
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.
|
</div>
|
||||||
|
);
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
}
|
||||||
|
```
|
||||||
|
@ -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
|
## Setup Application and get Keys
|
||||||
|
|
||||||
Before we can start building our application we have to do a few configuration steps in ZITADEL Console.
|
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).
|
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.
|
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,38 +54,40 @@ 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.
|
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.
|
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.
|
Replace the clientId value 'YOUR-CLIENT-ID' with the generated client id of you application in ZITADEL Console.
|
||||||
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
import React from "react";
|
||||||
import React from 'react';
|
import { AuthProvider } from "oidc-react";
|
||||||
import { AuthProvider } from 'oidc-react';
|
import "./App.css";
|
||||||
import './App.css';
|
|
||||||
const oidcConfig = {
|
const oidcConfig = {
|
||||||
onSignIn: async (response: any) => {
|
onSignIn: async (response: any) => {
|
||||||
alert('You logged in :' + response.profile.given_name + ' ' + response.profile.family_name);
|
alert(
|
||||||
window.location.hash = '';
|
"You logged in :" +
|
||||||
},
|
response.profile.given_name +
|
||||||
authority: 'https://issuer.zitadel.ch',
|
" " +
|
||||||
clientId:
|
response.profile.family_name
|
||||||
'YOUR-CLIENT-ID',
|
);
|
||||||
responseType: 'code',
|
window.location.hash = "";
|
||||||
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() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<AuthProvider {...oidcConfig}>
|
<AuthProvider {...oidcConfig}>
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<header className="App-header">
|
<header className="App-header">
|
||||||
<p>Hello World</p>
|
<p>Hello World</p>
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
@ -100,7 +102,7 @@ npm start
|
|||||||
```
|
```
|
||||||
|
|
||||||
Your browser should automatically open the app site or just go to `http://localhost:3000/`.
|
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.
|
After successfully authenticating your user, you will get back to you application.
|
||||||
It should show a popup which says: **You logged in {FirstName} {LastName}**
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
user, err := s.query.GetUser(ctx, loginName)
|
user, err := s.query.GetUser(ctx, true, loginName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/logging"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"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) {
|
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) {
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||||
resp, err := handler(ctx, req)
|
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 {
|
if loc, ok := resp.(localizers); ok && resp != nil {
|
||||||
translateFields(ctx, loc, translator)
|
translateFields(ctx, loc, translator)
|
||||||
}
|
}
|
||||||
|
@ -40,16 +40,13 @@ func translateError(ctx context.Context, err error, translator *i18n.Translator)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func newZitadelTranslator(defaultLanguage language.Tag) *i18n.Translator {
|
func newZitadelTranslator(defaultLanguage language.Tag) (*i18n.Translator, error) {
|
||||||
return translatorFromNamespace("zitadel", defaultLanguage)
|
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)
|
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, "")
|
return i18n.NewTranslator(dir, defaultLanguage, "")
|
||||||
logging.Log("ERROR-Sk8sf").OnError(err).Panic("unable to get i18n translator")
|
|
||||||
|
|
||||||
return translator
|
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
|
|||||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||||
return
|
return
|
||||||
|
@ -23,7 +23,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
l.renderPasswordResetDone(w, r, authReq, err)
|
l.renderPasswordResetDone(w, r, authReq, err)
|
||||||
return
|
return
|
||||||
|
@ -12,14 +12,14 @@ Login:
|
|||||||
NextButtonText: suivant
|
NextButtonText: suivant
|
||||||
|
|
||||||
SelectAccount:
|
SelectAccount:
|
||||||
Title: Select account
|
Title: Sélectionner un compte
|
||||||
Description: Use your ZITADEL-Account
|
Description: Utilisez votre compte ZITADEL
|
||||||
TitleLinking: Select account for user linking
|
TitleLinking: Sélectionnez le compte pour le lien avec l'utilisateur
|
||||||
DescriptionLinking: Select your account to link with your external user.
|
DescriptionLinking: Sélectionnez votre compte pour établir un lien avec votre utilisateur externe.
|
||||||
OtherUser: Other User
|
OtherUser: Autre utilisateur
|
||||||
SessionState0: active
|
SessionState0: actif
|
||||||
SessionState1: inactive
|
SessionState1: inactif
|
||||||
MustBeMemberOfOrg: The user must be member of the {{.OrgName}} organisation.
|
MustBeMemberOfOrg: L'utilisateur doit être membre de l'organisation {{.OrgName}}.
|
||||||
|
|
||||||
Password:
|
Password:
|
||||||
Title: Mot de passe
|
Title: Mot de passe
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--zitadel-color-font-500);
|
color: var(--zitadel-color-text-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lgn-logo-watermark {
|
.lgn-logo-watermark {
|
||||||
|
@ -50,17 +50,17 @@
|
|||||||
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
||||||
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
||||||
|
|
||||||
--zitadel-color-font-50: rgb(0, 0, 0);
|
--zitadel-color-text-50: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-100: rgb(0, 0, 0);
|
--zitadel-color-text-100: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-200: rgb(0, 0, 0);
|
--zitadel-color-text-200: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-300: rgb(0, 0, 0);
|
--zitadel-color-text-300: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-400: rgb(0, 0, 0);
|
--zitadel-color-text-400: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-500: rgb(0, 0, 0);
|
--zitadel-color-text-500: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-600: rgb(0, 0, 0);
|
--zitadel-color-text-600: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-700: rgb(0, 0, 0);
|
--zitadel-color-text-700: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-800: rgb(0, 0, 0);
|
--zitadel-color-text-800: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-900: rgb(0, 0, 0);
|
--zitadel-color-text-900: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-contrast: rgb(255, 255, 255);
|
--zitadel-color-text-contrast: rgb(255, 255, 255);
|
||||||
--zitadel-color-label: #0000008a;
|
--zitadel-color-label: #0000008a;
|
||||||
|
|
||||||
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
|
--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-hover: #1a1b1b;
|
||||||
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
--zitadel-color-input-border-active: var(--zitadel-color-primary-500);
|
||||||
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
|
||||||
--zitadel-color-font-50: rgb(0, 0, 0);
|
--zitadel-color-text-50: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-100: rgb(0, 0, 0);
|
--zitadel-color-text-100: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-200: rgb(0, 0, 0);
|
--zitadel-color-text-200: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-300: rgb(0, 0, 0);
|
--zitadel-color-text-300: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-400: rgb(0, 0, 0);
|
--zitadel-color-text-400: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-500: rgb(0, 0, 0);
|
--zitadel-color-text-500: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-600: rgb(0, 0, 0);
|
--zitadel-color-text-600: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-700: rgb(0, 0, 0);
|
--zitadel-color-text-700: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-800: rgb(0, 0, 0);
|
--zitadel-color-text-800: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-900: rgb(0, 0, 0);
|
--zitadel-color-text-900: rgb(0, 0, 0);
|
||||||
--zitadel-color-font-contrast: rgb(255, 255, 255);
|
--zitadel-color-text-contrast: rgb(255, 255, 255);
|
||||||
--zitadel-color-label: #0000008a;
|
--zitadel-color-label: #0000008a;
|
||||||
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
|
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
|
||||||
--zitadel-color-account-selector-active: rgba(0, 0, 0, 0.05);
|
--zitadel-color-account-selector-active: rgba(0, 0, 0, 0.05);
|
||||||
@ -2819,7 +2819,7 @@ footer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
footer a {
|
footer a {
|
||||||
color: var(--zitadel-color-font-500);
|
color: var(--zitadel-color-text-500);
|
||||||
}
|
}
|
||||||
footer .lgn-logo-watermark {
|
footer .lgn-logo-watermark {
|
||||||
background: var(--zitadel-logo-powered-by) no-repeat;
|
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()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
package view
|
package view
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/errors"
|
"github.com/zitadel/zitadel/internal/errors"
|
||||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||||
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
usr_model "github.com/zitadel/zitadel/internal/user/model"
|
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"
|
||||||
"github.com/zitadel/zitadel/internal/user/repository/view/model"
|
"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) {
|
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) {
|
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) {
|
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) {
|
func (v *View) UsersByOrgID(orgID, instanceID string) ([]*model.UserView, error) {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/crypto"
|
"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/id"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
)
|
)
|
||||||
@ -15,9 +16,10 @@ type View struct {
|
|||||||
keyAlgorithm crypto.EncryptionAlgorithm
|
keyAlgorithm crypto.EncryptionAlgorithm
|
||||||
idGenerator id.Generator
|
idGenerator id.Generator
|
||||||
query *query.Queries
|
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)
|
gorm, err := gorm.Open("postgres", sqlClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -27,6 +29,7 @@ func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, queri
|
|||||||
keyAlgorithm: keyAlgorithm,
|
keyAlgorithm: keyAlgorithm,
|
||||||
idGenerator: idGenerator,
|
idGenerator: idGenerator,
|
||||||
query: queries,
|
query: queries,
|
||||||
|
es: es,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ func (wm *OrgWriteModel) Reduce() error {
|
|||||||
wm.State = domain.OrgStateInactive
|
wm.State = domain.OrgStateInactive
|
||||||
case *org.OrgReactivatedEvent:
|
case *org.OrgReactivatedEvent:
|
||||||
wm.State = domain.OrgStateActive
|
wm.State = domain.OrgStateActive
|
||||||
|
case *org.OrgRemovedEvent:
|
||||||
|
wm.State = domain.OrgStateRemoved
|
||||||
case *org.OrgChangedEvent:
|
case *org.OrgChangedEvent:
|
||||||
wm.Name = e.Name
|
wm.Name = e.Name
|
||||||
case *org.DomainPrimarySetEvent:
|
case *org.DomainPrimarySetEvent:
|
||||||
@ -51,6 +53,9 @@ func (wm *OrgWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|||||||
EventTypes(
|
EventTypes(
|
||||||
org.OrgAddedEventType,
|
org.OrgAddedEventType,
|
||||||
org.OrgChangedEventType,
|
org.OrgChangedEventType,
|
||||||
|
org.OrgDeactivatedEventType,
|
||||||
|
org.OrgReactivatedEventType,
|
||||||
|
org.OrgRemovedEventType,
|
||||||
org.OrgDomainPrimarySetEventType).
|
org.OrgDomainPrimarySetEventType).
|
||||||
Builder()
|
Builder()
|
||||||
}
|
}
|
||||||
|
@ -292,8 +292,8 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggered bool, userID string, queries ...SearchQuery) (*User, error) {
|
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string, queries ...SearchQuery) (*User, error) {
|
||||||
if shouldTriggered {
|
if shouldTriggerBulk {
|
||||||
projection.UserProjection.TriggerBulk(ctx)
|
projection.UserProjection.TriggerBulk(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,7 +314,11 @@ func (q *Queries) GetUserByID(ctx context.Context, shouldTriggered bool, userID
|
|||||||
return scan(row)
|
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()
|
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||||
query, scan := prepareUserQuery(instanceID)
|
query, scan := prepareUserQuery(instanceID)
|
||||||
for _, q := range queries {
|
for _, q := range queries {
|
||||||
|
@ -39,8 +39,8 @@ Errors:
|
|||||||
NotFound: Générateur de secret non trouvé
|
NotFound: Générateur de secret non trouvé
|
||||||
SMSConfig:
|
SMSConfig:
|
||||||
NotFound: Configuration SMS non trouvée
|
NotFound: Configuration SMS non trouvée
|
||||||
AlreadyActive: Configuration SMS déjà active
|
AlreadyActive: Configuration SMS déjà active
|
||||||
AlreadyDeactivated: Configuration SMS déjà désactivée
|
AlreadyDeactivated: Configuration SMS déjà désactivée
|
||||||
SMTPConfig:
|
SMTPConfig:
|
||||||
NotFound: Configuration SMTP non trouvée
|
NotFound: Configuration SMTP non trouvée
|
||||||
AlreadyExists: La configuration SMTP existe déjà
|
AlreadyExists: La configuration SMTP existe déjà
|
||||||
|
Loading…
x
Reference in New Issue
Block a user