Merge pull request #4971 from FinnStutzenstein/anonymousImprovements
Improves the sidebar for anonymous users and routing
This commit is contained in:
commit
975f8dc169
@ -11,11 +11,11 @@ import { CollectionStringMapperService } from './collection-string-mapper.servic
|
|||||||
import { CommonAppConfig } from '../../site/common/common.config';
|
import { CommonAppConfig } from '../../site/common/common.config';
|
||||||
import { ConfigAppConfig } from '../../site/config/config.config';
|
import { ConfigAppConfig } from '../../site/config/config.config';
|
||||||
import { ServicesToLoadOnAppsLoaded } from '../core.module';
|
import { ServicesToLoadOnAppsLoaded } from '../core.module';
|
||||||
|
import { FallbackRoutesService } from './fallback-routes.service';
|
||||||
import { MainMenuService } from './main-menu.service';
|
import { MainMenuService } from './main-menu.service';
|
||||||
import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config';
|
import { MediafileAppConfig } from '../../site/mediafiles/mediafile.config';
|
||||||
import { MotionsAppConfig } from '../../site/motions/motions.config';
|
import { MotionsAppConfig } from '../../site/motions/motions.config';
|
||||||
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
|
import { OnAfterAppsLoaded } from '../definitions/on-after-apps-loaded';
|
||||||
import { plugins } from '../../../plugins';
|
|
||||||
import { SearchService } from '../ui-services/search.service';
|
import { SearchService } from '../ui-services/search.service';
|
||||||
import { isSearchable } from '../../site/base/searchable';
|
import { isSearchable } from '../../site/base/searchable';
|
||||||
import { TagAppConfig } from '../../site/tags/tag.config';
|
import { TagAppConfig } from '../../site/tags/tag.config';
|
||||||
@ -56,17 +56,11 @@ export class AppLoadService {
|
|||||||
private modelMapper: CollectionStringMapperService,
|
private modelMapper: CollectionStringMapperService,
|
||||||
private mainMenuService: MainMenuService,
|
private mainMenuService: MainMenuService,
|
||||||
private searchService: SearchService,
|
private searchService: SearchService,
|
||||||
private injector: Injector
|
private injector: Injector,
|
||||||
|
private fallbackRoutesService: FallbackRoutesService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async loadApps(): Promise<void> {
|
public async loadApps(): Promise<void> {
|
||||||
if (plugins.length) {
|
|
||||||
console.log('plugins: ', plugins);
|
|
||||||
}
|
|
||||||
/*for (const pluginName of plugins) {
|
|
||||||
const plugin = await import('../../../../../plugins/' + pluginName + '/' + pluginName);
|
|
||||||
plugin.main();
|
|
||||||
}*/
|
|
||||||
const repositories: OnAfterAppsLoaded[] = [];
|
const repositories: OnAfterAppsLoaded[] = [];
|
||||||
appConfigs.forEach((config: AppConfig) => {
|
appConfigs.forEach((config: AppConfig) => {
|
||||||
if (config.models) {
|
if (config.models) {
|
||||||
@ -92,6 +86,7 @@ export class AppLoadService {
|
|||||||
}
|
}
|
||||||
if (config.mainMenuEntries) {
|
if (config.mainMenuEntries) {
|
||||||
this.mainMenuService.registerEntries(config.mainMenuEntries);
|
this.mainMenuService.registerEntries(config.mainMenuEntries);
|
||||||
|
this.fallbackRoutesService.registerFallbackEntries(config.mainMenuEntries);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { FallbackRoutesService } from './fallback-routes.service';
|
||||||
import { OpenSlidesService } from './openslides.service';
|
import { OpenSlidesService } from './openslides.service';
|
||||||
import { OperatorService } from './operator.service';
|
import { OperatorService } from './operator.service';
|
||||||
|
|
||||||
@ -21,7 +22,8 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
public constructor(
|
public constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private openSlidesService: OpenSlidesService
|
private openSlidesService: OpenSlidesService,
|
||||||
|
private fallbackRoutesService: FallbackRoutesService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +58,28 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
if (this.canActivate(route)) {
|
if (this.canActivate(route)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
this.handleForbiddenRoute(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a forbidden route. If the route is "/" (start page), It is tried to
|
||||||
|
* use a fallback route provided by AuthGuardFallbackRoutes. If this won't work
|
||||||
|
* or it wasn't the start page in the first place, the operator will be redirected
|
||||||
|
* to an error page.
|
||||||
|
*/
|
||||||
|
private handleForbiddenRoute(route: ActivatedRouteSnapshot): void {
|
||||||
|
if (route.url.length === 0) {
|
||||||
|
// start page
|
||||||
|
const fallbackRoute = this.fallbackRoutesService.getFallbackRoute();
|
||||||
|
if (fallbackRoute) {
|
||||||
|
this.router.navigate([fallbackRoute]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall-through: If the url is the start page, but no other fallback was found,
|
||||||
|
// navigate to the error page.
|
||||||
|
|
||||||
this.openSlidesService.redirectUrl = location.pathname;
|
this.openSlidesService.redirectUrl = location.pathname;
|
||||||
this.router.navigate(['/error'], {
|
this.router.navigate(['/error'], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
@ -65,4 +89,3 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
43
client/src/app/core/core-services/fallback-routes.service.ts
Normal file
43
client/src/app/core/core-services/fallback-routes.service.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { OperatorService } from './operator.service';
|
||||||
|
|
||||||
|
export interface AuthGuardFallbackEntry {
|
||||||
|
route: string;
|
||||||
|
weight: number;
|
||||||
|
permission: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classical Auth-Guard. Checks if the user has to correct permissions to enter a page, and forwards to login if not.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class FallbackRoutesService {
|
||||||
|
private fallbackEntries: AuthGuardFallbackEntry[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param operator Asking for the required permission
|
||||||
|
*/
|
||||||
|
public constructor(private operator: OperatorService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds fallback navigation entries for the start page.
|
||||||
|
* @param entries The entries to add
|
||||||
|
*/
|
||||||
|
public registerFallbackEntries(entries: AuthGuardFallbackEntry[]): void {
|
||||||
|
this.fallbackEntries.push(...entries);
|
||||||
|
this.fallbackEntries = this.fallbackEntries.sort((a, b) => a.weight - b.weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFallbackRoute(): string | null {
|
||||||
|
for (const entry of this.fallbackEntries) {
|
||||||
|
if (this.operator.hasPerms(entry.permission)) {
|
||||||
|
return entry.route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,17 +30,21 @@ export class OfflineService {
|
|||||||
* Sets the offline flag. Restores the DataStoreService to the last known configuration.
|
* Sets the offline flag. Restores the DataStoreService to the last known configuration.
|
||||||
*/
|
*/
|
||||||
public goOfflineBecauseFailedWhoAmI(): void {
|
public goOfflineBecauseFailedWhoAmI(): void {
|
||||||
this.offline.next(true);
|
if (!this.offline.getValue()) {
|
||||||
console.log('offline because whoami failed.');
|
console.log('offline because whoami failed.');
|
||||||
}
|
}
|
||||||
|
this.offline.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the offline flag, because there is no connection to the server.
|
* Sets the offline flag, because there is no connection to the server.
|
||||||
*/
|
*/
|
||||||
public goOfflineBecauseConnectionLost(): void {
|
public goOfflineBecauseConnectionLost(): void {
|
||||||
this.offline.next(true);
|
if (!this.offline.getValue()) {
|
||||||
console.log('offline because connection lost.');
|
console.log('offline because connection lost.');
|
||||||
}
|
}
|
||||||
|
this.offline.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to return to online-status.
|
* Function to return to online-status.
|
||||||
|
@ -63,16 +63,23 @@ export class OpenSlidesService {
|
|||||||
*/
|
*/
|
||||||
public async bootup(): Promise<void> {
|
public async bootup(): Promise<void> {
|
||||||
// start autoupdate if the user is logged in:
|
// start autoupdate if the user is logged in:
|
||||||
const response = await this.operator.whoAmIFromStorage();
|
let response = await this.operator.whoAmIFromStorage();
|
||||||
|
const needToCheckOperator = !!response;
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
response = await this.operator.whoAmI();
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.user && !response.guest_enabled) {
|
if (!response.user && !response.guest_enabled) {
|
||||||
if (!location.pathname.includes('error')) {
|
if (!location.pathname.includes('error')) {
|
||||||
this.redirectUrl = location.pathname;
|
this.redirectUrl = location.pathname;
|
||||||
}
|
}
|
||||||
this.redirectToLoginIfNotSubpage();
|
this.redirectToLoginIfNotSubpage();
|
||||||
this.checkOperator(false);
|
|
||||||
} else {
|
} else {
|
||||||
await this.afterLoginBootup(response.user_id);
|
await this.afterLoginBootup(response.user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needToCheckOperator) {
|
||||||
// Check for the operator via a async whoami (so no await here)
|
// Check for the operator via a async whoami (so no await here)
|
||||||
// to validate, that the cache was correct.
|
// to validate, that the cache was correct.
|
||||||
this.checkOperator(false);
|
this.checkOperator(false);
|
||||||
|
@ -116,10 +116,30 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
*/
|
*/
|
||||||
private userRepository: UserRepositoryService | null;
|
private userRepository: UserRepositoryService | null;
|
||||||
|
|
||||||
|
private _currentWhoAmI: WhoAmI | null = null;
|
||||||
|
private _defaultWhoAMI: WhoAmI = {
|
||||||
|
user_id: null,
|
||||||
|
guest_enabled: false,
|
||||||
|
user: null,
|
||||||
|
permissions: []
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current WhoAmI response to extract the user (the operator) from.
|
* The current WhoAmI response to extract the user (the operator) from.
|
||||||
*/
|
*/
|
||||||
private currentWhoAmI: WhoAmI | null;
|
private get currentWhoAmI(): WhoAmI {
|
||||||
|
return this._currentWhoAmI || this._defaultWhoAMI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set currentWhoAmI(value: WhoAmI | null) {
|
||||||
|
this._currentWhoAmI = value;
|
||||||
|
|
||||||
|
// Resetting the default whoami, when the current whoami isn't there. This
|
||||||
|
// is for a fresh restart and do not have (old) changed values in this.defaultWhoAmI
|
||||||
|
if (!value) {
|
||||||
|
this._defaultWhoAMI = this.getDefaultWhoAmIResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly _loaded: Deferred<void> = new Deferred();
|
private readonly _loaded: Deferred<void> = new Deferred();
|
||||||
|
|
||||||
@ -169,7 +189,7 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
),
|
),
|
||||||
auditTime(10)
|
auditTime(10)
|
||||||
)
|
)
|
||||||
.subscribe(_ => this.updatePermissions());
|
.subscribe(() => this.updatePermissions());
|
||||||
|
|
||||||
// Watches the user observable to update the viewUser for the operator.
|
// Watches the user observable to update the viewUser for the operator.
|
||||||
this.getUserObservable().subscribe(user => {
|
this.getUserObservable().subscribe(user => {
|
||||||
@ -198,36 +218,6 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a default WhoAmI response
|
|
||||||
*/
|
|
||||||
private getDefaultWhoAmIResponse(): WhoAmI {
|
|
||||||
return {
|
|
||||||
user_id: null,
|
|
||||||
guest_enabled: false,
|
|
||||||
user: null,
|
|
||||||
permissions: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current WhoAmI response from the storage.
|
|
||||||
*/
|
|
||||||
public async whoAmIFromStorage(): Promise<WhoAmI> {
|
|
||||||
let response: WhoAmI;
|
|
||||||
try {
|
|
||||||
response = await this.storageService.get<WhoAmI>(WHOAMI_STORAGE_KEY);
|
|
||||||
if (!response) {
|
|
||||||
response = this.getDefaultWhoAmIResponse();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
response = this.getDefaultWhoAmIResponse();
|
|
||||||
}
|
|
||||||
await this.updateCurrentWhoAmI(response);
|
|
||||||
this._loaded.resolve();
|
|
||||||
return this.currentWhoAmI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the repo to get a view user.
|
* Load the repo to get a view user.
|
||||||
*/
|
*/
|
||||||
@ -239,6 +229,24 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
this.viewOperatorSubject.next(this._viewUser);
|
this.viewOperatorSubject.next(this._viewUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current WhoAmI response from the storage.
|
||||||
|
*/
|
||||||
|
public async whoAmIFromStorage(): Promise<WhoAmI | null> {
|
||||||
|
let response: WhoAmI | null = null;
|
||||||
|
try {
|
||||||
|
response = await this.storageService.get<WhoAmI>(WHOAMI_STORAGE_KEY);
|
||||||
|
if (!isWhoAmI(response)) {
|
||||||
|
response = null;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
await this.updateCurrentWhoAmI(response);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the operator user. Will be saved to storage
|
* Sets the operator user. Will be saved to storage
|
||||||
* @param user The new operator.
|
* @param user The new operator.
|
||||||
@ -274,9 +282,6 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
* WhoAMI response.
|
* WhoAMI response.
|
||||||
*/
|
*/
|
||||||
private async updateUserInCurrentWhoAmI(): Promise<void> {
|
private async updateUserInCurrentWhoAmI(): Promise<void> {
|
||||||
if (!this.currentWhoAmI) {
|
|
||||||
this.currentWhoAmI = this.getDefaultWhoAmIResponse();
|
|
||||||
}
|
|
||||||
if (this.isAnonymous) {
|
if (this.isAnonymous) {
|
||||||
this.currentWhoAmI.user_id = null;
|
this.currentWhoAmI.user_id = null;
|
||||||
this.currentWhoAmI.user = null;
|
this.currentWhoAmI.user = null;
|
||||||
@ -300,6 +305,7 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
|
|
||||||
this._user = whoami ? whoami.user : null;
|
this._user = whoami ? whoami.user : null;
|
||||||
await this.updatePermissions();
|
await this.updatePermissions();
|
||||||
|
this._loaded.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -394,9 +400,6 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save perms to current WhoAmI
|
// Save perms to current WhoAmI
|
||||||
if (!this.currentWhoAmI) {
|
|
||||||
this.currentWhoAmI = this.getDefaultWhoAmIResponse();
|
|
||||||
}
|
|
||||||
this.currentWhoAmI.permissions = this.permissions;
|
this.currentWhoAmI.permissions = this.permissions;
|
||||||
|
|
||||||
if (!this.OSStatus.isInHistoryMode) {
|
if (!this.OSStatus.isInHistoryMode) {
|
||||||
@ -406,4 +409,16 @@ export class OperatorService implements OnAfterAppsLoaded {
|
|||||||
// publish changes in the operator.
|
// publish changes in the operator.
|
||||||
this.operatorSubject.next(this.user);
|
this.operatorSubject.next(this.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a default WhoAmI response
|
||||||
|
*/
|
||||||
|
private getDefaultWhoAmIResponse(): WhoAmI {
|
||||||
|
return {
|
||||||
|
user_id: null,
|
||||||
|
guest_enabled: false,
|
||||||
|
user: null,
|
||||||
|
permissions: []
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,14 @@
|
|||||||
|
|
||||||
<!-- navigation -->
|
<!-- navigation -->
|
||||||
<mat-nav-list class="main-nav">
|
<mat-nav-list class="main-nav">
|
||||||
|
<ng-container *ngIf="!isLoggedIn">
|
||||||
|
<a routerLink="/login" mat-list-item>
|
||||||
|
<mat-icon>exit_to_app</mat-icon>
|
||||||
|
<span translate>Login</span>
|
||||||
|
</a>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<span *ngFor="let entry of mainMenuService.entries">
|
<span *ngFor="let entry of mainMenuService.entries">
|
||||||
<a
|
<a
|
||||||
[@navItemAnim]
|
[@navItemAnim]
|
||||||
|
@ -11,7 +11,6 @@ import { filter } from 'rxjs/operators';
|
|||||||
|
|
||||||
import { navItemAnim, pageTransition } from '../shared/animations';
|
import { navItemAnim, pageTransition } from '../shared/animations';
|
||||||
import { OfflineService } from 'app/core/core-services/offline.service';
|
import { OfflineService } from 'app/core/core-services/offline.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
|
||||||
import { OverlayService } from 'app/core/ui-services/overlay.service';
|
import { OverlayService } from 'app/core/ui-services/overlay.service';
|
||||||
import { UpdateService } from 'app/core/ui-services/update.service';
|
import { UpdateService } from 'app/core/ui-services/update.service';
|
||||||
import { langToLocale } from 'app/shared/utils/lang-to-locale';
|
import { langToLocale } from 'app/shared/utils/lang-to-locale';
|
||||||
@ -90,7 +89,6 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
|||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
configService: ConfigService,
|
|
||||||
offlineService: OfflineService,
|
offlineService: OfflineService,
|
||||||
private updateService: UpdateService,
|
private updateService: UpdateService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
@ -108,11 +106,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
|
|||||||
overlayService.setSpinner(true, translate.instant('Loading data. Please wait...'));
|
overlayService.setSpinner(true, translate.instant('Loading data. Please wait...'));
|
||||||
|
|
||||||
this.operator.getViewUserObservable().subscribe(user => {
|
this.operator.getViewUserObservable().subscribe(user => {
|
||||||
if (user) {
|
this.username = user ? user.short_name : translate.instant('Guest');
|
||||||
this.username = user.short_name;
|
|
||||||
} else if (!user && configService.instant<boolean>('general_system_enable_anonymous')) {
|
|
||||||
this.username = translate.instant('Guest');
|
|
||||||
}
|
|
||||||
this.isLoggedIn = !!user;
|
this.isLoggedIn = !!user;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user