Skip to content

Commit

Permalink
feat(*): App automatically signs in if user is online and recently us…
Browse files Browse the repository at this point in the history
…ed the app (#1215)

fixes: #1047

Co-authored-by: Simon <therealslimv@yahoo.de>
Co-authored-by: Simon <33730997+TheSlimvReal@users.noreply.github.com>
Co-authored-by: Sebastian <sebastian.leidig@gmail.com>
  • Loading branch information
4 people authored Apr 30, 2022
1 parent 4087e94 commit e27f657
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 5 deletions.
10 changes: 7 additions & 3 deletions src/app/core/session/session-service/local-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ export class LocalSession extends SessionService {
const user: LocalUser = JSON.parse(window.localStorage.getItem(username));
if (user) {
if (passwordEqualsEncrypted(password, user.encryptedPassword)) {
this.currentDBUser = user;
await this.initializeDatabaseForCurrentUser();
this.loginState.next(LoginState.LOGGED_IN);
await this.handleSuccessfulLogin(user);
} else {
this.loginState.next(LoginState.LOGIN_FAILED);
}
Expand All @@ -64,6 +62,12 @@ export class LocalSession extends SessionService {
return this.loginState.value;
}

public async handleSuccessfulLogin(userObject: DatabaseUser) {
this.currentDBUser = userObject;
await this.initializeDatabaseForCurrentUser();
this.loginState.next(LoginState.LOGGED_IN);
}

private async initializeDatabaseForCurrentUser() {
const userDBName = `${this.currentDBUser.name}-${AppConfig.settings.database.name}`;
this.initDatabase(userDBName);
Expand Down
8 changes: 7 additions & 1 deletion src/app/core/session/session-service/remote-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ export class RemoteSession extends SessionService {
public async login(username: string, password: string): Promise<LoginState> {
try {
const response = await this.httpClient
.post(
.post<DatabaseUser>(
`${AppConfig.settings.database.remote_url}_session`,
{ name: username, password: password },
{ withCredentials: true }
)
.toPromise();
await this.handleSuccessfulLogin(response);
this.assignDatabaseUser(response);
localStorage.setItem(
RemoteSession.LAST_LOGIN_KEY,
Expand All @@ -92,6 +93,11 @@ export class RemoteSession extends SessionService {
};
}

public async handleSuccessfulLogin(userObject: DatabaseUser) {
this.currentDBUser = userObject;
this.loginState.next(LoginState.LOGGED_IN);
}

/**
* Logout at the remote database.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/app/core/session/session-service/session.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { LoginState } from "../session-states/login-state.enum";
import { SessionService } from "./session.service";
import { SyncState } from "../session-states/sync-state.enum";
import { TEST_PASSWORD, TEST_USER } from "../../../utils/mocked-testing.module";
import { DatabaseUser } from "./local-user";

/**
* Default tests for testing basic functionality of any SessionService implementation.
Expand Down Expand Up @@ -85,6 +86,16 @@ export function testSessionServiceImplementation(
expectNotToBeLoggedIn(LoginState.LOGGED_OUT);
});

it("it correctly handles the necessary steps after a successful login", async () => {
const dummyUser: DatabaseUser = {
name: "Hanspeter",
roles: ["user_app"],
};
await sessionService.handleSuccessfulLogin(dummyUser);
expect(sessionService.loginState.value).toEqual(LoginState.LOGGED_IN);
expect(sessionService.getCurrentUser()).toEqual(dummyUser);
});

/**
* Check all states of the session to be "logged out".
* @param expectedLoginState The expected LoginState (failed or simply logged out)
Expand Down
7 changes: 7 additions & 0 deletions src/app/core/session/session-service/session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export abstract class SessionService {
*/
abstract login(username: string, password: string): Promise<LoginState>;

/**
* Do the necessary steps after the login has been successful.
* i.e. set the current user and change the login state
* @param userObject the user that is successfully loged in
*/
abstract handleSuccessfulLogin(userObject: DatabaseUser): Promise<void>;

/**
* Logout the current user.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ describe("SyncedSessionService", () => {
let mockHttpClient: jasmine.SpyObj<HttpClient>;

beforeEach(() => {
mockHttpClient = jasmine.createSpyObj(["post", "delete"]);
mockHttpClient = jasmine.createSpyObj(["post", "delete", "get"]);
mockHttpClient.delete.and.returnValue(of());
mockHttpClient.get.and.returnValue(of());
TestBed.configureTestingModule({
imports: [
MatSnackBarModule,
Expand Down Expand Up @@ -266,6 +267,41 @@ describe("SyncedSessionService", () => {
tick();
}));

it("should login, given that CouchDB cookie is still valid", fakeAsync(() => {
const responseObject = {
ok: true,
userCtx: {
name: "demo",
roles: ["user_app"],
},
info: {
authentication_handlers: ["cookie", "default"],
authenticated: "default",
},
};
mockHttpClient.get.and.returnValue(of(responseObject));
sessionService.checkForValidSession();
tick();
expect(sessionService.loginState.value).toEqual(LoginState.LOGGED_IN);
}));

it("should not login, given that there is no valid CouchDB cookie", fakeAsync(() => {
const responseObject = {
ok: true,
userCtx: {
name: null,
roles: [],
},
info: {
authentication_handlers: ["cookie", "default"],
},
};
mockHttpClient.get.and.returnValue(of(responseObject));
sessionService.checkForValidSession();
tick();
expect(sessionService.loginState.value).toEqual(LoginState.LOGGED_OUT);
}));

testSessionServiceImplementation(() => Promise.resolve(sessionService));

function passRemoteLogin(response: DatabaseUser = { name: "", roles: [] }) {
Expand Down
24 changes: 24 additions & 0 deletions src/app/core/session/session-service/synced-session.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { DatabaseUser } from "./local-user";
import { waitForChangeTo } from "../session-states/session-utils";
import { PouchDatabase } from "../../database/pouch-database";
import { zip } from "rxjs";
import { AppConfig } from "app/core/app-config/app-config";
import { filter } from "rxjs/operators";

/**
Expand Down Expand Up @@ -68,6 +69,29 @@ export class SyncedSessionService extends SessionService {
new Date().toISOString()
)
);
this.checkForValidSession();
}

/**
* Do login automatically if there is still a valid CouchDB cookie from last login with username and password
*/
checkForValidSession() {
this.httpClient
.get(`${AppConfig.settings.database.remote_url}_session`, {
withCredentials: true,
})
.subscribe((res: any) => {
if (res.userCtx.name) {
this.handleSuccessfulLogin(res.userCtx);
}
});
}

async handleSuccessfulLogin(userObject: DatabaseUser) {
this.startSyncAfterLocalAndRemoteLogin();
await this._remoteSession.handleSuccessfulLogin(userObject);
await this._localSession.handleSuccessfulLogin(userObject);
this.loginState.next(LoginState.LOGGED_IN);
}

/**
Expand Down

0 comments on commit e27f657

Please sign in to comment.