diff options
author | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-12-06 15:09:15 +0100 |
---|---|---|
committer | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-12-06 15:12:24 +0100 |
commit | 152cb381ea2c915c762416092337ce1d8589d1c6 (patch) | |
tree | 63b71c8343f9292281f5d7f5eac14342fec06402 /sdnr/wt/odlux/framework/src/services | |
parent | 8ea94e1210671b941f84abfe16e248cfa086fe49 (diff) |
Update ODLUX
Update login view, add logout after user session ends, add user settings, several bugfixes
Issue-ID: CCSDK-3540
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I21137756b204287e25766a9646bf2faf7bad9d35
Diffstat (limited to 'sdnr/wt/odlux/framework/src/services')
8 files changed, 290 insertions, 13 deletions
diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts index ff9ef0663..36523f9eb 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts @@ -15,8 +15,13 @@ * the License. * ============LICENSE_END========================================================================== */ +import { GeneralSettings } from '../models/settings'; +import { setGeneralSettingsAction, SetGeneralSettingsAction } from '../actions/settingsAction'; import { Event } from '../common/event'; import { ApplicationStore } from '../store/applicationStore'; +import { AuthMessage, getBroadcastChannel, sendMessage } from './broadcastService'; +import { endWebsocketSession } from './notificationService'; +import { getSettings } from './settingsService'; let resolveApplicationStoreInitialized: (store: ApplicationStore) => void; let applicationStore: ApplicationStore | null = null; @@ -24,13 +29,23 @@ const applicationStoreInitialized: Promise<ApplicationStore> = new Promise((reso const loginEvent = new Event(); const logoutEvent = new Event(); +let channel : BroadcastChannel | undefined; +const authChannelName = "odlux_auth"; export const onLogin = () => { + + const message : AuthMessage = {key: 'login', data: {}} + sendMessage(message, authChannelName); loginEvent.invoke(); + } export const onLogout = () => { + document.cookie = "JSESSIONID=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + + const message : AuthMessage = {key: 'logout', data: {}} + sendMessage(message, authChannelName); logoutEvent.invoke(); } diff --git a/sdnr/wt/odlux/framework/src/services/authenticationService.ts b/sdnr/wt/odlux/framework/src/services/authenticationService.ts index 4e7d109d9..a7691bf6f 100644 --- a/sdnr/wt/odlux/framework/src/services/authenticationService.ts +++ b/sdnr/wt/odlux/framework/src/services/authenticationService.ts @@ -24,6 +24,7 @@ type AuthTokenResponse = { access_token: string; token_type: string; expires_at: number; + issued_at: number; } class AuthenticationService { @@ -50,11 +51,14 @@ class AuthenticationService { scope: scope }) }, false); + + return result && { username: email, access_token: result.access_token, token_type: result.token_type, - expires: (result.expires_at * 1000) + expires: result.expires_at, + issued: result.issued_at } || null; } @@ -65,12 +69,14 @@ class AuthenticationService { 'Authorization': "Basic " + btoa(email + ":" + password) }, }, false); + if (result) { return { username: email, access_token: btoa(email + ":" + password), token_type: "Basic", - expires: (new Date()).valueOf() + 2678400000 // 31 days + expires: (new Date()).valueOf() / 1000 + 86400, // 1 day + issued: (new Date()).valueOf() / 1000 } } return null; diff --git a/sdnr/wt/odlux/framework/src/services/broadcastService.ts b/sdnr/wt/odlux/framework/src/services/broadcastService.ts new file mode 100644 index 000000000..85ae3e65c --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/broadcastService.ts @@ -0,0 +1,110 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { setGeneralSettingsAction } from "../actions/settingsAction"; +import { loginUserAction, logoutUser } from "../actions/authentication"; +import { ReplaceAction } from "../actions/navigationActions"; +import { User } from "../models/authentication"; +import { ApplicationStore } from "../store/applicationStore"; + +type Broadcaster = {channel: BroadcastChannel, key: String}; + +type AuthTypes = 'login' | 'logout'; +export type AuthMessage={key: AuthTypes, data: any}; + +type SettingsType = 'general'; +export type SettingsMessage={key: SettingsType, enableNotifications: boolean, user: string}; + +let channels: Broadcaster[] = []; +let store : ApplicationStore | null = null; + +export const subscribe = (channel: BroadcastChannel, channelName: string) => { + channels.push({channel: channel, key: channelName}); +} + +export const startBroadcastChannel = (applicationStore: ApplicationStore)=>{ + store=applicationStore; + + //might decide to use one general broadcast channel with more keys in the future + createAuthBroadcastChannel(); + createSettingsBroadcastChannel(); +} + +const createSettingsBroadcastChannel = () =>{ + + const name = "odlux_settings"; + const bc: BroadcastChannel = new BroadcastChannel(name); + channels.push({ channel: bc, key: name }); + + bc.onmessage = (eventMessage: MessageEvent<SettingsMessage>) => { + console.log(eventMessage) + + if (eventMessage.data.key === 'general') { + + if (store?.state.framework.authenticationState.user) { + const data = eventMessage.data; + if(store.state.framework.authenticationState.user.user === data.user){ + store?.dispatch(setGeneralSettingsAction(data.enableNotifications)); + } + } + } + } + +} + +const createAuthBroadcastChannel = () => { + const name = "odlux_auth"; + const bc: BroadcastChannel = new BroadcastChannel(name); + channels.push({ channel: bc, key: name }); + + bc.onmessage = (eventMessage: MessageEvent<AuthMessage>) => { + console.log(eventMessage) + + if (eventMessage.data.key === 'login') { + if (!store?.state.framework.authenticationState.user) { + const initialToken = localStorage.getItem("userToken"); + if (initialToken) { + store?.dispatch(loginUserAction(User.fromString(initialToken))); + store?.dispatch(new ReplaceAction("/")); + } + } + } + else if (eventMessage.data.key === 'logout') { + + if (store?.state.framework.authenticationState.user) { + store?.dispatch(logoutUser()); + store?.dispatch(new ReplaceAction("/login")); + } + } + } +} + +export const getBroadcastChannel = (channelName: string) =>{ + const foundChannel = channels.find(s =>s.key===channelName); + return foundChannel?.channel; +} + + +export const sendMessage = (data: any, channel: string) =>{ + + const foundChannel = channels.find(s =>s.key===channel); + if(foundChannel){ + foundChannel.channel.postMessage(data); + } + + } diff --git a/sdnr/wt/odlux/framework/src/services/index.ts b/sdnr/wt/odlux/framework/src/services/index.ts index c6071e7b8..19b451345 100644 --- a/sdnr/wt/odlux/framework/src/services/index.ts +++ b/sdnr/wt/odlux/framework/src/services/index.ts @@ -15,7 +15,8 @@ * the License. * ============LICENSE_END========================================================================== */ -export { applicationManager } from './applicationManager';
-export { subscribe, unsubscribe } from './notificationService';
-export { requestRest } from './restService';
-
+export { applicationManager } from './applicationManager'; +export { subscribe, unsubscribe } from './notificationService'; +export { requestRest } from './restService'; +export { putSettings, getSettings} from './settingsService'; + diff --git a/sdnr/wt/odlux/framework/src/services/notificationService.ts b/sdnr/wt/odlux/framework/src/services/notificationService.ts index 99e697e9a..b2880b9de 100644 --- a/sdnr/wt/odlux/framework/src/services/notificationService.ts +++ b/sdnr/wt/odlux/framework/src/services/notificationService.ts @@ -21,10 +21,12 @@ import { SetWebsocketAction } from '../actions/websocketAction'; const socketUrl = [location.protocol === 'https:' ? 'wss://' : 'ws://', location.hostname, ':', location.port, '/websocket'].join(''); const subscriptions: { [scope: string]: SubscriptionCallback[] } = {}; let socketReady: Promise<WebSocket>; -let userLoggedOut = false; let wasWebsocketConnectionEstablished: undefined | boolean; let applicationStore: ApplicationStore | null; +let areWebsocketsStoppedViaSettings = false; + + export interface IFormatedMessage { "event-time": string, "data": { @@ -166,10 +168,11 @@ const connect = (): Promise<WebSocket> => { notificationSocket.onclose = function (event) { console.log("socket connection closed"); - if (applicationStore) { - applicationStore.dispatch(new SetWebsocketAction(false)); - } - if (!userLoggedOut) { + dispatchSocketClose(); + + const isUserLoggedIn = applicationStore?.state.framework.authenticationState.user && applicationStore?.state.framework.authenticationState.user?.isValid; + + if (isUserLoggedIn && !areWebsocketsStoppedViaSettings) { socketReady = connect(); } }; @@ -179,17 +182,37 @@ const connect = (): Promise<WebSocket> => { export const startWebsocketSession = () => { socketReady = connect(); - userLoggedOut = false; + areWebsocketsStoppedViaSettings = false; +} + +export const suspendWebsocketSession = () =>{ + areWebsocketsStoppedViaSettings = true; + closeSocket(); } export const endWebsocketSession = () => { + closeSocket(); +} + +const closeSocket = () =>{ + if (socketReady) { socketReady.then(websocket => { websocket.close(); - userLoggedOut = true; }); + }else{ + dispatchSocketClose(); } +} + +const dispatchSocketClose = () =>{ + const isUserLoggedIn = applicationStore?.state.framework.authenticationState.user && applicationStore?.state.framework.authenticationState.user?.isValid; + if(isUserLoggedIn){ + applicationStore?.dispatch(new SetWebsocketAction(false)); + }else{ + applicationStore?.dispatch(new SetWebsocketAction(null)); + } } diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts index c7b122449..b21e3ec75 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -105,6 +105,7 @@ export async function requestRestExt<TData>(path: string = '', init: RequestInit if (!isAbsUrl && authenticate && applicationStore) { const { state: { framework: { authenticationState: { user } } } } = applicationStore; // do not request if the user is not valid + if (!user || !user.isValid) { return { ...result, diff --git a/sdnr/wt/odlux/framework/src/services/settingsService.ts b/sdnr/wt/odlux/framework/src/services/settingsService.ts new file mode 100644 index 000000000..6633a794d --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/settingsService.ts @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { requestRest } from "./restService"; + + + const settingsPath ="/userdata"; + + + export function getSettings<TData>(partialPath?: string){ + let path = settingsPath; + if(partialPath){ + path+=partialPath + } + + const result = requestRest<TData>(path, {method: "GET"}) + return result; + } + + export function putSettings<TData>(partialPath: string, data: string){ + + const result = requestRest<TData>(settingsPath+partialPath, {method: "PUT", body: data}) + return result; + } + + diff --git a/sdnr/wt/odlux/framework/src/services/userSessionService.ts b/sdnr/wt/odlux/framework/src/services/userSessionService.ts new file mode 100644 index 000000000..0d5936a7e --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/userSessionService.ts @@ -0,0 +1,80 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { ApplicationStore } from "../store/applicationStore"; +import { logoutUser } from "../actions/authentication"; +import { ReplaceAction } from "../actions/navigationActions"; +import { AuthMessage, getBroadcastChannel } from "./broadcastService"; +import { User } from "../models/authentication"; + +let currentUser: User | null; +let applicationStore: ApplicationStore | null = null; +let timer : NodeJS.Timeout | null = null; + +export const startUserSessionService = (store: ApplicationStore) =>{ + applicationStore=store; +} + +export const startUserSession = (user: User) => { + console.log("user session started...") + + const currentTime = new Date(); + //get time differnce between login time and now (eg after user refreshes page) + const timeDiffernce =(currentTime.valueOf()/1000 - user.loginAt); + + currentUser = user; + + if (process.env.NODE_ENV === "development") { + //console.warn("logout timer not started in development mode"); + + const expiresIn = (user.logoutAt - user.loginAt) - timeDiffernce; + console.log("user should be logged out in: "+expiresIn/60 +"minutes") + createForceLogoutInterval(expiresIn); + } else { + const expiresIn = (user.logoutAt - user.loginAt) - timeDiffernce; + console.log("user should be logged out in: "+expiresIn/60 +"minutes") + createForceLogoutInterval(expiresIn); + } +}; + +const createForceLogoutInterval = (intervalInSec: number) => { + console.log("logout timer running..."); + + if(timer!==null){ + console.error("an old session was available"); + clearTimeout(timer); + } + + timer = setTimeout(function () { + if (currentUser && applicationStore) { + + applicationStore.dispatch(logoutUser()); + applicationStore.dispatch(new ReplaceAction("/login")); + + } + + }, intervalInSec * 1000) +} + +export const endUserSession = ()=>{ + + if(timer!==null){ + clearTimeout(timer); + timer=null; + } +}
\ No newline at end of file |