src/app/core/session/session-service/session-manager.service.ts
This service handles the user session. This includes an online and offline login and logout. After a successful login, the database for the current user is initialised.
Properties |
|
Methods |
constructor(remoteAuthService: KeycloakAuthService, localAuthService: LocalAuthService, sessionInfo: SessionSubject, currentUser: CurrentUserSubject, entityMapper: EntityMapperService, loginStateSubject: LoginStateSubject, router: Router, navigator: Navigator, configService: ConfigService, databaseResolver: DatabaseResolverService)
|
|||||||||||||||||||||||||||||||||
Parameters :
|
clearRemoteSessionIfNecessary |
clearRemoteSessionIfNecessary()
|
Returns :
any
|
getOfflineUsers |
getOfflineUsers()
|
Get a list of all users that can log in offline
Returns :
SessionInfo[]
|
Async logout |
logout()
|
If online, clear the remote session. If offline, reset the state and forward to login page.
Returns :
unknown
|
offlineLogin | ||||||
offlineLogin(user: SessionInfo)
|
||||||
Login an offline session without sync.
Parameters :
Returns :
any
|
remoteLoginAvailable |
remoteLoginAvailable()
|
Returns :
boolean
|
Readonly RESET_REMOTE_SESSION_KEY |
Type : string
|
Default value : "RESET_REMOTE"
|
import { Inject, Injectable } from "@angular/core";
import { SessionInfo, SessionSubject } from "../auth/session-info";
import { LoginStateSubject, SessionType } from "../session-type";
import { LoginState } from "../session-states/login-state.enum";
import { Router } from "@angular/router";
import { KeycloakAuthService } from "../auth/keycloak/keycloak-auth.service";
import { LocalAuthService } from "../auth/local/local-auth.service";
import { environment } from "../../../../environments/environment";
import { NAVIGATOR_TOKEN } from "../../../utils/di-tokens";
import { CurrentUserSubject } from "../current-user-subject";
import { EntityMapperService } from "../../entity/entity-mapper/entity-mapper.service";
import { filter, take } from "rxjs/operators";
import { Subscription } from "rxjs";
import { Entity } from "../../entity/model/entity";
import { ConfigService } from "../../config/config.service";
import { DatabaseResolverService } from "../../database/database-resolver.service";
/**
* This service handles the user session.
* This includes an online and offline login and logout.
* After a successful login, the database for the current user is initialised.
*/
@Injectable()
export class SessionManagerService {
readonly RESET_REMOTE_SESSION_KEY = "RESET_REMOTE";
private remoteLoggedIn = false;
private updateSubscription: Subscription;
constructor(
private remoteAuthService: KeycloakAuthService,
private localAuthService: LocalAuthService,
private sessionInfo: SessionSubject,
private currentUser: CurrentUserSubject,
private entityMapper: EntityMapperService,
private loginStateSubject: LoginStateSubject,
private router: Router,
@Inject(NAVIGATOR_TOKEN) private navigator: Navigator,
private configService: ConfigService,
private databaseResolver: DatabaseResolverService,
) {}
/**
* Login for a remote session and start the sync.
* After a user has logged in once online, this user can later also use the app offline.
* Should only be called if there is an internet connection
*/
async remoteLogin() {
this.loginStateSubject.next(LoginState.IN_PROGRESS);
if (this.remoteLoginAvailable()) {
return this.remoteAuthService
.login()
.then((user) => this.handleRemoteLogin(user))
.catch((err) => {
this.loginStateSubject.next(LoginState.LOGIN_FAILED);
// ignore fall back to offline login - if there was a technical error, the AuthService has already logged/reported it
});
}
this.loginStateSubject.next(LoginState.LOGIN_FAILED);
}
remoteLoginAvailable() {
return navigator.onLine && environment.session_type === SessionType.synced;
}
/**
* Login an offline session without sync.
* @param user
*/
offlineLogin(user: SessionInfo) {
return this.initializeUser(user);
}
private async initializeUser(session: SessionInfo) {
await this.databaseResolver.initDatabasesForSession(session);
this.sessionInfo.next(session);
this.loginStateSubject.next(LoginState.LOGGED_IN);
this.configService.configUpdates.pipe(take(1)).subscribe(() =>
// requires initial config to be loaded first!
this.initUserEntity(session.entityId),
);
}
private initUserEntity(entityId: string) {
if (!entityId) {
this.currentUser.next(null);
return;
}
const entityType = Entity.extractTypeFromId(entityId);
this.entityMapper
.load(entityType, entityId)
.catch(() => null) // see CurrentUserSubject: emits "null" for non-existing user entity
.then((res) => this.currentUser.next(res));
this.updateSubscription = this.entityMapper
.receiveUpdates(entityType)
.pipe(
filter(
({ entity }) =>
entity.getId() === entityId || entity.getId(true) === entityId,
),
)
.subscribe(({ entity }) => this.currentUser.next(entity));
}
/**
* Get a list of all users that can log in offline
*/
getOfflineUsers(): SessionInfo[] {
return this.localAuthService.getStoredUsers();
}
/**
* If online, clear the remote session.
* If offline, reset the state and forward to login page.
*/
async logout() {
if (this.remoteLoggedIn) {
if (this.navigator.onLine) {
// This will forward to the keycloak logout page
await this.remoteAuthService.logout();
} else {
localStorage.setItem(this.RESET_REMOTE_SESSION_KEY, "1");
}
}
// resetting app state
this.sessionInfo.next(undefined);
this.updateSubscription?.unsubscribe();
this.currentUser.next(undefined);
this.loginStateSubject.next(LoginState.LOGGED_OUT);
this.remoteLoggedIn = false;
await this.databaseResolver.resetDatabases();
return this.router.navigate(["/login"], {
queryParams: { redirect_uri: this.router.routerState.snapshot.url },
});
}
clearRemoteSessionIfNecessary() {
if (localStorage.getItem(this.RESET_REMOTE_SESSION_KEY)) {
localStorage.removeItem(this.RESET_REMOTE_SESSION_KEY);
return this.remoteAuthService.logout();
}
}
private async handleRemoteLogin(user: SessionInfo) {
this.remoteLoggedIn = true;
await this.initializeUser(user);
this.localAuthService.saveUser(user);
}
}