Created auth module. Created Visitor auth guard for non-logged in users.

This commit is contained in:
Tom
2025-01-07 17:56:11 +00:00
parent 96441946b6
commit 11dfde9a03
20 changed files with 144 additions and 103 deletions

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { LoginComponent } from './login/login.component';
import { TtsLoginComponent } from './tts-login/tts-login.component';
import { ImpersonationComponent } from './impersonation/impersonation.component';
@NgModule({
declarations: [],
imports: [
LoginComponent,
TtsLoginComponent,
ImpersonationComponent
]
})
export class AuthModule { }

View File

@ -0,0 +1,19 @@
@if (isAdmin()) {
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title> Impersonation</mat-card-title>
<mat-card-subtitle>Impersonate as another user</mat-card-subtitle>
</mat-card-header>
<mat-card-actions>
<mat-form-field>
<mat-label>User to impersonate</mat-label>
<mat-select (selectionChange)="onChange($event)" [(value)]="impersonated">
<mat-option>{{getUsername()}}</mat-option>
@for (user of users; track user.id) {
<mat-option [value]="user.id">{{ user.name }}</mat-option>
}
</mat-select>
</mat-form-field>
</mat-card-actions>
</mat-card>
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ImpersonationComponent } from './impersonation.component';
describe('ImpersonationComponent', () => {
let component: ImpersonationComponent;
let fixture: ComponentFixture<ImpersonationComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ImpersonationComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ImpersonationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,82 @@
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { ApiAuthenticationService } from '../../shared/services/api/api-authentication.service';
import { MatCardModule } from '@angular/material/card';
import { MatSelectModule } from '@angular/material/select';
import { HttpClient } from '@angular/common/http';
import { isPlatformBrowser } from '@angular/common';
import { environment } from '../../../environments/environment';
import EventService from '../../shared/services/EventService';
import { HermesClientService } from '../../hermes-client.service';
import { Router } from '@angular/router';
@Component({
selector: 'impersonation',
standalone: true,
imports: [MatCardModule, MatSelectModule],
templateUrl: './impersonation.component.html',
styleUrl: './impersonation.component.scss'
})
export class ImpersonationComponent implements OnInit {
impersonated: string | undefined;
users: { id: string, name: string }[];
constructor(private hermes: HermesClientService, private auth: ApiAuthenticationService, private router: Router, private events: EventService, private http: HttpClient, @Inject(PLATFORM_ID) private platformId: Object) {
this.users = []
}
ngOnInit(): void {
if (!isPlatformBrowser(this.platformId)) {
return;
}
this.http.get(environment.API_HOST + '/admin/users', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
}
}).subscribe((data: any) => {
this.users = data.filter((d: any) => d.name != this.auth.getUsername());
const id = this.auth.getImpersonatedId();
if (id && this.users.find(u => u.id == id)) {
this.impersonated = id;
}
});
}
public isAdmin() {
return this.auth.isAdmin();
}
public getUsername() {
return this.auth.getUsername();
}
public onChange(e: any) {
console.log('impersonate befre', e.value);
if (!e.value) {
this.http.delete(environment.API_HOST + '/admin/impersonate', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
},
body: {
impersonation: e.value
}
}).subscribe((data: any) => {
this.hermes.disconnect();
this.events.emit('impersonation', e.value);
this.router.navigate(['/tts-login']);
});
} else {
this.http.put(environment.API_HOST + '/admin/impersonate', {
impersonation: e.value
}, {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
}
}).subscribe((data: any) => {
this.hermes.disconnect();
this.events.emit('impersonation', e.value);
this.router.navigate(['/tts-login']);
});
}
}
}

View File

@ -0,0 +1,21 @@
<div class="login">
<div></div>
<mat-card class="outer" appearance="outlined">
<mat-card-header>
<h1>Login</h1>
</mat-card-header>
<mat-card-content>
<p>Log in with your favorite livestream service</p>
<a>
<mat-card appearance="outlined" (click)="login()">
<mat-card-content>
Twitch
</mat-card-content>
</mat-card>
</a>
</mat-card-content>
</mat-card>
<div></div>
</div>

View File

@ -0,0 +1,13 @@
.login {
display: grid;
grid-template-columns: 1fr max-content 1fr;
place-items: center;
}
.mat-mdc-card-header {
align-self: center;
}
.mat-mdc-card-content {
align-self: center;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoginComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,33 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { Router, RouterModule } from '@angular/router';
import { Subscription } from 'rxjs';
import { environment } from '../../../environments/environment';
@Component({
selector: 'login',
standalone: true,
imports: [MatCardModule, RouterModule],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent implements OnInit, OnDestroy {
subscription: Subscription | null;
constructor(private router: Router) {
this.subscription = null;
}
ngOnInit(): void {
}
ngOnDestroy(): void {
if (this.subscription)
this.subscription.unsubscribe()
}
login() {
document.location.replace(environment.API_HOST + '/auth');
}
}

View File

@ -0,0 +1,14 @@
<h4>TTS Login</h4>
<div class="main-div">
<mat-card class="main-card">
<mat-form-field>
<mat-label>API Key</mat-label>
<mat-select [(value)]="selected_api_key">
@for (key of api_keys; track key.id) {
<mat-option [value]="key.id">{{key.label}}</mat-option>
}
</mat-select>
</mat-form-field>
<button mat-raised-button (click)="login()">Log In</button>
</mat-card>
</div>

View File

@ -0,0 +1,14 @@
.main-div {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
h4 {
text-align: center;
}
.main-card {
width: 20%;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TtsLoginComponent } from './tts-login.component';
describe('TtsLoginComponent', () => {
let component: TtsLoginComponent;
let fixture: ComponentFixture<TtsLoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TtsLoginComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TtsLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,70 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import EventService from '../../shared/services/EventService';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { environment } from '../../../environments/environment';
import { HermesClientService } from '../../hermes-client.service';
import { MatCard } from '@angular/material/card';
@Component({
selector: 'tts-login',
standalone: true,
imports: [MatButtonModule, MatCard, MatFormFieldModule, MatSelectModule, MatInputModule, FormsModule],
templateUrl: './tts-login.component.html',
styleUrl: './tts-login.component.scss'
})
export class TtsLoginComponent implements OnInit, OnDestroy {
api_keys: { id: string, label: string }[];
selected_api_key: string|undefined;
private subscription: Subscription|undefined;
constructor(private hermes: HermesClientService, private events: EventService, private http: HttpClient, private router: Router) {
this.api_keys = [];
}
ngOnInit(): void {
this.http.get(environment.API_HOST + '/keys', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
}
}).subscribe((data: any) => this.api_keys = data);
this.subscription = this.events.listen('tts_login_ack', _ => {
if (document.location.href.includes('/tts-login')) {
this.router.navigate(['/policies'])
}
});
this.events.listen('tts_logoff', _ => {
this.selected_api_key = undefined;
});
this.events.listen('impersonation', _ => {
this.selected_api_key = undefined;
this.http.get(environment.API_HOST + '/keys', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
}
}).subscribe((data: any) => this.api_keys = data);
});
}
ngOnDestroy(): void {
if (this.subscription)
this.subscription.unsubscribe();
}
login() {
console.log('api key for login', this.selected_api_key)
if (!this.selected_api_key)
return;
this.hermes.login(this.selected_api_key);
}
}