Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
security.service.ts 5.23 KiB
import {Injectable} from '@angular/core';
import {Observable, ReplaySubject} from 'rxjs';
import {User} from './user.model';
import {SecurityEventService} from './security-event.service';
import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
import {SmpConstants} from "../smp.constants";
import {Authority} from "./authority.model";
import {AlertMessageService} from "../common/alert-message/alert-message.service";
import {PasswordChangeDialogComponent} from "../common/dialogs/password-change-dialog/password-change-dialog.component";
import {MatDialog} from "@angular/material/dialog";

@Injectable()
export class SecurityService {

  readonly LOCAL_STORAGE_KEY_CURRENT_USER = 'currentUser';



  constructor(
    private http: HttpClient,
    private alertService: AlertMessageService,
    private securityEventService: SecurityEventService,
    private dialog: MatDialog,
  ) {
    this.securityEventService.onLogoutSuccessEvent().subscribe(() => window.location.reload());
    this.securityEventService.onLogoutErrorEvent().subscribe((error) => this.alertService.error(error));
  }

  login(username: string, password: string) {
    let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'});
    return this.http.post<User>(SmpConstants.REST_PUBLIC_SECURITY_AUTHENTICATION,
      JSON.stringify({
        username: username,
        password: password
      }),
      {headers})
      .subscribe((response: User) => {
          this.updateUserDetails(response);
          this.securityEventService.notifyLoginSuccessEvent(response);
        },
        (error: any) => {
          this.securityEventService.notifyLoginErrorEvent(error);
        });
  }

  refreshLoggedUserFromServer() {
    this.getCurrentUsernameFromServer().subscribe((userDetails: User) => {
      this.updateUserDetails(userDetails);
      this.securityEventService.notifyLoginSuccessEvent(userDetails);
      if (userDetails?.forceChangeExpiredPassword) {
        this.dialog.open(PasswordChangeDialogComponent, {
          data: {
            user: userDetails,
            adminUser: false
          }
        }).afterClosed().subscribe(res =>
          this.finalizeLogout(res)
        );
      }
    }, (error: any) => {
      // just clean local storage
      this.clearLocalStorage();
    });
  }

  logout() {
    this.http.delete(SmpConstants.REST_PUBLIC_SECURITY_AUTHENTICATION).subscribe((res: Response) => {
        this.finalizeLogout(res);
      },
      (error) => {
        this.securityEventService.notifyLogoutErrorEvent(error);
      });
  }

  finalizeLogout(res) {
    this.clearLocalStorage();
    this.securityEventService.notifyLogoutSuccessEvent(res);
  }


  getCurrentUser(): User {
    return JSON.parse(this.readLocalStorage());
  }

  private getCurrentUsernameFromServer(): Observable<User> {
    let subject = new ReplaySubject<User>();
    this.http.get<User>(SmpConstants.REST_PUBLIC_SECURITY_USER)
      .subscribe((res: User) => {
        subject.next(res);
      }, (error: any) => {
        subject.next(null);
      });
    return subject.asObservable();
  }

  isAuthenticated(callServer: boolean = false): Observable<boolean> {
    let subject = new ReplaySubject<boolean>();
    if (callServer) {
      //we get the username from the server to trigger the redirection to the login screen in case the user is not authenticated
      this.getCurrentUsernameFromServer().subscribe((user: User) => {
        if (!user) {
          this.clearLocalStorage();
        }
        subject.next(user !== null);
      }, (user: string) => {
        subject.next(false);
      });

    } else {
      let currentUser = this.getCurrentUser();
      subject.next(currentUser !== null);
    }
    return subject.asObservable();
  }

  isCurrentUserSystemAdmin(): boolean {
    return this.isCurrentUserInRole([Authority.SYSTEM_ADMIN]);
  }




  isCurrentUserSMPAdmin(): boolean {
    return this.isCurrentUserInRole([Authority.SMP_ADMIN]);
  }

  isCurrentUserServiceGroupAdmin(): boolean {
    return this.isCurrentUserInRole([Authority.SERVICE_GROUP_ADMIN]);
  }

  isCurrentUserInRole(roles: Array<Authority>): boolean {
    let hasRole = false;
    const currentUser = this.getCurrentUser();
    if (currentUser && currentUser.authorities) {
      roles.forEach((role: Authority) => {
        if (currentUser.authorities.indexOf(role) !== -1) {
          hasRole = true;
        }
      });
    }
    return hasRole;
  }

  isAuthorized(roles: Array<Authority>): Observable<boolean> {
    let subject = new ReplaySubject<boolean>();

    this.isAuthenticated(false).subscribe((isAuthenticated: boolean) => {
      if (isAuthenticated && roles) {
        let hasRole = this.isCurrentUserInRole(roles);
        subject.next(hasRole);
      }
    });
    return subject.asObservable();
  }

  updateUserDetails(userDetails: User) {
    // store user data to local storage!
    this.populateLocalStorage(JSON.stringify(userDetails));
  }

  private populateLocalStorage(userDetails: string) {
    localStorage.setItem(this.LOCAL_STORAGE_KEY_CURRENT_USER, userDetails);
  }

  private readLocalStorage(): string {
    return localStorage.getItem(this.LOCAL_STORAGE_KEY_CURRENT_USER);
  }

  private clearLocalStorage() {
    localStorage.removeItem(this.LOCAL_STORAGE_KEY_CURRENT_USER);
  }

}