Skip to content

Commit

Permalink
Merge pull request #763 from geo-engine/manager-oidc
Browse files Browse the repository at this point in the history
move user service to common
  • Loading branch information
ChristianBeilschmidt authored Dec 11, 2024
2 parents 76ee9d3 + bdfe1eb commit e60208f
Show file tree
Hide file tree
Showing 108 changed files with 753 additions and 867 deletions.
8 changes: 8 additions & 0 deletions projects/common/src/lib/common.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ import {AutocompleteSelectDirective} from './util/directives/autocomplete-select
import {CodeEditorComponent} from './util/components/code-editor.component';
import {OgrDatasetComponent} from './datasets/ogr-dataset/ogr-dataset.component';
import {RasterMultibandSymbologyEditorComponent} from './symbology/raster-multiband-symbology-editor/raster-multiband-symbology-editor.component';
import {LoginComponent} from './login/login.component';
import {RegisterComponent} from './register/register.component';
import {RouterLink, RouterLinkActive, RouterOutlet} from '@angular/router';

export const MATERIAL_MODULES = [
MatAutocompleteModule,
Expand Down Expand Up @@ -119,6 +122,7 @@ const COMMON_COMPONENTS = [
LayerCollectionNavigationComponent,
LineIconComponent,
LineIconComponent,
LoginComponent,
MeasurementComponent,
NumberParamEditorComponent,
PercentileBreakpointSelectorComponent,
Expand All @@ -130,6 +134,7 @@ const COMMON_COMPONENTS = [
RasterMultibandSymbologyEditorComponent,
RasterPaletteSymbologyEditorComponent,
RasterSymbologyEditorComponent,
RegisterComponent,
TimeInputComponent,
TimeIntervalInputComponent,
VectorSymbologyEditorComponent,
Expand Down Expand Up @@ -161,6 +166,9 @@ const FXFLEX_LEGACY_DIRECTIVES = [FxFlexDirective, FxLayoutDirective, FxLayoutGa
AngularCommonModule,
ScrollingModule,
OgrDatasetComponent,
RouterOutlet,
RouterLink,
RouterLinkActive,
...FXFLEX_LEGACY_DIRECTIVES,
],
exports: [
Expand Down
50 changes: 50 additions & 0 deletions projects/common/src/lib/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ export interface CommonConfigStructure {
readonly API_URL: string;
readonly DELAYS: Delays;
readonly PLOTS: Plots;
readonly USER: User;
readonly BRANDING: Branding;
}

export interface Branding {
readonly LOGO_URL: string;
readonly LOGO_ICON_URL: string;
readonly LOGO_ALT_URL: string;
readonly PAGE_TITLE: string;
readonly HOMEPAGE?: Homepage;
}

export interface Homepage {
readonly URL: string;
readonly BUTTON_IMAGE_URL: string;
readonly BUTTON_ALT_TEXT: string;
readonly BUTTON_TOOLTIP_TEXT: string;
}

/**
Expand All @@ -25,14 +42,39 @@ interface Plots {

export const DEFAULT_COMMON_CONFIG: CommonConfigStructure = {
API_URL: '/api',
BRANDING: {
LOGO_URL: 'assets/geoengine.svg',
LOGO_ICON_URL: 'assets/geoengine-favicon-white.svg',
LOGO_ALT_URL: 'assets/geoengine-white.svg',
PAGE_TITLE: 'Geo Engine',
},
DELAYS: {
DEBOUNCE: 400,
},
PLOTS: {
THEME: 'excel',
},
USER: {
GUEST: {
NAME: 'guest',
PASSWORD: 'guest',
},
AUTO_GUEST_LOGIN: true,
REGISTRATION_AVAILABLE: true,
LOCAL_LOGIN_AVAILABLE: false,
},
};

interface User {
readonly GUEST: {
readonly NAME: string;
readonly PASSWORD: string;
};
readonly AUTO_GUEST_LOGIN: boolean;
readonly REGISTRATION_AVAILABLE: boolean;
readonly LOCAL_LOGIN_AVAILABLE: boolean;
}

@Injectable()
export class CommonConfig {
static readonly CONFIG_FILE = 'assets/config.json';
Expand All @@ -43,6 +85,10 @@ export class CommonConfig {
return this.config.API_URL;
}

get BRANDING(): Branding {
return this.config.BRANDING;
}

get DELAYS(): Delays {
return this.config.DELAYS;
}
Expand All @@ -51,6 +97,10 @@ export class CommonConfig {
return this.config.PLOTS;
}

get USER(): User {
return this.config.USER;
}

// noinspection JSUnusedGlobalSymbols <- function used in parent app
/**
* Initialize the config on app start.
Expand Down
2 changes: 1 addition & 1 deletion projects/common/src/lib/datasets/datasets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class DatasetsService {

constructor(private sessionService: UserService) {
this.sessionService.getSessionStream().subscribe({
next: (session) => this.datasetApi.next(new DatasetsApi(apiConfigurationWithAccessKey(session.id))),
next: (session) => this.datasetApi.next(new DatasetsApi(apiConfigurationWithAccessKey(session.sessionToken))),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class LayersService {
private randomColorService: RandomColorService,
) {
this.sessionService.getSessionStream().subscribe({
next: (session) => this.layersApi.next(new LayersApi(apiConfigurationWithAccessKey(session.id))),
next: (session) => this.layersApi.next(new LayersApi(apiConfigurationWithAccessKey(session.sessionToken))),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@

<mat-card-content [ngSwitch]="formStatus$ | async">
<ng-template [ngSwitchCase]="FormStatus.Oidc">
<mat-card-actions>
<button mat-raised-button (click)="oidcLogin()" fxFlex color="primary">Sign in using OIDC</button>
<mat-card-actions class="login-buttons">
<button mat-raised-button (click)="oidcLogin()" color="primary">Sign in using OIDC</button>
@if (config.USER.LOCAL_LOGIN_AVAILABLE) {
<button mat-raised-button (click)="formStatus$.next(FormStatus.LoggedOut)">Use local login</button>
}
</mat-card-actions>
</ng-template>
<ng-template [ngSwitchCase]="FormStatus.LoggedOut">
Expand Down Expand Up @@ -61,7 +64,7 @@
<a routerLink="/register" *ngIf="canRegister">Register</a>
</ng-template>
<ng-template [ngSwitchCase]="FormStatus.LoggedIn">
<button mat-raised-button (click)="$event.preventDefault(); redirectToMainView()" fxFlex>To Map</button>
<button mat-raised-button (click)="$event.preventDefault(); redirectToMainView()" fxFlex>To App</button>
<button mat-raised-button (click)="$event.preventDefault(); logout()" fxFlex color="primary">Logout</button>
</ng-template>
</mat-card-actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,13 @@ img.logo {
vertical-align: middle;
}
}

.login-buttons {
display: flex;
flex-direction: column;
align-items: stretch;
}

.login-buttons button {
margin-top: 1rem;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {BehaviorSubject, Subscription} from 'rxjs';

import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, input, OnDestroy, OnInit} from '@angular/core';
import {UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';

import {NotificationService, UserService, User} from '@geoengine/core';
import {first} from 'rxjs/operators';
import {Router} from '@angular/router';
import {AppConfig} from '../app-config.service';
import {geoengineValidators} from '@geoengine/common';
import {CommonConfig} from '../config.service';
import {UserService} from '../user/user.service';
import {geoengineValidators} from '../util/form.validators';
import {User} from '../user/user.model';
import {NotificationService} from '../notification.service';

enum FormStatus {
LoggedOut,
Expand All @@ -25,8 +27,10 @@ enum FormStatus {
export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
readonly FormStatus = FormStatus;

loginRedirect = input('/map');

formStatus$ = new BehaviorSubject<FormStatus>(FormStatus.Loading);
canRegister = this.config.COMPONENTS.REGISTRATION.AVAILABLE;
canRegister = this.config.USER.REGISTRATION_AVAILABLE;

loginForm: UntypedFormGroup;

Expand All @@ -38,7 +42,7 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
private formStatusSubscription?: Subscription;

constructor(
readonly config: AppConfig,
readonly config: CommonConfig,
private readonly changeDetectorRef: ChangeDetectorRef,
private readonly userService: UserService,
private readonly notificationService: NotificationService,
Expand Down Expand Up @@ -139,6 +143,6 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
}

redirectToMainView(): void {
this.router.navigate(['map']);
this.router.navigate([this.loginRedirect()]);
}
}
2 changes: 1 addition & 1 deletion projects/common/src/lib/permissions/permissions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class PermissionsService {

constructor(private sessionService: UserService) {
this.sessionService.getSessionStream().subscribe({
next: (session) => this.permissionsApi.next(new PermissionsApi(apiConfigurationWithAccessKey(session.id))),
next: (session) => this.permissionsApi.next(new PermissionsApi(apiConfigurationWithAccessKey(session.sessionToken))),
});
}

Expand Down
2 changes: 1 addition & 1 deletion projects/common/src/lib/plots/plots.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class PlotsService {

constructor(private sessionService: UserService) {
this.sessionService.getSessionStream().subscribe({
next: (session) => this.plotApi.next(new PlotsApi(apiConfigurationWithAccessKey(session.id))),
next: (session) => this.plotApi.next(new PlotsApi(apiConfigurationWithAccessKey(session.sessionToken))),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</mat-card-content>
<mat-card-actions fxLayout="row" fxLayoutGap="1rem" fxLayoutAlign="space-between center" *ngIf="notLoading$ | async">
<button mat-raised-button type="submit" [disabled]="formIsInvalid$ | async" color="primary">Register</button>
<a routerLink="/login">Login</a>
<a routerLink="/signin">Login</a>
</mat-card-actions>
</mat-card>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import {BehaviorSubject, Observable} from 'rxjs';

import {AfterViewInit, ChangeDetectionStrategy, Component} from '@angular/core';
import {AfterViewInit, ChangeDetectionStrategy, Component, input} from '@angular/core';
import {UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms';

import {CoreConfig, NotificationService, UserService, BackendService} from '@geoengine/core';
import {map, mergeMap} from 'rxjs/operators';
import {Router} from '@angular/router';
import {geoengineValidators} from '@geoengine/common';
import {CommonConfig} from '../config.service';
import {UserService} from '../user/user.service';
import {NotificationService} from '../notification.service';
import {geoengineValidators} from '../util/form.validators';
import {GeoEngineError} from '../util/errors';

@Component({
selector: 'geoengine-register',
Expand All @@ -17,6 +20,8 @@ import {geoengineValidators} from '@geoengine/common';
export class RegisterComponent implements AfterViewInit {
PASSWORD_MIN_LENGTH = 8;

loginRedirect = input('/map');

loading$ = new BehaviorSubject<boolean>(false);
notLoading$ = this.loading$.pipe(map((loading) => !loading));
formIsInvalid$: Observable<boolean>;
Expand All @@ -26,9 +31,8 @@ export class RegisterComponent implements AfterViewInit {
registrationForm: UntypedFormGroup;

constructor(
private readonly config: CoreConfig,
private readonly config: CommonConfig,
private readonly userService: UserService,
private readonly backend: BackendService,
private readonly notificationService: NotificationService,
private readonly router: Router,
) {
Expand All @@ -49,37 +53,33 @@ export class RegisterComponent implements AfterViewInit {
setTimeout(() => this.registrationForm.updateValueAndValidity());
}

register(): void {
async register(): Promise<void> {
this.loading$.next(true);
this.registrationError$.next('');

const realName: string = this.registrationForm.controls['name'].value;
const email: string = this.registrationForm.controls['email'].value;
const password: string = this.registrationForm.controls['password'].value;

this.backend
.registerUser({
try {
await this.userService.registerUser({
email,
password,
realName,
})
.pipe(mergeMap(() => this.userService.login({email, password})))
.subscribe(
() => {
// success
this.notificationService.info('Registration successful!');
this.redirectToMainView();
},
(error) => {
// on error
this.registrationError$.next(error.error.message);

this.loading$.next(false);
},
);
});

this.notificationService.info('Registration successful!');
this.redirectToMainView();
} catch (error) {
if (error instanceof GeoEngineError) {
this.registrationError$.next(error.message);
}

this.loading$.next(false);
}
}

redirectToMainView(): void {
this.router.navigate(['map']);
this.router.navigate([this.loginRedirect()]);
}
}
2 changes: 1 addition & 1 deletion projects/common/src/lib/uploads/uploads.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class UploadsService {

constructor(private sessionService: UserService) {
this.sessionService.getSessionStream().subscribe({
next: (session) => this.uploadsApi.next(new UploadsApi(apiConfigurationWithAccessKey(session.id))),
next: (session) => this.uploadsApi.next(new UploadsApi(apiConfigurationWithAccessKey(session.sessionToken))),
});
}

Expand Down
16 changes: 16 additions & 0 deletions projects/common/src/lib/user/session.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Moment} from 'moment';
import {Configuration, STRectangle} from '@geoengine/openapi-client';
import {UUID} from '../datasets/dataset.model';
import {User} from './user.model';

/**
* A user session after login
*/
export interface Session {
sessionToken: string;
user?: User; // TODO: split for pro
apiConfiguration: Configuration;
validUntil: Moment; // TODO: custom time point?
lastProjectId?: UUID;
lastView?: STRectangle;
}
Loading

0 comments on commit e60208f

Please sign in to comment.