2018-07-06 09:38:25 +02:00
|
|
|
import { Injectable } from '@angular/core';
|
2019-01-31 13:40:27 +01:00
|
|
|
|
|
|
|
import { Observable, BehaviorSubject } from 'rxjs';
|
|
|
|
|
2018-07-23 16:42:17 +02:00
|
|
|
import { Group } from 'app/shared/models/users/group';
|
2018-08-22 11:26:53 +02:00
|
|
|
import { User } from '../../shared/models/users/user';
|
2018-08-23 16:49:51 +02:00
|
|
|
import { environment } from 'environments/environment';
|
2018-09-13 14:40:04 +02:00
|
|
|
import { DataStoreService } from './data-store.service';
|
2018-10-26 10:23:14 +02:00
|
|
|
import { OfflineService } from './offline.service';
|
2019-02-01 13:56:08 +01:00
|
|
|
import { ViewUser } from 'app/site/users/models/view-user';
|
|
|
|
import { OnAfterAppsLoaded } from '../onAfterAppsLoaded';
|
|
|
|
import { UserRepositoryService } from '../repositories/users/user-repository.service';
|
2019-03-04 11:45:15 +01:00
|
|
|
import { CollectionStringMapperService } from './collectionStringMapper.service';
|
|
|
|
import { StorageService } from './storage.service';
|
|
|
|
import { HttpService } from './http.service';
|
2018-07-06 09:38:25 +02:00
|
|
|
|
2018-08-28 11:07:10 +02:00
|
|
|
/**
|
|
|
|
* Permissions on the client are just strings. This makes clear, that
|
|
|
|
* permissions instead of arbitrary strings should be given.
|
|
|
|
*/
|
|
|
|
export type Permission = string;
|
|
|
|
|
|
|
|
/**
|
2019-03-04 11:45:15 +01:00
|
|
|
* Response format of the WhoAmI request.
|
2018-08-28 11:07:10 +02:00
|
|
|
*/
|
2018-10-26 10:23:14 +02:00
|
|
|
export interface WhoAmIResponse {
|
2018-08-28 11:07:10 +02:00
|
|
|
user_id: number;
|
|
|
|
guest_enabled: boolean;
|
|
|
|
user: User;
|
|
|
|
}
|
|
|
|
|
2019-03-04 11:45:15 +01:00
|
|
|
function isWhoAmIResponse(obj: any): obj is WhoAmIResponse {
|
|
|
|
if (!obj) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const whoAmI = obj as WhoAmIResponse;
|
|
|
|
return whoAmI.guest_enabled !== undefined && whoAmI.user !== undefined && whoAmI.user_id !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const WHOAMI_STORAGE_KEY = 'whoami';
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
|
|
|
* The operator represents the user who is using OpenSlides.
|
|
|
|
*
|
|
|
|
* Changes in operator can be observed, directives do so on order to show
|
|
|
|
* or hide certain information.
|
|
|
|
*/
|
2018-07-06 09:38:25 +02:00
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
2019-02-08 17:24:32 +01:00
|
|
|
export class OperatorService implements OnAfterAppsLoaded {
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* The operator.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-08-28 11:07:10 +02:00
|
|
|
private _user: User;
|
2018-07-06 09:38:25 +02:00
|
|
|
|
2019-03-04 11:45:15 +01:00
|
|
|
public get user(): User {
|
|
|
|
return this._user;
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:56:08 +01:00
|
|
|
/**
|
|
|
|
* The operator as a view user. We need a separation here, because
|
|
|
|
* we need to acces the operators permissions, before we get data
|
|
|
|
* from the server to build the view user.
|
|
|
|
*/
|
|
|
|
private _viewUser: ViewUser;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the user that corresponds to operator.
|
|
|
|
*/
|
|
|
|
public get viewUser(): ViewUser {
|
|
|
|
return this._viewUser;
|
|
|
|
}
|
|
|
|
|
2018-10-16 12:41:46 +02:00
|
|
|
public get isAnonymous(): boolean {
|
|
|
|
return !this.user || this.user.id === 0;
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2019-03-04 11:45:15 +01:00
|
|
|
* Save, if guests are enabled.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2019-03-04 11:45:15 +01:00
|
|
|
public get guestsEnabled(): boolean {
|
|
|
|
return this.currentWhoAmIResponse ? this.currentWhoAmIResponse.guest_enabled : false;
|
|
|
|
}
|
2018-07-06 09:38:25 +02:00
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* The permissions of the operator. Updated via {@method updatePermissions}.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-08-28 11:07:10 +02:00
|
|
|
private permissions: Permission[] = [];
|
2018-07-06 09:38:25 +02:00
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* The subject that can be observed by other instances using observing functions.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-09-07 13:12:59 +02:00
|
|
|
private operatorSubject: BehaviorSubject<User> = new BehaviorSubject<User>(null);
|
2018-07-06 09:38:25 +02:00
|
|
|
|
2019-02-01 13:56:08 +01:00
|
|
|
/**
|
|
|
|
* Subject for the operator as a view user.
|
|
|
|
*/
|
|
|
|
private viewOperatorSubject: BehaviorSubject<ViewUser> = new BehaviorSubject<ViewUser>(null);
|
|
|
|
|
|
|
|
/**
|
2019-02-10 20:18:15 +01:00
|
|
|
* Do not access the repo before it wasn't loaded. Will be true after `onAfterAppsLoaded`.
|
2019-02-01 13:56:08 +01:00
|
|
|
*/
|
2019-03-04 11:45:15 +01:00
|
|
|
private userRepository: UserRepositoryService | null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The current WhoAmI response to extract the user (the operator) from.
|
|
|
|
*/
|
|
|
|
private currentWhoAmIResponse: WhoAmIResponse | null;
|
2019-02-01 13:56:08 +01:00
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-10-26 10:23:14 +02:00
|
|
|
* Sets up an observer for watching changes in the DS. If the operator user or groups are changed,
|
|
|
|
* the operator's permissions are updated.
|
|
|
|
*
|
2019-03-04 11:45:15 +01:00
|
|
|
* @param http
|
2018-10-26 10:23:14 +02:00
|
|
|
* @param DS
|
|
|
|
* @param offlineService
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2019-02-01 13:56:08 +01:00
|
|
|
public constructor(
|
2019-03-04 11:45:15 +01:00
|
|
|
private http: HttpService,
|
2019-02-01 13:56:08 +01:00
|
|
|
private DS: DataStoreService,
|
|
|
|
private offlineService: OfflineService,
|
2019-03-04 11:45:15 +01:00
|
|
|
private collectionStringMapper: CollectionStringMapperService,
|
|
|
|
private storageService: StorageService
|
2019-02-01 13:56:08 +01:00
|
|
|
) {
|
2018-09-07 12:51:16 +02:00
|
|
|
this.DS.changeObservable.subscribe(newModel => {
|
2018-08-28 11:07:10 +02:00
|
|
|
if (this._user) {
|
|
|
|
if (newModel instanceof Group) {
|
|
|
|
this.updatePermissions();
|
|
|
|
}
|
2018-08-22 11:26:53 +02:00
|
|
|
|
2018-08-28 11:07:10 +02:00
|
|
|
if (newModel instanceof User && this._user.id === newModel.id) {
|
2019-02-01 13:56:08 +01:00
|
|
|
this.updateUser(newModel);
|
2018-08-28 11:07:10 +02:00
|
|
|
}
|
|
|
|
} else if (newModel instanceof Group && newModel.id === 1) {
|
|
|
|
// Group 1 (default) for anonymous changed
|
|
|
|
this.updatePermissions();
|
2018-08-22 11:26:53 +02:00
|
|
|
}
|
2018-07-06 09:38:25 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-04 11:45:15 +01:00
|
|
|
/**
|
|
|
|
* Gets the current WHoAmI response from the storage.
|
|
|
|
*/
|
|
|
|
public async whoAmIFromStorage(): Promise<WhoAmIResponse> {
|
|
|
|
const defaultResponse = {
|
|
|
|
user_id: null,
|
|
|
|
guest_enabled: false,
|
|
|
|
user: null
|
|
|
|
};
|
|
|
|
let response: WhoAmIResponse;
|
|
|
|
try {
|
|
|
|
response = await this.storageService.get<WhoAmIResponse>(WHOAMI_STORAGE_KEY);
|
|
|
|
if (response) {
|
|
|
|
this.processWhoAmIResponse(response);
|
|
|
|
} else {
|
|
|
|
response = defaultResponse;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
response = defaultResponse;
|
|
|
|
}
|
|
|
|
this.currentWhoAmIResponse = response;
|
|
|
|
return this.currentWhoAmIResponse;
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:56:08 +01:00
|
|
|
/**
|
|
|
|
* Load the repo to get a view user.
|
|
|
|
*/
|
|
|
|
public onAfterAppsLoaded(): void {
|
2019-03-04 11:45:15 +01:00
|
|
|
this.userRepository = this.collectionStringMapper.getRepository(ViewUser) as UserRepositoryService;
|
2019-02-01 13:56:08 +01:00
|
|
|
if (this.user) {
|
|
|
|
this._viewUser = this.userRepository.getViewModel(this.user.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-04 11:45:15 +01:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:56:08 +01:00
|
|
|
/**
|
|
|
|
* Updates the user and update the permissions.
|
|
|
|
*
|
|
|
|
* @param user The user to set.
|
2019-03-04 11:45:15 +01:00
|
|
|
* @param saveToStoare Whether to save the user to the storage WhoAmI.
|
2019-02-01 13:56:08 +01:00
|
|
|
*/
|
2019-03-04 11:45:15 +01:00
|
|
|
private async updateUser(user: User | null, saveToStorage: boolean = false): Promise<void> {
|
2019-02-01 13:56:08 +01:00
|
|
|
this._user = user;
|
2019-03-04 11:45:15 +01:00
|
|
|
if (saveToStorage) {
|
|
|
|
await this.saveUserToStorate();
|
|
|
|
}
|
|
|
|
if (user && this.userRepository) {
|
2019-02-01 13:56:08 +01:00
|
|
|
this._viewUser = this.userRepository.getViewModel(user.id);
|
|
|
|
} else {
|
|
|
|
this._viewUser = null;
|
|
|
|
}
|
|
|
|
this.updatePermissions();
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* Calls `/apps/users/whoami` to find out the real operator.
|
2019-03-04 11:45:15 +01:00
|
|
|
*
|
2018-10-26 10:23:14 +02:00
|
|
|
* @returns The response of the WhoAmI request.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-10-26 10:23:14 +02:00
|
|
|
public async whoAmI(): Promise<WhoAmIResponse> {
|
|
|
|
try {
|
2019-03-04 11:45:15 +01:00
|
|
|
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;
|
|
|
|
} else {
|
|
|
|
this.offlineService.goOfflineBecauseFailedWhoAmI();
|
2018-10-26 10:23:14 +02:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
this.offlineService.goOfflineBecauseFailedWhoAmI();
|
|
|
|
}
|
2019-03-04 11:45:15 +01:00
|
|
|
return this.currentWhoAmIResponse;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (this.user) {
|
|
|
|
this.currentWhoAmIResponse.user_id = this.user.id;
|
|
|
|
this.currentWhoAmIResponse.user = this.user;
|
|
|
|
} else {
|
|
|
|
this.currentWhoAmIResponse.user_id = null;
|
|
|
|
this.currentWhoAmIResponse.user = null;
|
|
|
|
}
|
|
|
|
await this.storageService.set(WHOAMI_STORAGE_KEY, this.currentWhoAmIResponse);
|
2018-07-06 09:38:25 +02:00
|
|
|
}
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2019-03-04 11:45:15 +01:00
|
|
|
* Processes a WhoAmI response and set the user appropriately.
|
2018-07-12 14:11:31 +02:00
|
|
|
*
|
2019-03-04 11:45:15 +01:00
|
|
|
* @param response The WhoAMI response
|
|
|
|
*/
|
|
|
|
private processWhoAmIResponse(response: WhoAmIResponse): void {
|
|
|
|
this.updateUser(response.user ? new User(response.user) : null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @returns an observable for the operator as a user.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2019-02-01 13:56:08 +01:00
|
|
|
public getUserObservable(): Observable<User> {
|
2018-07-06 09:38:25 +02:00
|
|
|
return this.operatorSubject.asObservable();
|
|
|
|
}
|
|
|
|
|
2019-03-04 11:45:15 +01:00
|
|
|
/**
|
|
|
|
* @returns an observable for the operator as a viewUser. Note, that
|
|
|
|
* the viewUser might not be there, so for reliable (and not display) information,
|
|
|
|
* use the `getUserObservable`.
|
|
|
|
*/
|
2019-02-01 13:56:08 +01:00
|
|
|
public getViewUserObservable(): Observable<ViewUser> {
|
|
|
|
return this.viewOperatorSubject.asObservable();
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* Checks, if the operator has at least one of the given permissions.
|
2018-09-18 18:27:14 +02:00
|
|
|
* @param checkPerms The permissions to check, if at least one matches.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-09-18 18:27:14 +02:00
|
|
|
public hasPerms(...checkPerms: Permission[]): boolean {
|
2018-10-09 13:44:38 +02:00
|
|
|
if (this._user && this._user.groups_id.includes(2)) {
|
|
|
|
return true;
|
|
|
|
}
|
2018-09-18 18:27:14 +02:00
|
|
|
return checkPerms.some(permission => {
|
|
|
|
return this.permissions.includes(permission);
|
2018-08-28 11:07:10 +02:00
|
|
|
});
|
2018-07-06 09:38:25 +02:00
|
|
|
}
|
|
|
|
|
2018-10-16 12:41:46 +02:00
|
|
|
/**
|
|
|
|
* Returns true, if the operator is in at least one group or he is in the admin group.
|
|
|
|
* @param groups The groups to check
|
|
|
|
*/
|
|
|
|
public isInGroup(...groups: Group[]): boolean {
|
|
|
|
return this.isInGroupIds(...groups.map(group => group.id));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true, if the operator is in at least one group or he is in the admin group.
|
|
|
|
* @param groups The group ids to check
|
|
|
|
*/
|
|
|
|
public isInGroupIds(...groupIds: number[]): boolean {
|
|
|
|
if (!this.user) {
|
|
|
|
return groupIds.includes(1); // any anonymous is in the default group.
|
|
|
|
}
|
|
|
|
if (this.user.groups_id.includes(2)) {
|
|
|
|
// An admin has all perms and is technically in every group.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return groupIds.some(id => this.user.groups_id.includes(id));
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:11:31 +02:00
|
|
|
/**
|
2018-08-28 11:07:10 +02:00
|
|
|
* Update the operators permissions and publish the operator afterwards.
|
2018-07-12 14:11:31 +02:00
|
|
|
*/
|
2018-08-28 11:07:10 +02:00
|
|
|
private updatePermissions(): void {
|
|
|
|
this.permissions = [];
|
2019-01-31 11:15:21 +01:00
|
|
|
// Anonymous or users in the default group.
|
|
|
|
if (!this.user || this.user.groups_id.length === 0) {
|
2018-09-10 08:57:53 +02:00
|
|
|
const defaultGroup = this.DS.get<Group>('users/group', 1);
|
2018-08-28 11:07:10 +02:00
|
|
|
if (defaultGroup && defaultGroup.permissions instanceof Array) {
|
|
|
|
this.permissions = defaultGroup.permissions;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const permissionSet = new Set();
|
2018-09-13 14:40:04 +02:00
|
|
|
this.DS.getMany(Group, this.user.groups_id).forEach(group => {
|
2018-08-28 11:07:10 +02:00
|
|
|
group.permissions.forEach(permission => {
|
|
|
|
permissionSet.add(permission);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
this.permissions = Array.from(permissionSet.values());
|
2018-07-06 09:38:25 +02:00
|
|
|
}
|
2018-08-28 11:07:10 +02:00
|
|
|
// publish changes in the operator.
|
|
|
|
this.operatorSubject.next(this.user);
|
2019-02-01 13:56:08 +01:00
|
|
|
this.viewOperatorSubject.next(this.viewUser);
|
2018-08-22 11:26:53 +02:00
|
|
|
}
|
2018-07-06 09:38:25 +02:00
|
|
|
}
|