Merge pull request #4471 from FinnStutzenstein/permissionsInWhoAmI
rework login system (again)
This commit is contained in:
commit
3ac7788fe8
@ -28,6 +28,10 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
||||
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||
const basePerm: string | string[] = route.data.basePerm;
|
||||
|
||||
console.log('Auth guard');
|
||||
console.log('motions.can_see:', this.operator.hasPerms('motions.can_see'));
|
||||
console.log('motions.can_manage:', this.operator.hasPerms('motions.can_manage'));
|
||||
|
||||
if (!basePerm) {
|
||||
return true;
|
||||
} else if (basePerm instanceof Array) {
|
||||
|
@ -1,21 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { OperatorService, WhoAmI } from 'app/core/core-services/operator.service';
|
||||
import { environment } from 'environments/environment';
|
||||
import { User } from '../../shared/models/users/user';
|
||||
import { OpenSlidesService } from './openslides.service';
|
||||
import { HttpService } from './http.service';
|
||||
import { DataStoreService } from './data-store.service';
|
||||
|
||||
/**
|
||||
* The data returned by a post request to the login route.
|
||||
*/
|
||||
interface LoginResponse {
|
||||
user_id: number;
|
||||
user: User;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates an OpenSlides user with username and password
|
||||
*/
|
||||
@ -49,31 +40,57 @@ export class AuthService {
|
||||
* @param password
|
||||
* @returns The login response.
|
||||
*/
|
||||
public async login(username: string, password: string): Promise<LoginResponse> {
|
||||
public async login(username: string, password: string, earlySuccessCallback: () => void): Promise<WhoAmI> {
|
||||
const user = {
|
||||
username: username,
|
||||
password: password
|
||||
};
|
||||
const response = await this.http.post<LoginResponse>(environment.urlPrefix + '/users/login/', user);
|
||||
const response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/login/', user);
|
||||
earlySuccessCallback();
|
||||
await this.operator.setWhoAmI(response);
|
||||
await this.redirectUser(response.user_id);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the user to the page where he came from. Boots OpenSlides,
|
||||
* if it wasn't done before.
|
||||
*/
|
||||
public async redirectUser(userId: number): Promise<void> {
|
||||
if (!this.OpenSlides.booted) {
|
||||
await this.OpenSlides.afterLoginBootup(userId);
|
||||
}
|
||||
|
||||
let redirect = this.OpenSlides.redirectUrl ? this.OpenSlides.redirectUrl : '/';
|
||||
if (redirect.includes('login')) {
|
||||
redirect = '/';
|
||||
}
|
||||
this.router.navigate([redirect]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login for guests.
|
||||
*/
|
||||
public async guestLogin(): Promise<void> {
|
||||
this.redirectUser(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout function for both the client and the server.
|
||||
*
|
||||
* Will clear the current operator and datastore and
|
||||
* Will clear the datastore, update the current operator and
|
||||
* send a `post`-request to `/apps/users/logout/'`. Restarts OpenSlides.
|
||||
*/
|
||||
public async logout(): Promise<void> {
|
||||
await this.operator.setUser(null);
|
||||
let response = null;
|
||||
try {
|
||||
await this.http.post(environment.urlPrefix + '/users/logout/', {});
|
||||
response = await this.http.post<WhoAmI>(environment.urlPrefix + '/users/logout/', {});
|
||||
} catch (e) {
|
||||
// We do nothing on failures. Reboot OpenSlides anyway.
|
||||
}
|
||||
// Clear the DataStore
|
||||
this.DS.clear();
|
||||
await this.DS.clear();
|
||||
await this.operator.setWhoAmI(response);
|
||||
await this.OpenSlides.reboot();
|
||||
this.router.navigate(['/']);
|
||||
this.OpenSlides.reboot();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { WebsocketService } from './websocket.service';
|
||||
import { OperatorService, WhoAmIResponse } from './operator.service';
|
||||
import { OperatorService } from './operator.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import { AutoupdateService } from './autoupdate.service';
|
||||
import { DataStoreService } from './data-store.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* Handles the bootup/showdown of this application.
|
||||
@ -16,10 +17,20 @@ import { take } from 'rxjs/operators';
|
||||
})
|
||||
export class OpenSlidesService {
|
||||
/**
|
||||
* if the user tries to access a certain URL without being authenticated, the URL will be stored here
|
||||
* If the user tries to access a certain URL without being authenticated, the URL will be stored here
|
||||
*/
|
||||
public redirectUrl: string;
|
||||
|
||||
/**
|
||||
* Saves, if OpenSlides is fully booted. This means, that a user must be logged in
|
||||
* (Anonymous is also a user in this case). This is the case after `afterLoginBootup`.
|
||||
*/
|
||||
private _booted = false;
|
||||
|
||||
public get booted(): boolean {
|
||||
return this._booted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to create the OpenSlidesService. Registers itself to the WebsocketService.
|
||||
* @param storageService
|
||||
@ -53,10 +64,6 @@ export class OpenSlidesService {
|
||||
public async bootup(): Promise<void> {
|
||||
// start autoupdate if the user is logged in:
|
||||
const response = await this.operator.whoAmIFromStorage();
|
||||
await this.bootupWithWhoAmI(response);
|
||||
}
|
||||
|
||||
private async bootupWithWhoAmI(response: WhoAmIResponse): Promise<void> {
|
||||
if (!response.user && !response.guest_enabled) {
|
||||
this.redirectUrl = location.pathname;
|
||||
|
||||
@ -85,9 +92,10 @@ export class OpenSlidesService {
|
||||
* the login bootup-sequence: Check (and maybe clear) the cache und setup the DataStore
|
||||
* and websocket. This "login" also may be the "login" of an anonymous when he is using
|
||||
* OpenSlides as a guest.
|
||||
* @param userId
|
||||
* @param userId the id or null for guest
|
||||
*/
|
||||
public async afterLoginBootup(userId: number): Promise<void> {
|
||||
public async afterLoginBootup(userId: number | null): Promise<void> {
|
||||
console.log('user id', userId);
|
||||
// Check, which user was logged in last time
|
||||
const lastUserId = await this.storageService.get<number>('lastUserLoggedIn');
|
||||
// if the user changed, reset the cache and save the new user.
|
||||
@ -96,6 +104,8 @@ export class OpenSlidesService {
|
||||
await this.storageService.set('lastUserLoggedIn', userId);
|
||||
}
|
||||
await this.setupDataStoreAndWebSocket();
|
||||
// Now finally booted.
|
||||
this._booted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,16 +131,16 @@ export class OpenSlidesService {
|
||||
/**
|
||||
* Shuts OpenSlides down. The websocket is closed and the operator is not set.
|
||||
*/
|
||||
public async shutdown(): Promise<void> {
|
||||
public shutdown(): void {
|
||||
this.websocketService.close();
|
||||
await this.operator.setUser(null);
|
||||
this._booted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown and bootup.
|
||||
*/
|
||||
public async reboot(): Promise<void> {
|
||||
await this.shutdown();
|
||||
this.shutdown();
|
||||
await this.bootup();
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { UserRepositoryService } from '../repositories/users/user-repository.ser
|
||||
import { CollectionStringMapperService } from './collectionStringMapper.service';
|
||||
import { StorageService } from './storage.service';
|
||||
import { HttpService } from './http.service';
|
||||
import { filter, auditTime } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* Permissions on the client are just strings. This makes clear, that
|
||||
@ -23,18 +24,24 @@ export type Permission = string;
|
||||
/**
|
||||
* Response format of the WhoAmI request.
|
||||
*/
|
||||
export interface WhoAmIResponse {
|
||||
export interface WhoAmI {
|
||||
user_id: number;
|
||||
guest_enabled: boolean;
|
||||
user: User;
|
||||
permissions: Permission[];
|
||||
}
|
||||
|
||||
function isWhoAmIResponse(obj: any): obj is WhoAmIResponse {
|
||||
function isWhoAmI(obj: any): obj is WhoAmI {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
const whoAmI = obj as WhoAmIResponse;
|
||||
return whoAmI.guest_enabled !== undefined && whoAmI.user !== undefined && whoAmI.user_id !== undefined;
|
||||
const whoAmI = obj as WhoAmI;
|
||||
return (
|
||||
whoAmI.guest_enabled !== undefined &&
|
||||
whoAmI.user !== undefined &&
|
||||
whoAmI.user_id !== undefined &&
|
||||
whoAmI.permissions !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
const WHOAMI_STORAGE_KEY = 'whoami';
|
||||
@ -80,7 +87,7 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
* Save, if guests are enabled.
|
||||
*/
|
||||
public get guestsEnabled(): boolean {
|
||||
return this.currentWhoAmIResponse ? this.currentWhoAmIResponse.guest_enabled : false;
|
||||
return this.currentWhoAmI ? this.currentWhoAmI.guest_enabled : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +113,7 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
/**
|
||||
* The current WhoAmI response to extract the user (the operator) from.
|
||||
*/
|
||||
private currentWhoAmIResponse: WhoAmIResponse | null;
|
||||
private currentWhoAmI: WhoAmI | null;
|
||||
|
||||
/**
|
||||
* Sets up an observer for watching changes in the DS. If the operator user or groups are changed,
|
||||
@ -124,43 +131,51 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
private storageService: StorageService
|
||||
) {
|
||||
this.DS.changeObservable.subscribe(newModel => {
|
||||
if (this._user) {
|
||||
if (newModel instanceof Group) {
|
||||
this.updatePermissions();
|
||||
}
|
||||
|
||||
if (newModel instanceof User && this._user.id === newModel.id) {
|
||||
this.updateUser(newModel);
|
||||
}
|
||||
} else if (newModel instanceof Group && newModel.id === 1) {
|
||||
// Group 1 (default) for anonymous changed
|
||||
this.updatePermissions();
|
||||
if (this._user && newModel instanceof User && this._user.id === newModel.id) {
|
||||
this._user = newModel;
|
||||
this.updateUserInCurrentWhoAmI();
|
||||
}
|
||||
});
|
||||
this.DS.changeObservable
|
||||
.pipe(
|
||||
filter(
|
||||
model =>
|
||||
// Any group has changed if we have an operator or
|
||||
// group 1 (default) for anonymous changed
|
||||
model instanceof Group && (!!this._user || model.id === 1)
|
||||
),
|
||||
auditTime(10)
|
||||
)
|
||||
.subscribe(newModel => this.updatePermissions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current WHoAmI response from the storage.
|
||||
* Returns a default WhoAmI response
|
||||
*/
|
||||
public async whoAmIFromStorage(): Promise<WhoAmIResponse> {
|
||||
const defaultResponse = {
|
||||
private getDefaultWhoAmIResponse(): WhoAmI {
|
||||
return {
|
||||
user_id: null,
|
||||
guest_enabled: false,
|
||||
user: null
|
||||
user: null,
|
||||
permissions: []
|
||||
};
|
||||
let response: WhoAmIResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current WhoAmI response from the storage.
|
||||
*/
|
||||
public async whoAmIFromStorage(): Promise<WhoAmI> {
|
||||
let response: WhoAmI;
|
||||
try {
|
||||
response = await this.storageService.get<WhoAmIResponse>(WHOAMI_STORAGE_KEY);
|
||||
if (response) {
|
||||
this.processWhoAmIResponse(response);
|
||||
} else {
|
||||
response = defaultResponse;
|
||||
response = await this.storageService.get<WhoAmI>(WHOAMI_STORAGE_KEY);
|
||||
if (!response) {
|
||||
response = this.getDefaultWhoAmIResponse();
|
||||
}
|
||||
} catch (e) {
|
||||
response = defaultResponse;
|
||||
response = this.getDefaultWhoAmIResponse();
|
||||
}
|
||||
this.currentWhoAmIResponse = response;
|
||||
return this.currentWhoAmIResponse;
|
||||
await this.updateCurrentWhoAmI(response);
|
||||
return this.currentWhoAmI;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,27 +192,11 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
* Sets the operator user. Will be saved to storage
|
||||
* @param user The new operator.
|
||||
*/
|
||||
public async setUser(user: User): Promise<void> {
|
||||
await this.updateUser(user, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user and update the permissions.
|
||||
*
|
||||
* @param user The user to set.
|
||||
* @param saveToStoare Whether to save the user to the storage WhoAmI.
|
||||
*/
|
||||
private async updateUser(user: User | null, saveToStorage: boolean = false): Promise<void> {
|
||||
this._user = user;
|
||||
if (saveToStorage) {
|
||||
await this.saveUserToStorate();
|
||||
public async setWhoAmI(whoami: WhoAmI | null): Promise<void> {
|
||||
if (whoami === null) {
|
||||
whoami = this.getDefaultWhoAmIResponse();
|
||||
}
|
||||
if (user && this.userRepository) {
|
||||
this._viewUser = this.userRepository.getViewModel(user.id);
|
||||
} else {
|
||||
this._viewUser = null;
|
||||
}
|
||||
this.updatePermissions();
|
||||
await this.updateCurrentWhoAmI(whoami);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,51 +204,56 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
*
|
||||
* @returns The response of the WhoAmI request.
|
||||
*/
|
||||
public async whoAmI(): Promise<WhoAmIResponse> {
|
||||
public async whoAmI(): Promise<WhoAmI> {
|
||||
try {
|
||||
const response = await this.http.get(environment.urlPrefix + '/users/whoami/');
|
||||
if (isWhoAmIResponse(response)) {
|
||||
this.processWhoAmIResponse(response);
|
||||
await this.storageService.set(WHOAMI_STORAGE_KEY, response);
|
||||
this.currentWhoAmIResponse = response;
|
||||
if (isWhoAmI(response)) {
|
||||
await this.updateCurrentWhoAmI(response);
|
||||
} else {
|
||||
this.offlineService.goOfflineBecauseFailedWhoAmI();
|
||||
}
|
||||
} catch (e) {
|
||||
this.offlineService.goOfflineBecauseFailedWhoAmI();
|
||||
}
|
||||
return this.currentWhoAmIResponse;
|
||||
return this.currentWhoAmI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the user to storage by wrapping it into a (maybe existing)
|
||||
* WhoAMI response.
|
||||
*/
|
||||
private async saveUserToStorate(): Promise<void> {
|
||||
if (!this.currentWhoAmIResponse) {
|
||||
this.currentWhoAmIResponse = {
|
||||
user_id: null,
|
||||
guest_enabled: false,
|
||||
user: null
|
||||
};
|
||||
private async updateUserInCurrentWhoAmI(): Promise<void> {
|
||||
if (!this.currentWhoAmI) {
|
||||
this.currentWhoAmI = this.getDefaultWhoAmIResponse();
|
||||
}
|
||||
if (this.user) {
|
||||
this.currentWhoAmIResponse.user_id = this.user.id;
|
||||
this.currentWhoAmIResponse.user = this.user;
|
||||
if (this.isAnonymous) {
|
||||
this.currentWhoAmI.user_id = null;
|
||||
this.currentWhoAmI.user = null;
|
||||
} else {
|
||||
this.currentWhoAmIResponse.user_id = null;
|
||||
this.currentWhoAmIResponse.user = null;
|
||||
this.currentWhoAmI.user_id = this.user.id;
|
||||
this.currentWhoAmI.user = this.user;
|
||||
}
|
||||
await this.storageService.set(WHOAMI_STORAGE_KEY, this.currentWhoAmIResponse);
|
||||
this.currentWhoAmI.permissions = this.permissions;
|
||||
await this.updateCurrentWhoAmI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a WhoAmI response and set the user appropriately.
|
||||
*
|
||||
* @param response The WhoAMI response
|
||||
* Updates the user and update the permissions.
|
||||
*/
|
||||
private processWhoAmIResponse(response: WhoAmIResponse): void {
|
||||
this.updateUser(response.user ? new User(response.user) : null);
|
||||
private async updateCurrentWhoAmI(whoami?: WhoAmI): Promise<void> {
|
||||
if (whoami) {
|
||||
this.currentWhoAmI = whoami;
|
||||
} else {
|
||||
whoami = this.currentWhoAmI;
|
||||
}
|
||||
|
||||
this._user = whoami ? whoami.user : null;
|
||||
if (this._user && this.userRepository) {
|
||||
this._viewUser = this.userRepository.getViewModel(this._user.id);
|
||||
} else {
|
||||
this._viewUser = null;
|
||||
}
|
||||
await this.updatePermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,24 +310,42 @@ export class OperatorService implements OnAfterAppsLoaded {
|
||||
|
||||
/**
|
||||
* Update the operators permissions and publish the operator afterwards.
|
||||
* Saves the current WhoAmI to storage with the updated permissions
|
||||
*/
|
||||
private updatePermissions(): void {
|
||||
private async updatePermissions(): Promise<void> {
|
||||
this.permissions = [];
|
||||
// Anonymous or users in the default group.
|
||||
if (!this.user || this.user.groups_id.length === 0) {
|
||||
const defaultGroup = this.DS.get<Group>('users/group', 1);
|
||||
if (defaultGroup && defaultGroup.permissions instanceof Array) {
|
||||
this.permissions = defaultGroup.permissions;
|
||||
|
||||
// If we do not have any groups, take the permissions from the
|
||||
// latest WhoAmI response.
|
||||
if (this.DS.getAll(Group).length === 0) {
|
||||
if (this.currentWhoAmI) {
|
||||
this.permissions = this.currentWhoAmI.permissions;
|
||||
}
|
||||
} else {
|
||||
const permissionSet = new Set();
|
||||
this.DS.getMany(Group, this.user.groups_id).forEach(group => {
|
||||
group.permissions.forEach(permission => {
|
||||
permissionSet.add(permission);
|
||||
// Anonymous or users in the default group.
|
||||
if (!this.user || this.user.groups_id.length === 0) {
|
||||
const defaultGroup = this.DS.get<Group>('users/group', 1);
|
||||
if (defaultGroup && defaultGroup.permissions instanceof Array) {
|
||||
this.permissions = defaultGroup.permissions;
|
||||
}
|
||||
} else {
|
||||
const permissionSet = new Set();
|
||||
this.DS.getMany(Group, this.user.groups_id).forEach(group => {
|
||||
group.permissions.forEach(permission => {
|
||||
permissionSet.add(permission);
|
||||
});
|
||||
});
|
||||
});
|
||||
this.permissions = Array.from(permissionSet.values());
|
||||
this.permissions = Array.from(permissionSet.values());
|
||||
}
|
||||
}
|
||||
|
||||
// Save perms to current WhoAmI
|
||||
if (!this.currentWhoAmI) {
|
||||
this.currentWhoAmI = this.getDefaultWhoAmIResponse();
|
||||
}
|
||||
this.currentWhoAmI.permissions = this.permissions;
|
||||
await this.storageService.set(WHOAMI_STORAGE_KEY, this.currentWhoAmI);
|
||||
|
||||
// publish changes in the operator.
|
||||
this.operatorSubject.next(this.user);
|
||||
this.viewOperatorSubject.next(this.viewUser);
|
||||
|
@ -9,11 +9,9 @@ import { BaseComponent } from 'app/base.component';
|
||||
import { AuthService } from 'app/core/core-services/auth.service';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { environment } from 'environments/environment';
|
||||
import { OpenSlidesService } from 'app/core/core-services/openslides.service';
|
||||
import { LoginDataService, LoginData } from 'app/core/ui-services/login-data.service';
|
||||
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
||||
import { HttpService } from 'app/core/core-services/http.service';
|
||||
import { User } from 'app/shared/models/users/user';
|
||||
|
||||
interface LoginDataWithInfoText extends LoginData {
|
||||
info_text?: string;
|
||||
@ -81,7 +79,6 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
||||
private route: ActivatedRoute,
|
||||
private formBuilder: FormBuilder,
|
||||
private httpService: HttpService,
|
||||
private OpenSlides: OpenSlidesService,
|
||||
private loginDataService: LoginDataService
|
||||
) {
|
||||
super();
|
||||
@ -112,8 +109,7 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
||||
this.operatorSubscription = this.operator.getUserObservable().subscribe(user => {
|
||||
if (user) {
|
||||
this.clearOperatorSubscription();
|
||||
this.redirectUser();
|
||||
this.OpenSlides.afterLoginBootup(user.id);
|
||||
this.authService.redirectUser(user.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -154,30 +150,16 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
||||
this.loginErrorMsg = '';
|
||||
this.inProcess = true;
|
||||
try {
|
||||
const response = await this.authService.login(this.loginForm.value.username, this.loginForm.value.password);
|
||||
this.clearOperatorSubscription(); // We take control, not the subscription.
|
||||
this.inProcess = false;
|
||||
await this.operator.setUser(new User(response.user));
|
||||
this.OpenSlides.afterLoginBootup(response.user_id);
|
||||
this.redirectUser();
|
||||
await this.authService.login(this.loginForm.value.username, this.loginForm.value.password, () => {
|
||||
this.clearOperatorSubscription(); // We take control, not the subscription.
|
||||
});
|
||||
} catch (e) {
|
||||
this.loginForm.setErrors({
|
||||
notFound: true
|
||||
});
|
||||
this.loginErrorMsg = e;
|
||||
this.inProcess = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the user to the page where he came from.
|
||||
*/
|
||||
private redirectUser(): void {
|
||||
let redirect = this.OpenSlides.redirectUrl ? this.OpenSlides.redirectUrl : '/';
|
||||
if (redirect.includes('login')) {
|
||||
redirect = '/';
|
||||
}
|
||||
this.router.navigate([redirect]);
|
||||
this.inProcess = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,6 +180,6 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
||||
* Guests (if enabled) can navigate directly to the main page.
|
||||
*/
|
||||
public guestLogin(): void {
|
||||
this.router.navigate(['/']);
|
||||
this.authService.guestLogin();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import smtplib
|
||||
import textwrap
|
||||
from typing import List
|
||||
from typing import List, Set
|
||||
|
||||
from asgiref.sync import async_to_sync
|
||||
from django.conf import settings
|
||||
@ -471,7 +471,46 @@ class PersonalNoteViewSet(ModelViewSet):
|
||||
# Special API views
|
||||
|
||||
|
||||
class UserLoginView(APIView):
|
||||
class WhoAmIDataView(APIView):
|
||||
def get_whoami_data(self):
|
||||
"""
|
||||
Appends the user id to the context. Uses None for the anonymous
|
||||
user. Appends also a flag if guest users are enabled in the config.
|
||||
Appends also the serialized user if available.
|
||||
"""
|
||||
user_id = self.request.user.pk or 0
|
||||
guest_enabled = anonymous_is_enabled()
|
||||
|
||||
if user_id:
|
||||
user_data = async_to_sync(element_cache.get_element_restricted_data)(
|
||||
user_id, self.request.user.get_collection_string(), user_id
|
||||
)
|
||||
group_ids = user_data["groups_id"] or [GROUP_DEFAULT_PK]
|
||||
else:
|
||||
user_data = None
|
||||
group_ids = [GROUP_DEFAULT_PK] if guest_enabled else []
|
||||
|
||||
# collect all permissions
|
||||
permissions: Set[str] = set()
|
||||
group_all_data = async_to_sync(element_cache.get_all_full_data_ordered)()[
|
||||
"users/group"
|
||||
]
|
||||
for group_id in group_ids:
|
||||
permissions.update(group_all_data[group_id]["permissions"])
|
||||
|
||||
return {
|
||||
"user_id": user_id or None,
|
||||
"guest_enabled": guest_enabled,
|
||||
"user": user_data,
|
||||
"permissions": list(permissions),
|
||||
}
|
||||
|
||||
def get_context_data(self, **context):
|
||||
context.update(self.get_whoami_data())
|
||||
return super().get_context_data(**context)
|
||||
|
||||
|
||||
class UserLoginView(WhoAmIDataView):
|
||||
"""
|
||||
Login the user.
|
||||
"""
|
||||
@ -525,14 +564,11 @@ class UserLoginView(APIView):
|
||||
context["theme"] = config["openslides_theme"]
|
||||
else:
|
||||
# self.request.method == 'POST'
|
||||
context["user_id"] = self.user.pk
|
||||
context["user"] = async_to_sync(element_cache.get_element_restricted_data)(
|
||||
self.user.pk or 0, self.user.get_collection_string(), self.user.pk
|
||||
)
|
||||
context.update(self.get_whoami_data())
|
||||
return super().get_context_data(**context)
|
||||
|
||||
|
||||
class UserLogoutView(APIView):
|
||||
class UserLogoutView(WhoAmIDataView):
|
||||
"""
|
||||
Logout the user.
|
||||
"""
|
||||
@ -546,33 +582,13 @@ class UserLogoutView(APIView):
|
||||
return super().post(*args, **kwargs)
|
||||
|
||||
|
||||
class WhoAmIView(APIView):
|
||||
class WhoAmIView(WhoAmIDataView):
|
||||
"""
|
||||
Returns the id of the requesting user.
|
||||
"""
|
||||
|
||||
http_method_names = ["get"]
|
||||
|
||||
def get_context_data(self, **context):
|
||||
"""
|
||||
Appends the user id to the context. Uses None for the anonymous
|
||||
user. Appends also a flag if guest users are enabled in the config.
|
||||
Appends also the serialized user if available.
|
||||
"""
|
||||
user_id = self.request.user.pk or 0
|
||||
if user_id:
|
||||
user_data = async_to_sync(element_cache.get_element_restricted_data)(
|
||||
user_id, self.request.user.get_collection_string(), user_id
|
||||
)
|
||||
else:
|
||||
user_data = None
|
||||
return super().get_context_data(
|
||||
user_id=user_id or None,
|
||||
guest_enabled=anonymous_is_enabled(),
|
||||
user=user_data,
|
||||
**context,
|
||||
)
|
||||
|
||||
|
||||
class SetPasswordView(APIView):
|
||||
"""
|
||||
|
@ -15,7 +15,7 @@ class TestWhoAmIView(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
json.loads(response.content.decode()),
|
||||
{"user_id": None, "user": None, "guest_enabled": False},
|
||||
{"user_id": None, "user": None, "permissions": [], "guest_enabled": False},
|
||||
)
|
||||
|
||||
def test_get_authenticated_user(self):
|
||||
@ -56,6 +56,10 @@ class TestUserLogoutView(TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(hasattr(self.client.session, "test_key"))
|
||||
self.assertEqual(
|
||||
json.loads(response.content.decode()),
|
||||
{"user_id": None, "user": None, "permissions": [], "guest_enabled": False},
|
||||
)
|
||||
|
||||
|
||||
class TestUserLoginView(TestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user