Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions client/src/app/openslides-main-module/interceptors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Provider } from '@angular/core';

import { AuthTokenService } from '../../site/services/auth-token.service';
import { AuthTokenInterceptor } from './auth-token.interceptor';
import { UserHeaderInterceptor } from './user-header.interceptor';

export const httpInterceptorProviders: Provider[] = [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthTokenInterceptor,
useClass: UserHeaderInterceptor,
deps: [AuthTokenService],
multi: true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, tap } from 'rxjs';

import { AuthTokenService } from '../../site/services/auth-token.service';

@Injectable()
export class UserHeaderInterceptor implements HttpInterceptor {
public constructor(private authTokenService: AuthTokenService) {}

public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
tap({
next: httpEvent => {
if (httpEvent instanceof HttpResponse && httpEvent.headers.get(`x-user-id`) !== null) {
this.authTokenService.setUserId(+httpEvent.headers.get(`x-user-id`));
}
},
error: (_error: unknown) => {}
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
</mat-list-item>
<mat-divider></mat-divider>
}

<a mat-list-item [href]="[keycloakPasswordResetLink]">
{{ "Change Password" | translate }}

Check failure on line 19 in client/src/app/site/modules/global-headbar/components/account-dialog/account-dialog.component.html

View workflow job for this annotation

GitHub Actions / build-and-test-dev-image

Replace `{{·"Change·Password"` with `·{{·'Change·Password'`
</a>
<mat-divider></mat-divider>
</mat-nav-list>
</div>
<mat-divider [vertical]="true" />
Expand All @@ -26,9 +31,6 @@
@case (menuItemsRef.SHOW_MEETINGS) {
<ng-container [ngTemplateOutlet]="showMeetingsView"></ng-container>
}
@case (menuItemsRef.CHANGE_PASSWORD) {
<ng-container [ngTemplateOutlet]="changePasswordView"></ng-container>
}
}
</div>
</div>
Expand Down Expand Up @@ -79,17 +81,3 @@
</cdk-virtual-scroll-viewport>
</div>
</ng-template>

<ng-template #changePasswordView>
<h2>{{ 'Change password' | translate }}</h2>
<os-password-form
#changePasswordComponent
(changeEvent)="userPasswordForm = $event"
(validEvent)="isUserPasswordValid = $event"
/>
<div>
<button mat-button [disabled]="!isUserPasswordValid" (click)="changePassword()">
{{ 'Save' | translate }}
</button>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslateService } from '@ngx-translate/core';
import { Permission } from 'src/app/domain/definitions/permission';
import { PasswordForm, PasswordFormComponent } from 'src/app/site/modules/user-components';
import { ViewGroup } from 'src/app/site/pages/meetings/pages/participants';
import { MeetingControllerService } from 'src/app/site/pages/meetings/services/meeting-controller.service';
import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting';
import { PERSONAL_FORM_CONTROLS, ViewUser } from 'src/app/site/pages/meetings/view-models/view-user';
import { AuthService } from 'src/app/site/services/auth.service';
import { OperatorService } from 'src/app/site/services/operator.service';
import { UserService } from 'src/app/site/services/user.service';
import { UserControllerService } from 'src/app/site/services/user-controller.service';
Expand Down Expand Up @@ -40,9 +37,6 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {
},
{
name: MenuItems.SHOW_MEETINGS
},
{
name: MenuItems.CHANGE_PASSWORD
}
];

Expand Down Expand Up @@ -86,6 +80,21 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {
return this._isEditing;
}

public get keycloakPasswordResetLink(): string {
const instance_url = 'http://localhost:8080';
const openslides_realm = 'openslides';
const redirect_uri = 'https://localhost:8000/';

return (
instance_url +
'/realms/' +
openslides_realm +
'/protocol/openid-connect/auth?client_id=proxy-client&redirect_uri=' +
redirect_uri +
'&response_type=code&scope=openid&kc_action=UPDATE_PASSWORD'
);
}

public isUserFormValid = false;
public isUserPasswordValid = false;
public userPersonalForm: any;
Expand All @@ -100,10 +109,7 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {
private operator: OperatorService,
private repo: UserControllerService,
private meetingRepo: MeetingControllerService,
private userService: UserService,
private snackbar: MatSnackBar,
private authService: AuthService,
private translate: TranslateService
private userService: UserService
) {
super();
}
Expand All @@ -124,8 +130,6 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {
if (event.key === `Enter` && event.shiftKey) {
if (this.activeMenuItem === MenuItems.SHOW_PROFILE && this.isEditing && this.isUserFormValid) {
this.saveUserChanges();
} else if (this.activeMenuItem === MenuItems.CHANGE_PASSWORD && this.isUserPasswordValid) {
this.changePassword();
}
}
}
Expand All @@ -151,27 +155,6 @@ export class AccountDialogComponent extends BaseUiComponent implements OnInit {
return this.self!.groups(meeting.id);
}

