Resolve "Auth providers should be ready on components init"

This commit is contained in:
Jovan Krunić
2022-05-13 09:05:33 +00:00
parent 28caaf1d21
commit b7ae2cf019
11 changed files with 77 additions and 96 deletions

View File

@@ -5,5 +5,6 @@
"screenshotsFolder": "cypress/screenshots", "screenshotsFolder": "cypress/screenshots",
"pluginsFile": "cypress/plugins/index.ts", "pluginsFile": "cypress/plugins/index.ts",
"fixturesFolder": "cypress/fixtures", "fixturesFolder": "cypress/fixtures",
"baseUrl": "http://localhost:4200" "baseUrl": "http://localhost:4200",
"defaultCommandTimeout": 10000
} }

View File

@@ -89,7 +89,7 @@ export class AppComponent implements AfterContentInit {
if (Capacitor.isNativePlatform()) { if (Capacitor.isNativePlatform()) {
await StatusBar.setStyle({style: Style.Dark}); await StatusBar.setStyle({style: Style.Dark});
} }
await this.authInit(); await this.authNotificationsInit();
// set order of categories in settings // set order of categories in settings
this.settingsProvider.setCategoriesOrder([ this.settingsProvider.setCategoriesOrder([
@@ -101,9 +101,7 @@ export class AppComponent implements AfterContentInit {
}); });
} }
private async authInit() { private async authNotificationsInit() {
await this.authHelper.getProvider('default').init();
await this.authHelper.getProvider('paia').init();
this.authHelper this.authHelper
.getProvider('default') .getProvider('default')
.events$.subscribe(action => .events$.subscribe(action =>

View File

@@ -66,6 +66,8 @@ import {StorageProvider} from './modules/storage/storage.provider';
import {AssessmentsModule} from './modules/assessments/assessments.module'; import {AssessmentsModule} from './modules/assessments/assessments.module';
import {RoutingStackService} from './util/routing-stack.service'; import {RoutingStackService} from './util/routing-stack.service';
import {SCSettingValue} from '@openstapps/core'; import {SCSettingValue} from '@openstapps/core';
import {DefaultAuthService} from './modules/auth/default-auth.service';
import {PAIAAuthService} from './modules/auth/paia/paia-auth.service';
registerLocaleData(localeDe); registerLocaleData(localeDe);
@@ -86,6 +88,8 @@ export function initializerFactory(
configProvider: ConfigProvider, configProvider: ConfigProvider,
translateService: TranslateService, translateService: TranslateService,
_routingStackService: RoutingStackService, _routingStackService: RoutingStackService,
defaultAuthService: DefaultAuthService,
paiaAuthService: PAIAAuthService,
) { ) {
return async () => { return async () => {
initLogger(logger); initLogger(logger);
@@ -109,6 +113,8 @@ export function initializerFactory(
translateService.setDefaultLang('en'); translateService.setDefaultLang('en');
translateService.use(languageCode); translateService.use(languageCode);
moment.locale(languageCode); moment.locale(languageCode);
await defaultAuthService.init();
await paiaAuthService.init();
} catch (error) { } catch (error) {
logger.warn(error); logger.warn(error);
} }
@@ -196,6 +202,8 @@ export function createTranslateLoader(http: HttpClient) {
ConfigProvider, ConfigProvider,
TranslateService, TranslateService,
RoutingStackService, RoutingStackService,
DefaultAuthService,
PAIAAuthService,
], ],
useFactory: initializerFactory, useFactory: initializerFactory,
}, },

View File

@@ -2,18 +2,17 @@ import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {Platform} from '@ionic/angular'; import {Platform} from '@ionic/angular';
import {Requestor, StorageBackend} from '@openid/appauth'; import {Requestor, StorageBackend} from '@openid/appauth';
import {authFactory, paiaAuthFactory, storageFactory} from './factories'; import {storageFactory} from './factories';
import {DefaultAuthService} from './default-auth.service';
import {Browser} from 'ionic-appauth'; import {Browser} from 'ionic-appauth';
import {CapacitorBrowser} from 'ionic-appauth/lib/capacitor'; import {CapacitorBrowser} from 'ionic-appauth/lib/capacitor';
import {httpFactory} from './factories/http.factory'; import {httpFactory} from './factories/http.factory';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {PAIAAuthService} from './paia/paia-auth.service';
import {AuthRoutingModule} from './auth-routing.module'; import {AuthRoutingModule} from './auth-routing.module';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule} from '@ngx-translate/core';
import {ConfigProvider} from '../config/config.provider';
import {AuthCallbackPageComponent} from './auth-callback/page/auth-callback-page.component'; import {AuthCallbackPageComponent} from './auth-callback/page/auth-callback-page.component';
import {PAIAAuthCallbackPageComponent} from './paia/auth-callback/page/paiaauth-callback-page.component'; import {PAIAAuthCallbackPageComponent} from './paia/auth-callback/page/paiaauth-callback-page.component';
import {DefaultAuthService} from './default-auth.service';
import {PAIAAuthService} from './paia/paia-auth.service';
@NgModule({ @NgModule({
declarations: [AuthCallbackPageComponent, PAIAAuthCallbackPageComponent], declarations: [AuthCallbackPageComponent, PAIAAuthCallbackPageComponent],
@@ -33,16 +32,8 @@ import {PAIAAuthCallbackPageComponent} from './paia/auth-callback/page/paiaauth-
provide: Browser, provide: Browser,
useClass: CapacitorBrowser, useClass: CapacitorBrowser,
}, },
{ DefaultAuthService,
provide: DefaultAuthService, PAIAAuthService,
useFactory: authFactory,
deps: [Requestor, Browser, StorageBackend, ConfigProvider],
},
{
provide: PAIAAuthService,
useFactory: paiaAuthFactory,
deps: [Requestor, Browser, StorageBackend, ConfigProvider],
},
], ],
}) })
export class AuthModule {} export class AuthModule {}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2021 StApps * Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -13,68 +13,20 @@
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { import {AuthorizationServiceConfigurationJson} from '@openid/appauth';
StorageBackend, import {IAuthConfig} from 'ionic-appauth';
Requestor,
AuthorizationServiceConfiguration,
AuthorizationServiceConfigurationJson,
} from '@openid/appauth';
import {Browser, IAuthConfig} from 'ionic-appauth';
import {PAIAAuthService} from '../paia/paia-auth.service';
import {ConfigProvider} from '../../config/config.provider';
import { import {
SCAuthorizationProvider, SCAuthorizationProvider,
SCAuthorizationProviderType, SCAuthorizationProviderType,
} from '@openstapps/core'; } from '@openstapps/core';
import {DefaultAuthService} from '../default-auth.service';
import {Capacitor} from '@capacitor/core'; import {Capacitor} from '@capacitor/core';
import {authPaths} from '../auth-paths'; import {authPaths} from './auth-paths';
import {environment} from '../../../../environments/environment'; import {environment} from '../../../environments/environment';
export const authFactory = (
requestor: Requestor,
browser: Browser,
storage: StorageBackend,
configProvider: ConfigProvider,
) => {
const authService = new DefaultAuthService(browser, storage, requestor);
const authConfig = configProvider.getAnyValue('auth') as {
default: SCAuthorizationProvider;
};
authService.authConfig = getClientConfig('default', authConfig);
authService.localConfiguration = new AuthorizationServiceConfiguration(
getEndpointsConfig('default', authConfig),
);
return authService;
};
export const paiaAuthFactory = (
requestor: Requestor,
browser: Browser,
storage: StorageBackend,
configProvider: ConfigProvider,
) => {
const authService = new PAIAAuthService(browser, storage, requestor);
const authConfig = configProvider.getAnyValue('auth') as {
paia: SCAuthorizationProvider;
};
authService.authConfig = getClientConfig('paia', authConfig);
authService.localConfiguration = new AuthorizationServiceConfiguration(
getEndpointsConfig('paia', authConfig),
);
return authService;
};
/** /**
* Get configuration of an OAuth2 client * Get configuration of an OAuth2 client
*/ */
function getClientConfig( export function getClientConfig(
providerType: SCAuthorizationProviderType, providerType: SCAuthorizationProviderType,
authConfig: { authConfig: {
default?: SCAuthorizationProvider; default?: SCAuthorizationProvider;
@@ -95,7 +47,7 @@ function getClientConfig(
/** /**
* Get configuration about endpoints of an OAuth2 server * Get configuration about endpoints of an OAuth2 server
*/ */
function getEndpointsConfig( export function getEndpointsConfig(
providerType: SCAuthorizationProviderType, providerType: SCAuthorizationProviderType,
authConfig: { authConfig: {
default?: SCAuthorizationProvider; default?: SCAuthorizationProvider;

View File

@@ -15,9 +15,16 @@ import {
AuthService, AuthService,
AuthActionBuilder, AuthActionBuilder,
} from 'ionic-appauth'; } from 'ionic-appauth';
import {ConfigProvider} from '../config/config.provider';
import {SCAuthorizationProvider} from '@openstapps/core';
import {getClientConfig, getEndpointsConfig} from './auth.provider.methods';
import {Injectable} from '@angular/core';
const TOKEN_RESPONSE_KEY = 'token_response'; const TOKEN_RESPONSE_KEY = 'token_response';
@Injectable({
providedIn: 'root',
})
export class DefaultAuthService extends AuthService { export class DefaultAuthService extends AuthService {
public localConfiguration: AuthorizationServiceConfiguration; public localConfiguration: AuthorizationServiceConfiguration;
@@ -33,6 +40,7 @@ export class DefaultAuthService extends AuthService {
protected browser: Browser = new DefaultBrowser(), protected browser: Browser = new DefaultBrowser(),
protected storage: StorageBackend = new LocalStorageBackend(), protected storage: StorageBackend = new LocalStorageBackend(),
protected requestor: Requestor = new JQueryRequestor(), protected requestor: Requestor = new JQueryRequestor(),
private readonly configProvider: ConfigProvider,
) { ) {
super(browser, storage, requestor); super(browser, storage, requestor);
} }
@@ -44,6 +52,16 @@ export class DefaultAuthService extends AuthService {
return Promise.resolve(this.localConfiguration); return Promise.resolve(this.localConfiguration);
} }
setupConfiguration() {
const authConfig = this.configProvider.getAnyValue('auth') as {
default: SCAuthorizationProvider;
};
this.authConfig = getClientConfig('default', authConfig);
this.localConfiguration = new AuthorizationServiceConfiguration(
getEndpointsConfig('default', authConfig),
);
}
public async signOut() { public async signOut() {
await this.storage.removeItem(TOKEN_RESPONSE_KEY).catch(error => { await this.storage.removeItem(TOKEN_RESPONSE_KEY).catch(error => {
this.notifyActionListers(AuthActionBuilder.SignOutFailed(error)); this.notifyActionListers(AuthActionBuilder.SignOutFailed(error));

View File

@@ -1,3 +1,2 @@
export * from './auth.factory';
export * from './browser.factory'; export * from './browser.factory';
export * from './storage.factory'; export * from './storage.factory';

View File

@@ -31,6 +31,10 @@ import {PAIAAuthorizationResponse} from './paia-authorization-response';
import {PAIAAuthorizationNotifier} from './paia-authorization-notifier'; import {PAIAAuthorizationNotifier} from './paia-authorization-notifier';
import {PAIATokenResponse} from './paia-token-response'; import {PAIATokenResponse} from './paia-token-response';
import {IPAIAAuthAction, PAIAAuthActionBuilder} from './paia-auth-action'; import {IPAIAAuthAction, PAIAAuthActionBuilder} from './paia-auth-action';
import {SCAuthorizationProvider} from '@openstapps/core';
import {ConfigProvider} from '../../config/config.provider';
import {getClientConfig, getEndpointsConfig} from '../auth.provider.methods';
import {Injectable} from '@angular/core';
const TOKEN_KEY = 'auth_paia_token'; const TOKEN_KEY = 'auth_paia_token';
const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds
@@ -44,7 +48,10 @@ export interface IAuthService {
getValidToken(buffer?: number): Promise<PAIATokenResponse>; getValidToken(buffer?: number): Promise<PAIATokenResponse>;
} }
export class PAIAAuthService implements IAuthService { @Injectable({
providedIn: 'root',
})
export class PAIAAuthService {
private _authConfig?: IAuthConfig; private _authConfig?: IAuthConfig;
private _authSubject: AuthSubject = new AuthSubject(); private _authSubject: AuthSubject = new AuthSubject();
@@ -78,14 +85,14 @@ export class PAIAAuthService implements IAuthService {
protected browser: Browser = new DefaultBrowser(), protected browser: Browser = new DefaultBrowser(),
protected storage: StorageBackend = new LocalStorageBackend(), protected storage: StorageBackend = new LocalStorageBackend(),
protected requestor: Requestor = new JQueryRequestor(), protected requestor: Requestor = new JQueryRequestor(),
utils = new BasicQueryStringUtils(), private readonly configProvider: ConfigProvider,
) { ) {
this.tokenHandler = new PAIATokenRequestHandler(requestor); this.tokenHandler = new PAIATokenRequestHandler(requestor);
this.userInfoHandler = new IonicUserInfoHandler(requestor); this.userInfoHandler = new IonicUserInfoHandler(requestor);
this.requestHandler = new PAIAAuthorizationRequestHandler( this.requestHandler = new PAIAAuthorizationRequestHandler(
browser, browser,
storage, storage,
utils, new BasicQueryStringUtils(),
crypto, crypto,
); );
this.endSessionHandler = new IonicEndSessionHandler(browser); this.endSessionHandler = new IonicEndSessionHandler(browser);
@@ -130,8 +137,19 @@ export class PAIAAuthService implements IAuthService {
} }
public async init() { public async init() {
this.setupConfiguration();
this.setupAuthorizationNotifier(); this.setupAuthorizationNotifier();
this.loadTokenFromStorage(); await this.loadTokenFromStorage();
}
setupConfiguration() {
const authConfig = this.configProvider.getAnyValue('auth') as {
paia: SCAuthorizationProvider;
};
this.authConfig = getClientConfig('paia', authConfig);
this.localConfiguration = new AuthorizationServiceConfiguration(
getEndpointsConfig('paia', authConfig),
);
} }
protected notifyActionListers(action: IPAIAAuthAction) { protected notifyActionListers(action: IPAIAAuthAction) {

View File

@@ -53,7 +53,7 @@ export type ICalLike = ICalKeyValuePair[];
/** /**
* *
*/ */
function timeDist( function timeDistance(
current: SCISO8601Date, current: SCISO8601Date,
next: SCISO8601Date | undefined, next: SCISO8601Date | undefined,
recurrence: unitOfTime.Diff, recurrence: unitOfTime.Diff,
@@ -127,11 +127,11 @@ export function findRRules(
const freq = minBy( const freq = minBy(
units.map(recurrence => ({ units.map(recurrence => ({
recurrence: recurrence, recurrence: recurrence,
dist: timeDist(current, next, recurrence), dist: timeDistance(current, next, recurrence),
})), })),
it => it.dist, it => it.dist,
)?.recurrence; )?.recurrence;
const interval = freq ? timeDist(current, next, freq) : undefined; const interval = freq ? timeDistance(current, next, freq) : undefined;
if (element?.interval === -1) { if (element?.interval === -1) {
element.freq = freq; element.freq = freq;

View File

@@ -22,6 +22,7 @@ import {
} from '@openstapps/core'; } from '@openstapps/core';
import {NavigationService} from './navigation.service'; import {NavigationService} from './navigation.service';
import config from 'capacitor.config'; import config from 'capacitor.config';
import {SettingsProvider} from '../../settings/settings.provider';
/** /**
* Generated class for the MenuPage page. * Generated class for the MenuPage page.
@@ -43,23 +44,13 @@ export class NavigationComponent implements OnInit {
/** /**
* Possible languages to be used for translation * Possible languages to be used for translation
*/ */
language: keyof SCTranslations<SCLanguage> = 'en'; language: keyof SCTranslations<SCLanguage>;
/** /**
* Menu entries from config module * Menu entries from config module
*/ */
menu: SCAppConfigurationMenuCategory[]; menu: SCAppConfigurationMenuCategory[];
/**
* Possible languages to be used for translation
*/
public pages = [
{title: 'Search', url: '/search', icon: 'search'},
{title: 'Hebis Search', url: '/hebis-search', icon: 'search'},
{title: 'Schedule', url: '/schedule', icon: 'calendar'},
{title: 'Settings', url: '/settings', icon: 'settings'},
];
/** /**
* Core translator * Core translator
*/ */
@@ -68,15 +59,20 @@ export class NavigationComponent implements OnInit {
constructor( constructor(
public translateService: TranslateService, public translateService: TranslateService,
private navigationService: NavigationService, private navigationService: NavigationService,
private settingsProvider: SettingsProvider,
) { ) {
translateService.onLangChange.subscribe((event: LangChangeEvent) => { translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<SCLanguage>; this.language = event.lang as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language); this.translator = new SCThingTranslator(this.language);
}); });
this.translator = new SCThingTranslator('en');
} }
async ngOnInit() { async ngOnInit() {
this.language = (await this.settingsProvider.getValue(
'profile',
'language',
)) as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language);
this.menu = await this.navigationService.getMenu(); this.menu = await this.navigationService.getMenu();
} }
} }

View File

@@ -11,7 +11,7 @@
</ion-title> </ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content> <ion-content *ngIf="menu">
<ion-list *ngFor="let category of menu"> <ion-list *ngFor="let category of menu">
<ion-list-header *ngIf="category.name !== ''"> <ion-list-header *ngIf="category.name !== ''">
<ion-label> <ion-label>