Skip to content

Commit

Permalink
auth services updated
Browse files Browse the repository at this point in the history
  • Loading branch information
ralfaron committed Sep 24, 2023
1 parent 5d842c3 commit b5ed855
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 378 deletions.
79 changes: 6 additions & 73 deletions projects/aas-lib/src/lib/auth/auth-api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,95 +20,28 @@ export class AuthApiService {
private readonly http: HttpClient
) { }

public registerUserAsync(profile: UserProfile): Promise<AuthResult> {
return new Promise<AuthResult>((result, reject) => {
let data: AuthResult;
this.http.post<AuthResult>(
'/api/v1/register',
profile).subscribe(
{
next: (value) => data = value,
complete: () => result(data),
error: (error) => reject(error)
});
});
}

/** @deprecated */
public guestAsync(): Promise<AuthResult> {
return new Promise<AuthResult>((result, reject) => {
let data: AuthResult;
this.http.post<AuthResult>(
'/api/v1/guest', undefined).subscribe(
{
next: value => data = value,
complete: () => result(data),
error: (error) => reject(error)
});
});
public register(profile: UserProfile): Observable<AuthResult> {
return this.http.post<AuthResult>('/api/v1/register', profile);
}

public guest(): Observable<AuthResult> {
return this.http.post<AuthResult>('/api/v1/guest', undefined);
}

/** @deprecated */
public loginAsync(credentials: Credentials): Promise<AuthResult> {
return new Promise<AuthResult>((result, reject) => {
let data: AuthResult;
this.http.post<AuthResult>(
'/api/v1/login',
credentials).subscribe(
{
next: value => data = value,
complete: () => result(data),
error: (error) => reject(error)
});
});
}

public login(credentials: Credentials): Observable<AuthResult> {
return this.http.post<AuthResult>('/api/v1/login', credentials);
}

public updateProfileAsync(id: string, profile: UserProfile): Promise<AuthResult> {
return new Promise<AuthResult>((result, reject) => {
let data: AuthResult;
this.http.put<AuthResult>(
`/api/v1/users/${encodeBase64Url(id)}`,
profile).subscribe(
{
next: (value) => data = value,
complete: () => result(data),
error: (error) => reject(error)
});
});
}

public updateProfile(id: string, profile: UserProfile): Observable<AuthResult> {
return this.http.put<AuthResult>(`/api/v1/users/${encodeBase64Url(id)}`, profile);
}

public deleteUserAsync(id: string): Promise<void> {
return new Promise<void>((result, reject) => {
this.http.delete<void>(
`/api/v1/users/${encodeBase64Url(id)}`)
.subscribe({
complete: () => result(),
error: (error) => reject(error)
});
});
public delete(id: string): Observable<void> {
return this.http.delete<void>(`/api/v1/users/${encodeBase64Url(id)}`);
}

public resetPasswordAsync(id: string): Promise<void> {
return new Promise<void>((result, reject) => {
this.http.delete<void>(
`/api/v1/users/${encodeBase64Url(id)}/reset`)
.subscribe({
complete: () => result(),
error: (error) => reject(error)
});
});
public resetPassword(id: string): Observable<void> {
return this.http.delete<void>(`/api/v1/users/${encodeBase64Url(id)}/reset`);
}

public getCookies(id: string): Observable<Cookie[]> {
Expand Down
6 changes: 3 additions & 3 deletions projects/aas-lib/src/lib/auth/auth.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
</div>
</a>
<div ngbDropdownMenu aria-labelledby="navbarLocalize" class="dropdown-menu dropdown-menu-end">
<a type="button" class="dropdown-item" *ngIf="!userAuthenticated" (click)="loginUser()" translate>CMD_LOGIN</a>
<a type="button" class="dropdown-item" *ngIf="!userAuthenticated" (click)="registerUser()"
<a type="button" class="dropdown-item" *ngIf="!userAuthenticated" (click)="login()" translate>CMD_LOGIN</a>
<a type="button" class="dropdown-item" *ngIf="!userAuthenticated" (click)="register()"
translate>CMD_REGISTER_USER</a>
<a type="button" class="dropdown-item" *ngIf="userAuthenticated" (click)="updateUserProfile()"
translate>CMD_UPDATE_PROFILE</a>
<a type="button" class="dropdown-item" *ngIf="userAuthenticated" (click)="logoutUser()" translate>CMD_LOGOUT</a>
<a type="button" class="dropdown-item" *ngIf="userAuthenticated" (click)="logout()" translate>CMD_LOGOUT</a>
</div>
</div>
32 changes: 8 additions & 24 deletions projects/aas-lib/src/lib/auth/auth.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,19 @@ export class AuthComponent {
return this.auth.name;
}

public async registerUser(): Promise<void> {
try {
await this.auth.registerAsync();
} catch (error) {
this.notify.error(error);
}
public register(): void {
this.auth.register().subscribe({ error: error => this.notify.error(error) });
}

public async loginUser(): Promise<void> {
try {
await this.auth.loginAsync();
} catch (error) {
this.notify.error(error);
}
public login(): void {
this.auth.login().subscribe({ error: error => this.notify.error(error) });
}

public async logoutUser(): Promise<void> {
try {
await this.auth.logoutAsync();
} catch (error) {
this.notify.error(error);
}
public logout(): void {
this.auth.logout().subscribe({ error: error => this.notify.error(error) });
}

public async updateUserProfile(): Promise<void> {
try {
await this.auth.updateUserProfileAsync();
} catch (error) {
this.notify.error(error);
}
public updateUserProfile(): void {
this.auth.updateUserProfile().subscribe({ error: error => this.notify.error(error) });
}
}
147 changes: 67 additions & 80 deletions projects/aas-lib/src/lib/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Injectable } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, EMPTY, last, map, mergeMap, Observable } from 'rxjs';
import { BehaviorSubject, from, last, map, mergeMap, Observable, of, throwError } from 'rxjs';
import jwtDecode from 'jwt-decode';
import {
ApplicationError,
Expand Down Expand Up @@ -93,132 +93,129 @@ export class AuthService {
* User login.
* @param credentials The credentials.
*/
public async loginAsync(credentials?: Credentials): Promise<void> {
public login(credentials?: Credentials): Observable<void> {
if (credentials) {
const result = await this.api.loginAsync(credentials);
this.nextPayload(result.token);
} else {
const modalRef = this.modal.open(LoginFormComponent, { backdrop: 'static', animation: true, keyboard: true });
const stayLoggedIn = toBoolean(this.window.getLocalStorageItem('.StayLoggedIn'));
const token = this.window.getLocalStorageItem('.Token');
if (stayLoggedIn && token) {
modalRef.componentInstance.stayLoggedIn = stayLoggedIn;
}
return this.api.login(credentials).pipe(map(result => this.nextPayload(result.token)));
}

return of(this.modal.open(LoginFormComponent, { backdrop: 'static', animation: true, keyboard: true })).pipe(
mergeMap(modalRef => {
const stayLoggedIn = toBoolean(this.window.getLocalStorageItem('.StayLoggedIn'));
const token = this.window.getLocalStorageItem('.Token');
if (stayLoggedIn && token) {
modalRef.componentInstance.stayLoggedIn = stayLoggedIn;
}

const result: LoginFormResult = await modalRef.result;
if (result != null) {
if (result.token) {
return from<Promise<LoginFormResult | undefined>>(modalRef.result);
}),
mergeMap(result => {
if (result?.token) {
this.nextPayload(result.token);
if (result.stayLoggedIn) {
this.window.setLocalStorageItem('.StayLoggedIn', 'true');
} else if (stayLoggedIn) {
} else if (toBoolean(this.window.getLocalStorageItem('.StayLoggedIn'))) {
this.window.removeLocalStorageItem('.StayLoggedIn');
}
} else if (result.action === 'register') {
await this.registerAsync();
} else if (result?.action === 'register') {
return this.register();
}
}
}

return of(void 0);
}));
}

/**
* Ensures that the current user has the expected rights.
* @param role The expected user role.
*/
public async ensureAuthorizedAsync(role: UserRole): Promise<void> {
if (!this.isAuthorized(role)) {
await this.loginAsync();
public ensureAuthorized(role: UserRole): Observable<void> {
if (this.isAuthorized(role)) {
return of(void 0);
}

return this.login().pipe(map(() => {
if (!this.isAuthorized(role)) {
throw new ApplicationError('Unauthorized access.', ERRORS.UNAUTHORIZED_ACCESS);
}
}
}));
}

/** Logs out the current user. */
public async logoutAsync(): Promise<void> {
public logout(): Observable<void> {
if (!this.userId) {
throw new Error('Invalid operation.');
return throwError(() => new Error('Invalid operation.'));
}

await this.loginAsGuestAsync();
return this.loginAsGuest();
}

/**
* Registers a new user.
* @param profile The profile of the new user.
*/
public async registerAsync(profile?: UserProfile): Promise<void> {
public register(profile?: UserProfile): Observable<void> {
if (profile) {
const result = await this.api.registerUserAsync(profile);
this.nextPayload(result.token);
} else {
const modalRef = this.modal.open(
RegisterFormComponent, {
backdrop: 'static',
animation: true,
keyboard: true
});
return this.api.register(profile).pipe(map(result => this.nextPayload(result.token)));
}

const result: RegisterFormResult = await modalRef.result;
if (result) {
this.nextPayload(result.token);
if (result.stayLoggedIn) {
this.window.setLocalStorageItem('.StayLoggedIn', 'true');
} else {
this.window.removeLocalStorageItem('.StayLoggedIn');
return of(this.modal.open(RegisterFormComponent, { backdrop: 'static', animation: true, keyboard: true })).pipe(
mergeMap(modalRef => from<Promise<RegisterFormResult | undefined>>(modalRef.result)),
map(result => {
if (result) {
this.nextPayload(result.token);
if (result.stayLoggedIn) {
this.window.setLocalStorageItem('.StayLoggedIn', 'true');
} else {
this.window.removeLocalStorageItem('.StayLoggedIn');
}
}
}
}
}));
}

/**
* Updates the profile of the current user.
* @param profile The updated user profile.
*/
public async updateUserProfileAsync(profile?: UserProfile): Promise<void> {
public updateUserProfile(profile?: UserProfile): Observable<void> {
const payload = this.payload$.getValue();
if (!payload || !payload.sub || !payload.name) {
throw new Error('Invalid operation.');
return throwError(() => new Error('Invalid operation.'));
}

if (profile) {
const result = await this.api.updateProfileAsync(payload.sub, profile);
this.nextPayload(result.token);
} else {
profile = { id: payload.sub, name: payload.name };
const form = this.modal.open(
ProfileFormComponent, {
backdrop: 'static',
animation: true,
keyboard: true
});
return this.api.updateProfile(payload.sub, profile).pipe(
map(result => this.nextPayload(result.token)));
}

form.componentInstance.initialize(profile);
const result: ProfileFormResult = await form.result;
if (result) {
if (result.token) {
return of(this.modal.open(ProfileFormComponent, { backdrop: 'static', animation: true, keyboard: true })).pipe(
mergeMap(form => {
form.componentInstance.initialize({ id: payload.sub, name: payload.name });
return from<Promise<ProfileFormResult>>(form.result)
}),
mergeMap(result => {
if (result?.token) {
this.nextPayload(result.token);
} else if (result.action === 'deleteUser') {
} else if (result?.action === 'deleteUser') {
const message = stringFormat(this.translate.instant('CMD_DELETE_USER'), this.userId);
if (this.window.confirm(message)) {
await this.deleteUserAsync();
return this.deleteUser();
}
}
}
}

return of(void 0);
}));
}

/**
* Deletes the account of the current authenticated user.
*/
public async deleteUserAsync(): Promise<void> {
public deleteUser(): Observable<void> {
const payload = this.payload$.getValue();
if (!payload || !payload.sub || !payload.name) {
throw new Error('Invalid operation');
}

await this.api.deleteUserAsync(payload.sub);
await this.loginAsGuestAsync();
return this.api.delete(payload.sub).pipe(mergeMap(() => this.loginAsGuest()));
}

/**
Expand Down Expand Up @@ -289,27 +286,17 @@ export class AuthService {
if (payload && payload.sub) {
const id = payload.sub;
return this.api.deleteCookie(id, name).pipe(
mergeMap(() => {
const values = this.api.getCookies(id);
return values;
}),
mergeMap(() => this.api.getCookies(id)),
map(cookies => {
this.cookies.clear();
cookies.forEach(cookie => this.cookies.set(cookie.name, cookie.data));
}));
} else {
this.window.removeLocalStorageItem(name);
return EMPTY;
return of(void 0);
}
}

private async loginAsGuestAsync(): Promise<void> {
this.window.removeLocalStorageItem('.Token');
this.window.removeLocalStorageItem('.StayLoggedIn');
const result = await this.api.guestAsync();
this.nextPayload(result.token);
}

private loginAsGuest(): Observable<void> {
this.window.removeLocalStorageItem('.Token');
this.window.removeLocalStorageItem('.StayLoggedIn');
Expand Down
Loading

0 comments on commit b5ed855

Please sign in to comment.