public async changePassword(): Promise<void> {
const { oldPassword, newPassword }: PasswordForm = this.userPasswordForm;

this.authService
.invalidateSessionAfter(() => this.repo.setPasswordSelf(this.self!, oldPassword, newPassword))
.then(() => {
this.snackbar.open(this.translate.instant(`Password changed successfully!`), `Ok`);
this.changePasswordComponent.reset();
this.dialogRef.close();
})
.catch(e => {
if (e?.message) {
this.snackbar.open(this.translate.instant(e.message), this.translate.instant(`OK`), {
duration: 0
});
}

console.log(e);
});
}

public async saveUserChanges(): Promise<void> {
if (this.self) {
const payload = this.userPersonalForm;
Expand Down
6 changes: 5 additions & 1 deletion client/src/app/site/pages/login/login-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const routes: Routes = [
children: [
{
path: ``,
loadChildren: () => import(`./pages/login-mask/login-mask.module`).then(m => m.LoginMaskModule)
pathMatch: 'full',
redirectTo: (): null => {
window.location.href = '/system/login';
return null;
}
},
{
path: `legalnotice`,
Expand Down
8 changes: 4 additions & 4 deletions client/src/app/site/services/auth-token.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ describe(`AuthTokenService`, () => {
service = TestBed.inject(AuthTokenService);
});

it(`should be created with empty token`, () => {
xit(`should be created with empty token`, () => {
expect(service).toBeTruthy();
expect(service.rawAccessToken).toBe(null);
expect(service.accessToken).toBe(null);
});

it(`check observable`, () => {
xit(`check observable`, () => {
const token: AuthToken = { expiresAt: new Date(), userId: 1, sessionId: `test`, iat: 5, exp: 12 };
let test_token: AuthToken | null;
const raw_token = `UNKNOWN.` + btoa(JSON.stringify(token));
Expand All @@ -26,15 +26,15 @@ describe(`AuthTokenService`, () => {
expect(JSON.stringify(test_token)).toEqual(JSON.stringify(token));
});

it(`set accessToken with setRawAccessToken`, () => {
xit(`set accessToken with setRawAccessToken`, () => {
const token = { expiresAt: new Date(), userId: 1, sessionId: `test`, iat: 5, exp: 12 };
const raw_token = `UNKNOWN.` + btoa(JSON.stringify(token));
service.setRawAccessToken(raw_token);
expect(service.rawAccessToken).toBe(raw_token);
expect(JSON.stringify(service.accessToken)).toEqual(JSON.stringify(token));
});

it(`set empty access token with setRawAccessToken`, () => {
xit(`set empty access token with setRawAccessToken`, () => {
const token = { expiresAt: new Date(), userId: 1, sessionId: `test`, iat: 5, exp: 12 };
const raw_token = `UNKNOWN.` + btoa(JSON.stringify(token));
service.setRawAccessToken(raw_token);
Expand Down
24 changes: 24 additions & 0 deletions client/src/app/site/services/auth-token.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,52 @@ import { AuthToken } from '../../domain/interfaces/auth-token';
providedIn: `root`
})
export class AuthTokenService {
/* @deprecated */
public get rawAccessToken(): string | null {
return this._rawAccessToken;
}

/* @deprecated */
public get accessTokenObservable(): Observable<AuthToken | null> {
return this._accessTokenSubject;
}

/* @deprecated */
public get accessToken(): AuthToken | null {
return this._accessTokenSubject.getValue();
}

public get userIdObservable(): Observable<number | null> {
return this._userIdSubject;
}

public get userId(): number | null {
return this._userIdSubject.getValue();
}

// Use undefined as the state of not being initialized
private _accessTokenSubject = new BehaviorSubject<AuthToken | null>(null);
private _rawAccessToken: string | null = null;

private _userIdSubject = new BehaviorSubject<number | null>(null);

/* @deprecated */
public setRawAccessToken(rawToken: string | null): void {
this._rawAccessToken = rawToken;
const token = this.parseToken(rawToken);
this._accessTokenSubject.next(token);
}

public setUserId(id: number): void {
if (!id) {
id = null;
}

if (id !== this.userId) {
this._userIdSubject.next(id);
}
}

private parseToken(rawToken: string | null): AuthToken | null {
if (!rawToken) {
return null;
Expand Down
Loading
Loading