From e17b8acc9cad8f0838cd06f4f33e32f55acf26a6 Mon Sep 17 00:00:00 2001 From: smuddyx Date: Sat, 25 Apr 2020 14:53:35 +0200 Subject: [PATCH] simple role management --- firestore.rules | 14 ++++- src/app/app.component.ts | 2 - .../modules/shows/services/docx.service.ts | 4 +- .../shows/services/show-song.service.ts | 2 +- .../modules/shows/services/show.service.ts | 4 +- .../modules/shows/show/show.component.html | 2 +- src/app/modules/songs/song/song.component.ts | 4 +- src/app/modules/user/info/info.component.html | 3 + src/app/modules/user/info/info.component.ts | 4 +- src/app/modules/user/info/role.pipe.ts | 12 +++- .../user/info/users/users.component.html | 18 ++++++ .../user/info/users/users.component.less | 5 ++ .../user/info/users/users.component.spec.ts | 25 +++++++++ .../user/info/users/users.component.ts | 31 +++++++++++ src/app/modules/user/login/login.component.ts | 2 +- .../modules/user/logout/logout.component.ts | 2 +- .../user/password/password.component.ts | 2 +- src/app/modules/user/user.module.ts | 5 +- src/app/services/user/role.directive.spec.ts | 8 +++ src/app/services/user/role.directive.ts | 55 +++++++++++++++++++ src/app/services/user/role.module.ts | 13 +++++ src/app/services/user/roles.ts | 2 + .../services/{ => user}/user.service.spec.ts | 0 src/app/services/{ => user}/user.service.ts | 6 +- src/app/services/{ => user}/user.ts | 2 +- .../application-frame.module.ts | 5 +- .../navigation/navigation.component.html | 6 +- .../song-text/song-text.component.ts | 10 ++-- src/styles/styles.less | 2 + 29 files changed, 219 insertions(+), 31 deletions(-) create mode 100644 src/app/modules/user/info/users/users.component.html create mode 100644 src/app/modules/user/info/users/users.component.less create mode 100644 src/app/modules/user/info/users/users.component.spec.ts create mode 100644 src/app/modules/user/info/users/users.component.ts create mode 100644 src/app/services/user/role.directive.spec.ts create mode 100644 src/app/services/user/role.directive.ts create mode 100644 src/app/services/user/role.module.ts create mode 100644 src/app/services/user/roles.ts rename src/app/services/{ => user}/user.service.spec.ts (100%) rename src/app/services/{ => user}/user.service.ts (88%) rename src/app/services/{ => user}/user.ts (52%) diff --git a/firestore.rules b/firestore.rules index bf5a42b..6aa41a2 100644 --- a/firestore.rules +++ b/firestore.rules @@ -2,8 +2,17 @@ rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /users/{user} { - allow read: if resource.id == request.auth.uid; - allow write: if resource.id == request.auth.uid; + + function isUser(rsc) { + return rsc.id == request.auth.uid + } + + function isAdmin() { + return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin' + } + + allow read: if isUser(resource) || isAdmin(); + allow write: if isUser(resource) || isAdmin(); } match /songs/{song} { @@ -31,3 +40,4 @@ service cloud.firestore { } } + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 7cf39a9..a8809cb 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -14,6 +14,4 @@ export class AppComponent implements OnInit { setTimeout(() => AppComponent.hideLoader(), 800); } - - title = 'wgenerator'; } diff --git a/src/app/modules/shows/services/docx.service.ts b/src/app/modules/shows/services/docx.service.ts index 3d10ab6..0107d2c 100644 --- a/src/app/modules/shows/services/docx.service.ts +++ b/src/app/modules/shows/services/docx.service.ts @@ -10,8 +10,8 @@ import {SongService} from '../../songs/services/song.service'; import {ShowSong} from './show-song'; import {Show} from './show'; import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component'; -import {UserService} from '../../../services/user.service'; -import {User} from '../../../services/user'; +import {UserService} from '../../../services/user/user.service'; +import {User} from '../../../services/user/user'; @Injectable({ diff --git a/src/app/modules/shows/services/show-song.service.ts b/src/app/modules/shows/services/show-song.service.ts index d96d322..9c63bd2 100644 --- a/src/app/modules/shows/services/show-song.service.ts +++ b/src/app/modules/shows/services/show-song.service.ts @@ -4,7 +4,7 @@ import {Observable} from 'rxjs'; import {ShowSong} from './show-song'; import {SongDataService} from '../../songs/services/song-data.service'; import {take} from 'rxjs/operators'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; @Injectable({ providedIn: 'root' diff --git a/src/app/modules/shows/services/show.service.ts b/src/app/modules/shows/services/show.service.ts index fc8b6bd..ea07f5a 100644 --- a/src/app/modules/shows/services/show.service.ts +++ b/src/app/modules/shows/services/show.service.ts @@ -2,9 +2,9 @@ import {Injectable} from '@angular/core'; import {ShowDataService} from './show-data.service'; import {Show} from './show'; import {Observable} from 'rxjs'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; import {map, switchMap} from 'rxjs/operators'; -import {User} from '../../../services/user'; +import {User} from '../../../services/user/user'; @Injectable({ providedIn: 'root' diff --git a/src/app/modules/shows/show/show.component.html b/src/app/modules/shows/show/show.component.html index d88acea..e6f6c13 100644 --- a/src/app/modules/shows/show/show.component.html +++ b/src/app/modules/shows/show/show.component.html @@ -11,8 +11,8 @@ [showSong]="song" [showSongs]="showSongs" [showText]="showText" - [song]="getSong(song.songId)" [show]="show" + [song]="getSong(song.songId)" class="song-row" > diff --git a/src/app/modules/songs/song/song.component.ts b/src/app/modules/songs/song/song.component.ts index be86d5e..2900584 100644 --- a/src/app/modules/songs/song/song.component.ts +++ b/src/app/modules/songs/song/song.component.ts @@ -6,8 +6,8 @@ import {Song} from '../services/song'; import {Observable} from 'rxjs'; import {FileDataService} from '../services/file-data.service'; import {File} from '../services/file'; -import {UserService} from '../../../services/user.service'; -import {User} from '../../../services/user'; +import {UserService} from '../../../services/user/user.service'; +import {User} from '../../../services/user/user'; @Component({ selector: 'app-song', diff --git a/src/app/modules/user/info/info.component.html b/src/app/modules/user/info/info.component.html index 23c69f7..3d0e033 100644 --- a/src/app/modules/user/info/info.component.html +++ b/src/app/modules/user/info/info.component.html @@ -17,3 +17,6 @@ + + + diff --git a/src/app/modules/user/info/info.component.ts b/src/app/modules/user/info/info.component.ts index 5fbd2b1..46756bc 100644 --- a/src/app/modules/user/info/info.component.ts +++ b/src/app/modules/user/info/info.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; import {Observable} from 'rxjs'; -import {User} from '../../../services/user'; +import {User} from '../../../services/user/user'; import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component'; @Component({ diff --git a/src/app/modules/user/info/role.pipe.ts b/src/app/modules/user/info/role.pipe.ts index 3202bb0..76a7478 100644 --- a/src/app/modules/user/info/role.pipe.ts +++ b/src/app/modules/user/info/role.pipe.ts @@ -1,14 +1,24 @@ import {Pipe, PipeTransform} from '@angular/core'; +import {roles} from '../../../services/user/roles'; + @Pipe({ name: 'role' }) export class RolePipe implements PipeTransform { - transform(role: 'admin'): string { + transform(role: roles): string { switch (role) { + case 'none': + return 'keine Berechtigung'; case 'admin': return 'Administrator'; + case 'user': + return 'Benutzer'; + case 'leader': + return 'Lobpreisleiter'; + case 'presenter': + return 'Präsentator'; } } diff --git a/src/app/modules/user/info/users/users.component.html b/src/app/modules/user/info/users/users.component.html new file mode 100644 index 0000000..ec51809 --- /dev/null +++ b/src/app/modules/user/info/users/users.component.html @@ -0,0 +1,18 @@ + + +
+ + Name + + + + + Rolle + + {{role | role}} + + + +
+ +
diff --git a/src/app/modules/user/info/users/users.component.less b/src/app/modules/user/info/users/users.component.less new file mode 100644 index 0000000..7558267 --- /dev/null +++ b/src/app/modules/user/info/users/users.component.less @@ -0,0 +1,5 @@ +.users { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 20px; +} diff --git a/src/app/modules/user/info/users/users.component.spec.ts b/src/app/modules/user/info/users/users.component.spec.ts new file mode 100644 index 0000000..a851916 --- /dev/null +++ b/src/app/modules/user/info/users/users.component.spec.ts @@ -0,0 +1,25 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {UsersComponent} from './users.component'; + +describe('UsersComponent', () => { + let component: UsersComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [UsersComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UsersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/user/info/users/users.component.ts b/src/app/modules/user/info/users/users.component.ts new file mode 100644 index 0000000..d5b8c34 --- /dev/null +++ b/src/app/modules/user/info/users/users.component.ts @@ -0,0 +1,31 @@ +import {Component, OnInit} from '@angular/core'; +import {UserService} from '../../../../services/user/user.service'; +import {Observable} from 'rxjs'; +import {User} from '../../../../services/user/user'; +import {ROLE_TYPES} from '../../../../services/user/roles'; + +@Component({ + selector: 'app-users', + templateUrl: './users.component.html', + styleUrls: ['./users.component.less'] +}) +export class UsersComponent implements OnInit { + public users$: Observable; + public ROLE_TYPES = ROLE_TYPES; + + constructor(private userService: UserService) { + this.users$ = userService.list$(); + } + + ngOnInit(): void { + + } + + public async onRoleChanged(id: string, role: any): Promise { + await this.userService.update$(id, {role}); + } + + public async onNameChanged(id: string, name: any): Promise { + await this.userService.update$(id, {name: name.target.value}); + } +} diff --git a/src/app/modules/user/login/login.component.ts b/src/app/modules/user/login/login.component.ts index 837f6cb..5804921 100644 --- a/src/app/modules/user/login/login.component.ts +++ b/src/app/modules/user/login/login.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {FormControl, FormGroup, Validators} from '@angular/forms'; import {Router} from '@angular/router'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; @Component({ selector: 'app-login', diff --git a/src/app/modules/user/logout/logout.component.ts b/src/app/modules/user/logout/logout.component.ts index b9d4e11..b02e6c7 100644 --- a/src/app/modules/user/logout/logout.component.ts +++ b/src/app/modules/user/logout/logout.component.ts @@ -1,6 +1,6 @@ import {AfterViewInit, Component} from '@angular/core'; import {Router} from '@angular/router'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; @Component({ selector: 'app-logout', diff --git a/src/app/modules/user/password/password.component.ts b/src/app/modules/user/password/password.component.ts index 26bd106..58d85d9 100644 --- a/src/app/modules/user/password/password.component.ts +++ b/src/app/modules/user/password/password.component.ts @@ -1,7 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {FormControl, FormGroup, Validators} from '@angular/forms'; import {Router} from '@angular/router'; -import {UserService} from '../../../services/user.service'; +import {UserService} from '../../../services/user/user.service'; @Component({ selector: 'app-password', diff --git a/src/app/modules/user/user.module.ts b/src/app/modules/user/user.module.ts index 8575289..b0f1990 100644 --- a/src/app/modules/user/user.module.ts +++ b/src/app/modules/user/user.module.ts @@ -15,10 +15,12 @@ import {RolePipe} from './info/role.pipe'; import {MatSelectModule} from '@angular/material/select'; import {PasswordComponent} from './password/password.component'; import {PasswordSendComponent} from './password-send/password-send.component'; +import {UsersComponent} from './info/users/users.component'; +import {RoleModule} from '../../services/user/role.module'; @NgModule({ - declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent], + declarations: [LoginComponent, AuthMessagePipe, InfoComponent, LogoutComponent, RolePipe, PasswordComponent, PasswordSendComponent, UsersComponent], imports: [ CommonModule, UserRoutingModule, @@ -30,6 +32,7 @@ import {PasswordSendComponent} from './password-send/password-send.component'; MatButtonModule, MatSelectModule, FormsModule, + RoleModule, ] diff --git a/src/app/services/user/role.directive.spec.ts b/src/app/services/user/role.directive.spec.ts new file mode 100644 index 0000000..807efde --- /dev/null +++ b/src/app/services/user/role.directive.spec.ts @@ -0,0 +1,8 @@ +import {RoleDirective} from './role.directive'; + +describe('RoleDirective', () => { + it('should create an instance', () => { + const directive = new RoleDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/services/user/role.directive.ts b/src/app/services/user/role.directive.ts new file mode 100644 index 0000000..2a2f58d --- /dev/null +++ b/src/app/services/user/role.directive.ts @@ -0,0 +1,55 @@ +import {Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core'; +import {roles} from './roles'; +import {UserService} from './user.service'; +import {User} from './user'; + +@Directive({ + selector: '[appRole]' +}) +export class RoleDirective implements OnInit { + @Input() appRole: roles[] = []; + private currentUser: User; + private loggedIn: boolean; + + constructor( + private element: ElementRef, + private templateRef: TemplateRef, + private viewContainer: ViewContainerRef, + private userService: UserService + ) { + + } + + public ngOnInit(): void { + this.userService.user$.subscribe(user => { + this.currentUser = user; + this.updateView(); + }); + this.userService.loggedIn$().subscribe(_ => { + this.loggedIn = !!_; + this.updateView(); + }); + this.updateView(); + } + + private updateView() { + if (this.loggedIn && this.checkPermission()) { + this.viewContainer.createEmbeddedView(this.templateRef); + } else { + this.viewContainer.clear(); + } + } + + private checkPermission() { + if (this.currentUser && this.currentUser.role) { + if (this.currentUser.role === 'admin') return true; + for (const role of this.appRole) { + if (this.currentUser.role === role) return true; + } + } + + return false; + } + + +} diff --git a/src/app/services/user/role.module.ts b/src/app/services/user/role.module.ts new file mode 100644 index 0000000..db0b9f7 --- /dev/null +++ b/src/app/services/user/role.module.ts @@ -0,0 +1,13 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {RoleDirective} from './role.directive'; + +@NgModule({ + declarations: [RoleDirective], + exports: [RoleDirective], + imports: [ + CommonModule + ] +}) +export class RoleModule { +} diff --git a/src/app/services/user/roles.ts b/src/app/services/user/roles.ts new file mode 100644 index 0000000..59cc908 --- /dev/null +++ b/src/app/services/user/roles.ts @@ -0,0 +1,2 @@ +export type roles = 'none' | 'admin' | 'user' | 'leader' | 'presenter'; +export const ROLE_TYPES: roles[] = ['none', 'admin', 'user', 'leader', 'presenter']; diff --git a/src/app/services/user.service.spec.ts b/src/app/services/user/user.service.spec.ts similarity index 100% rename from src/app/services/user.service.spec.ts rename to src/app/services/user/user.service.spec.ts diff --git a/src/app/services/user.service.ts b/src/app/services/user/user.service.ts similarity index 88% rename from src/app/services/user.service.ts rename to src/app/services/user/user.service.ts index 3ff3a9a..fb3cb7f 100644 --- a/src/app/services/user.service.ts +++ b/src/app/services/user/user.service.ts @@ -3,7 +3,7 @@ import {AngularFireAuth} from '@angular/fire/auth'; import {BehaviorSubject, Observable} from 'rxjs'; import {filter, switchMap} from 'rxjs/operators'; import {User} from './user'; -import {DbService} from './db.service'; +import {DbService} from '../db.service'; @Injectable({ providedIn: 'root' @@ -22,6 +22,10 @@ export class UserService { return this._user$.pipe(filter(_ => !!_)); } + public loggedIn$ = () => this.afAuth.authState; + + public list$ = (): Observable => this.db.col$('users'); + public getUserbyId$(userId: string): Observable { return this.db.doc$('users/' + userId); } diff --git a/src/app/services/user.ts b/src/app/services/user/user.ts similarity index 52% rename from src/app/services/user.ts rename to src/app/services/user/user.ts index 2745b02..329b7d2 100644 --- a/src/app/services/user.ts +++ b/src/app/services/user/user.ts @@ -1,4 +1,4 @@ -import {ChordMode} from '../widget-modules/components/song-text/song-text.component'; +import {ChordMode} from '../../widget-modules/components/song-text/song-text.component'; export interface User { id: string; diff --git a/src/app/widget-modules/components/application-frame/application-frame.module.ts b/src/app/widget-modules/components/application-frame/application-frame.module.ts index b379953..f529229 100644 --- a/src/app/widget-modules/components/application-frame/application-frame.module.ts +++ b/src/app/widget-modules/components/application-frame/application-frame.module.ts @@ -6,7 +6,7 @@ import {FilterComponent} from './navigation/filter/filter.component'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {LinkComponent} from './navigation/link/link.component'; import {FormsModule} from '@angular/forms'; - +import {RoleModule} from '../../../services/user/role.module'; @NgModule({ declarations: [ @@ -18,7 +18,8 @@ import {FormsModule} from '@angular/forms'; CommonModule, RouterModule, FontAwesomeModule, - FormsModule + FormsModule, + RoleModule ], exports: [ NavigationComponent diff --git a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html index f73adc7..e07ee73 100644 --- a/src/app/widget-modules/components/application-frame/navigation/navigation.component.html +++ b/src/app/widget-modules/components/application-frame/navigation/navigation.component.html @@ -1,8 +1,8 @@