mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-03 12:02:53 +00:00
committed by
Jovan Krunić
parent
07e5c80223
commit
580ebee362
@@ -86,6 +86,8 @@ export abstract class AuthService implements IAuthService {
|
|||||||
|
|
||||||
private _authenticatedSubject = new BehaviorSubject<boolean>(false);
|
private _authenticatedSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
private _loggedInSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
private _initComplete = new BehaviorSubject<boolean>(false);
|
private _initComplete = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
protected tokenHandler: TokenRequestHandler;
|
protected tokenHandler: TokenRequestHandler;
|
||||||
@@ -133,6 +135,13 @@ export abstract class AuthService implements IAuthService {
|
|||||||
return this._authenticatedSubject.asObservable();
|
return this._authenticatedSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to isAuthenticated$, but will also return true if the token is expired
|
||||||
|
*/
|
||||||
|
get isLoggedIn$(): Observable<boolean> {
|
||||||
|
return this._loggedInSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
get initComplete$(): Observable<boolean> {
|
get initComplete$(): Observable<boolean> {
|
||||||
return this._initComplete.asObservable();
|
return this._initComplete.asObservable();
|
||||||
}
|
}
|
||||||
@@ -183,19 +192,20 @@ export abstract class AuthService implements IAuthService {
|
|||||||
protected notifyActionListers(action: IAuthAction) {
|
protected notifyActionListers(action: IAuthAction) {
|
||||||
/* eslint-disable unicorn/no-useless-undefined */
|
/* eslint-disable unicorn/no-useless-undefined */
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case AuthActions.RefreshFailed:
|
|
||||||
case AuthActions.SignInFailed:
|
case AuthActions.SignInFailed:
|
||||||
case AuthActions.SignOutSuccess:
|
case AuthActions.SignOutSuccess:
|
||||||
case AuthActions.SignOutFailed: {
|
case AuthActions.SignOutFailed: {
|
||||||
this._tokenSubject.next(undefined);
|
this._tokenSubject.next(undefined);
|
||||||
this._userSubject.next(undefined);
|
this._userSubject.next(undefined);
|
||||||
this._authenticatedSubject.next(false);
|
this._authenticatedSubject.next(false);
|
||||||
|
this._loggedInSubject.next(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthActions.LoadTokenFromStorageFailed: {
|
case AuthActions.LoadTokenFromStorageFailed: {
|
||||||
this._tokenSubject.next(undefined);
|
this._tokenSubject.next(undefined);
|
||||||
this._userSubject.next(undefined);
|
this._userSubject.next(undefined);
|
||||||
this._authenticatedSubject.next(false);
|
this._authenticatedSubject.next(false);
|
||||||
|
this._loggedInSubject.next(false);
|
||||||
this._initComplete.next(true);
|
this._initComplete.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -203,11 +213,13 @@ export abstract class AuthService implements IAuthService {
|
|||||||
case AuthActions.RefreshSuccess: {
|
case AuthActions.RefreshSuccess: {
|
||||||
this._tokenSubject.next(action.tokenResponse);
|
this._tokenSubject.next(action.tokenResponse);
|
||||||
this._authenticatedSubject.next(true);
|
this._authenticatedSubject.next(true);
|
||||||
|
this._loggedInSubject.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthActions.LoadTokenFromStorageSuccess: {
|
case AuthActions.LoadTokenFromStorageSuccess: {
|
||||||
this._tokenSubject.next(action.tokenResponse);
|
this._tokenSubject.next(action.tokenResponse);
|
||||||
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
||||||
|
this._loggedInSubject.next(true);
|
||||||
this._initComplete.next(true);
|
this._initComplete.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -442,7 +454,6 @@ export abstract class AuthService implements IAuthService {
|
|||||||
|
|
||||||
public async refreshToken() {
|
public async refreshToken() {
|
||||||
await this.requestTokenRefresh().catch(error => {
|
await this.requestTokenRefresh().catch(error => {
|
||||||
this.storage.removeItem(TOKEN_RESPONSE_KEY);
|
|
||||||
this.notifyActionListers(AuthActionBuilder.RefreshFailed(error));
|
this.notifyActionListers(AuthActionBuilder.RefreshFailed(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ export class PAIAAuthService {
|
|||||||
|
|
||||||
private _authenticatedSubject = new BehaviorSubject<boolean>(false);
|
private _authenticatedSubject = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
|
private _loggedIn = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
private _initComplete = new BehaviorSubject<boolean>(false);
|
private _initComplete = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
protected tokenHandler: PAIATokenRequestHandler;
|
protected tokenHandler: PAIATokenRequestHandler;
|
||||||
@@ -118,6 +120,13 @@ export class PAIAAuthService {
|
|||||||
return this._authenticatedSubject.asObservable();
|
return this._authenticatedSubject.asObservable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to isAuthenticated$, but will also return true if the token is expired
|
||||||
|
*/
|
||||||
|
get isLoggedIn$(): Observable<boolean> {
|
||||||
|
return this._loggedIn.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
get initComplete$(): Observable<boolean> {
|
get initComplete$(): Observable<boolean> {
|
||||||
return this._initComplete.asObservable();
|
return this._initComplete.asObservable();
|
||||||
}
|
}
|
||||||
@@ -170,23 +179,27 @@ export class PAIAAuthService {
|
|||||||
this._tokenSubject.next(undefined);
|
this._tokenSubject.next(undefined);
|
||||||
this._userSubject.next(undefined);
|
this._userSubject.next(undefined);
|
||||||
this._authenticatedSubject.next(false);
|
this._authenticatedSubject.next(false);
|
||||||
|
this._loggedIn.next(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthActions.LoadTokenFromStorageFailed: {
|
case AuthActions.LoadTokenFromStorageFailed: {
|
||||||
this._tokenSubject.next(undefined);
|
this._tokenSubject.next(undefined);
|
||||||
this._userSubject.next(undefined);
|
this._userSubject.next(undefined);
|
||||||
this._authenticatedSubject.next(false);
|
this._authenticatedSubject.next(false);
|
||||||
|
this._loggedIn.next(false);
|
||||||
this._initComplete.next(true);
|
this._initComplete.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthActions.SignInSuccess: {
|
case AuthActions.SignInSuccess: {
|
||||||
this._tokenSubject.next(action.tokenResponse);
|
this._tokenSubject.next(action.tokenResponse);
|
||||||
this._authenticatedSubject.next(true);
|
this._authenticatedSubject.next(true);
|
||||||
|
this._loggedIn.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthActions.LoadTokenFromStorageSuccess: {
|
case AuthActions.LoadTokenFromStorageSuccess: {
|
||||||
this._tokenSubject.next(action.tokenResponse);
|
this._tokenSubject.next(action.tokenResponse);
|
||||||
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
this._authenticatedSubject.next((action.tokenResponse as TokenResponse).isValid(0));
|
||||||
|
this._loggedIn.next(true);
|
||||||
this._initComplete.next(true);
|
this._initComplete.next(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ export class IdCardsProvider {
|
|||||||
this.encryptedStorageProvider.get<SCIdCard[]>('id-cards') as Promise<SCIdCard[]>,
|
this.encryptedStorageProvider.get<SCIdCard[]>('id-cards') as Promise<SCIdCard[]>,
|
||||||
).pipe(filter(it => it !== undefined));
|
).pipe(filter(it => it !== undefined));
|
||||||
|
|
||||||
return auth.isAuthenticated$.pipe(
|
return auth.isLoggedIn$.pipe(
|
||||||
mergeMap(isAuthenticated =>
|
mergeMap(isLoggedIn =>
|
||||||
isAuthenticated
|
isLoggedIn
|
||||||
? feature
|
? feature
|
||||||
? storedIdCards.pipe(
|
? storedIdCards.pipe(
|
||||||
concatWith(
|
concatWith(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {SCAuthorizationProviderType} from '@openstapps/core';
|
|||||||
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
|
import {EncryptedStorageProvider} from '../storage/encrypted-storage.provider';
|
||||||
|
|
||||||
class FakeAuth {
|
class FakeAuth {
|
||||||
isAuthenticated$ = new BehaviorSubject(false);
|
isLoggedIn$ = new BehaviorSubject(false);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
getValidToken() {}
|
getValidToken() {}
|
||||||
@@ -42,7 +42,7 @@ describe('IdCards', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should emit network result when logged in', async () => {
|
it('should emit network result when logged in', async () => {
|
||||||
fakeAuth.isAuthenticated$.next(true);
|
fakeAuth.isLoggedIn$.next(true);
|
||||||
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
||||||
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
|
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
|
||||||
const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
|
const provider = new IdCardsProvider(authHelper, configProvider, httpClient, encryptedStorageProvider);
|
||||||
@@ -63,7 +63,7 @@ describe('IdCards', () => {
|
|||||||
expect(await firstValueFrom(observable)).toEqual([]);
|
expect(await firstValueFrom(observable)).toEqual([]);
|
||||||
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
httpClient.get = jasmine.createSpy().and.returnValue(of(['abc']));
|
||||||
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
|
fakeAuth.getValidToken = jasmine.createSpy().and.resolveTo({accessToken: 'fake-token'});
|
||||||
fakeAuth.isAuthenticated$.next(true);
|
fakeAuth.isLoggedIn$.next(true);
|
||||||
// this is counter-intuitive, but because we unsubscribed above the first value
|
// this is counter-intuitive, but because we unsubscribed above the first value
|
||||||
// will now contain the network result.
|
// will now contain the network result.
|
||||||
expect(await firstValueFrom(observable)).toEqual(['abc' as never]);
|
expect(await firstValueFrom(observable)).toEqual(['abc' as never]);
|
||||||
|
|||||||
@@ -6,74 +6,56 @@
|
|||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* FITNESS FOR A PARTICULAr purpose. see the gnu general public license for
|
||||||
* more details.
|
* more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with
|
* you should have received a copy of the gnu general public license along with
|
||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. if not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
import {Component, inject, input} from '@angular/core';
|
||||||
import {SCSection} from '../../../../config/profile-page-sections';
|
import {SCSection} from '../../../../config/profile-page-sections';
|
||||||
import {AuthHelperService} from '../../auth/auth-helper.service';
|
import {AuthHelperService} from '../../auth/auth-helper.service';
|
||||||
import {Observable} from 'rxjs';
|
import {mergeMap, of} from 'rxjs';
|
||||||
import {SCAuthorizationProviderType} from '@openstapps/core';
|
|
||||||
import Swiper from 'swiper';
|
import Swiper from 'swiper';
|
||||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
import {toObservable, toSignal} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'stapps-profile-page-section',
|
selector: 'stapps-profile-page-section',
|
||||||
templateUrl: 'profile-page-section.html',
|
templateUrl: 'profile-page-section.html',
|
||||||
styleUrls: ['profile-page-section.scss'],
|
styleUrls: ['profile-page-section.scss'],
|
||||||
})
|
})
|
||||||
export class ProfilePageSectionComponent implements OnInit {
|
export class ProfilePageSectionComponent {
|
||||||
@Input() item: SCSection;
|
item = input.required<SCSection>();
|
||||||
|
|
||||||
@Input() minSlideWidth = 110;
|
minSlideWidth = input(110);
|
||||||
|
|
||||||
isLoggedIn: boolean;
|
authHelper = inject(AuthHelperService);
|
||||||
|
|
||||||
|
loggedIn = toSignal(
|
||||||
|
toObservable(this.item).pipe(
|
||||||
|
mergeMap(item =>
|
||||||
|
item.authProvider ? this.authHelper.getProvider(item.authProvider).isLoggedIn$ : of(false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
isEnd = false;
|
isEnd = false;
|
||||||
|
|
||||||
isBeginning = true;
|
isBeginning = true;
|
||||||
|
|
||||||
slidesPerView: number;
|
slidesPerView?: number;
|
||||||
|
|
||||||
slidesFillScreen = false;
|
slidesFillScreen = false;
|
||||||
|
|
||||||
data: {
|
|
||||||
[key in SCAuthorizationProviderType]: {loggedIn$: Observable<boolean>};
|
|
||||||
} = {
|
|
||||||
default: {
|
|
||||||
loggedIn$: this.authHelper.getProvider('default').isAuthenticated$,
|
|
||||||
},
|
|
||||||
paia: {
|
|
||||||
loggedIn$: this.authHelper.getProvider('paia').isAuthenticated$,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
destroy$ = inject(DestroyRef);
|
|
||||||
|
|
||||||
constructor(private authHelper: AuthHelperService) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (this.item.authProvider) {
|
|
||||||
this.data[this.item.authProvider].loggedIn$
|
|
||||||
.pipe(takeUntilDestroyed(this.destroy$))
|
|
||||||
.subscribe(loggedIn => {
|
|
||||||
this.isLoggedIn = loggedIn;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activeIndexChange(swiper: Swiper) {
|
activeIndexChange(swiper: Swiper) {
|
||||||
this.isBeginning = swiper.isBeginning;
|
this.isBeginning = swiper.isBeginning;
|
||||||
this.isEnd = swiper.isEnd;
|
this.isEnd = swiper.isEnd;
|
||||||
this.slidesFillScreen = this.slidesPerView >= swiper.slides.length;
|
this.slidesFillScreen = this.slidesPerView! >= swiper.slides.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeSwiper(resizeEvent: ResizeObserverEntry, swiper: Swiper) {
|
resizeSwiper(resizeEvent: ResizeObserverEntry, swiper: Swiper) {
|
||||||
const slidesPerView =
|
const slidesPerView =
|
||||||
Math.floor((resizeEvent.contentRect.width - this.minSlideWidth / 2) / this.minSlideWidth) + 0.5;
|
Math.floor((resizeEvent.contentRect.width - this.minSlideWidth() / 2) / this.minSlideWidth()) + 0.5;
|
||||||
|
|
||||||
if (slidesPerView > 1 && slidesPerView !== this.slidesPerView) {
|
if (slidesPerView > 1 && slidesPerView !== this.slidesPerView) {
|
||||||
this.slidesPerView = slidesPerView;
|
this.slidesPerView = slidesPerView;
|
||||||
@@ -84,16 +66,13 @@ export class ProfilePageSectionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async toggleLogIn() {
|
async toggleLogIn() {
|
||||||
if (!this.item.authProvider) return;
|
const providerType = this.item().authProvider;
|
||||||
await (this.isLoggedIn ? this.signOut(this.item.authProvider) : this.signIn(this.item.authProvider));
|
if (!providerType) return;
|
||||||
}
|
if (this.loggedIn()) {
|
||||||
|
await this.authHelper.getProvider(providerType).signOut();
|
||||||
async signIn(providerType: SCAuthorizationProviderType) {
|
await this.authHelper.endBrowserSession(providerType);
|
||||||
await this.authHelper.getProvider(providerType).signIn();
|
} else {
|
||||||
}
|
await this.authHelper.getProvider(providerType).signIn();
|
||||||
|
}
|
||||||
async signOut(providerType: SCAuthorizationProviderType) {
|
|
||||||
await this.authHelper.getProvider(providerType).signOut();
|
|
||||||
await this.authHelper.endBrowserSession(providerType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,24 +13,24 @@
|
|||||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<stapps-section [title]="'name' | translateSimple: item">
|
<stapps-section [title]="'name' | translateSimple: item()">
|
||||||
@if (item.authProvider) {
|
@if (item().authProvider) {
|
||||||
<ion-button slot="button-end" fill="clear" color="dark" (click)="toggleLogIn()">
|
<ion-button slot="button-end" fill="clear" color="dark" (click)="toggleLogIn()">
|
||||||
@if (isLoggedIn) {
|
@if (loggedIn()) {
|
||||||
<ion-icon slot="end" name="logout"></ion-icon>
|
<ion-icon slot="end" name="logout"></ion-icon>
|
||||||
} @else {
|
} @else {
|
||||||
<ion-icon slot="end" name="login"></ion-icon>
|
<ion-icon slot="end" name="login"></ion-icon>
|
||||||
}
|
}
|
||||||
<ion-label>{{ 'profile.buttons.default.log_' + (isLoggedIn ? 'out' : 'in') | translate }}</ion-label>
|
<ion-label>{{ 'profile.buttons.default.log_' + (loggedIn() ? 'out' : 'in') | translate }}</ion-label>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
}
|
}
|
||||||
|
|
||||||
<simple-swiper>
|
<simple-swiper>
|
||||||
@for (link of item.links; track link) {
|
@for (link of item().links; track link) {
|
||||||
<ion-item
|
<ion-item
|
||||||
lines="none"
|
lines="none"
|
||||||
[routerLink]="link.link"
|
[routerLink]="link.link"
|
||||||
[disabled]="link.needsAuth && !isLoggedIn"
|
[disabled]="link.needsAuth && !loggedIn()"
|
||||||
[detail]="false"
|
[detail]="false"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user