aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework/src
diff options
context:
space:
mode:
authorAijana Schumann <aijana.schumann@highstreet-technologies.com>2021-02-15 18:22:28 +0100
committerAijana Schumann <aijana.schumann@highstreet-technologies.com>2021-02-15 18:23:57 +0100
commit8515052e1a6de2de56effbc61c73d3aa80169a93 (patch)
tree8707b2b587890522b35cd7dd1b54ce4f006f1c3a /sdnr/wt/odlux/framework/src
parentdb20d36689c011333ed7216b64d3e987e473f1ee (diff)
Add OAuth support to odlux
Extend odlux to support oauth, support external login provider for sign-in Issue-ID: CCSDK-3167 Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com> Change-Id: Id5772e0026fa7ebda22c41c2620a7868598f41aa
Diffstat (limited to 'sdnr/wt/odlux/framework/src')
-rw-r--r--sdnr/wt/odlux/framework/src/actions/authentication.ts13
-rw-r--r--sdnr/wt/odlux/framework/src/actions/loginProvider.ts36
-rw-r--r--sdnr/wt/odlux/framework/src/app.tsx30
-rw-r--r--sdnr/wt/odlux/framework/src/assets/version.json4
-rw-r--r--sdnr/wt/odlux/framework/src/components/titleBar.tsx4
-rw-r--r--sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts52
-rw-r--r--sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts28
-rw-r--r--sdnr/wt/odlux/framework/src/index.html1
-rw-r--r--sdnr/wt/odlux/framework/src/middleware/navigation.ts19
-rw-r--r--sdnr/wt/odlux/framework/src/middleware/policies.ts41
-rw-r--r--sdnr/wt/odlux/framework/src/models/applicationConfig.ts4
-rw-r--r--sdnr/wt/odlux/framework/src/models/authentication.ts10
-rw-r--r--sdnr/wt/odlux/framework/src/models/externalLoginProvider.ts23
-rw-r--r--sdnr/wt/odlux/framework/src/run.ts1
-rw-r--r--sdnr/wt/odlux/framework/src/services/authenticationService.ts32
-rw-r--r--sdnr/wt/odlux/framework/src/services/forceLogoutService.ts4
-rw-r--r--sdnr/wt/odlux/framework/src/store/applicationStore.ts3
-rw-r--r--sdnr/wt/odlux/framework/src/views/frame.tsx40
-rw-r--r--sdnr/wt/odlux/framework/src/views/login.tsx92
19 files changed, 347 insertions, 90 deletions
diff --git a/sdnr/wt/odlux/framework/src/actions/authentication.ts b/sdnr/wt/odlux/framework/src/actions/authentication.ts
index b28100035..de8093573 100644
--- a/sdnr/wt/odlux/framework/src/actions/authentication.ts
+++ b/sdnr/wt/odlux/framework/src/actions/authentication.ts
@@ -16,11 +16,18 @@
* ============LICENSE_END==========================================================================
*/
import { Action } from '../flux/action';
-import { AuthToken } from '../models/authentication';
+import { AuthPolicy, User } from '../models/authentication';
-export class UpdateAuthentication extends Action {
+export class UpdateUser extends Action {
- constructor (public bearerToken: AuthToken | null) {
+ constructor (public user?: User) {
+ super();
+ }
+}
+
+export class UpdatePolicies extends Action {
+
+ constructor (public authPolicies?: AuthPolicy[]) {
super();
}
} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/actions/loginProvider.ts b/sdnr/wt/odlux/framework/src/actions/loginProvider.ts
new file mode 100644
index 000000000..e6467111e
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/actions/loginProvider.ts
@@ -0,0 +1,36 @@
+/**
+ * ============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 { Action } from '../flux/action';
+import { Dispatch } from '../flux/store';
+
+import { IApplicationStoreState } from '../store/applicationStore';
+import { ExternalLoginProvider } from '../models/externalLoginProvider';
+
+import authenticationService from '../services/authenticationService';
+
+export class SetExternalLoginProviderAction extends Action {
+ constructor(public externalLoginProvders: ExternalLoginProvider[] | null) {
+ super();
+ }
+}
+
+export const updateExternalLoginProviderAsyncActionCreator = () => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => {
+ const providers = await authenticationService.getAvaliableExteralProvider();
+ dispatch(new SetExternalLoginProviderAction(providers || null));
+} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx
index 6a24bfb2a..23ae2fbc9 100644
--- a/sdnr/wt/odlux/framework/src/app.tsx
+++ b/sdnr/wt/odlux/framework/src/app.tsx
@@ -23,7 +23,10 @@ import { MuiThemeProvider } from '@material-ui/core/styles';
import { Frame } from './views/frame';
+import { User } from './models/authentication';
+
import { AddErrorInfoAction } from './actions/errorActions';
+import { UpdateUser } from './actions/authentication';
import { applicationStoreCreator } from './store/applicationStore';
import { ApplicationStoreProvider } from './flux/connect';
@@ -31,12 +34,11 @@ import { ApplicationStoreProvider } from './flux/connect';
import { startHistoryListener } from './middleware/navigation';
import { startRestService } from './services/restService';
+import { startForceLogoutService } from './services/forceLogoutService';
+import { startNotificationService } from './services/notificationService';
import theme from './design/default';
import '!style-loader!css-loader!./app.css';
-import { ReplaceAction } from './actions/navigationActions';
-import { startForceLogoutService } from './services/forceLogoutService';
-import { startNotificationService } from './services/notificationService';
declare module '@material-ui/core/styles/createMuiTheme' {
@@ -57,9 +59,13 @@ declare module '@material-ui/core/styles/createMuiTheme' {
}
}
+export { configureApplication } from "./handlers/applicationStateHandler";
+
export const transportPCEUrl = "transportPCEUrl";
export const runApplication = () => {
+
+ const initialToken = localStorage.getItem("userToken");
const applicationStore = applicationStoreCreator();
window.onerror = function (msg: string, url: string, line: number, col: number, error: Error) {
@@ -81,7 +87,6 @@ export const runApplication = () => {
startHistoryListener(applicationStore);
startForceLogoutService(applicationStore);
startNotificationService(applicationStore);
- addTransportPCEUrl();
const App = (): JSX.Element => (
<ApplicationStoreProvider applicationStore={applicationStore} >
@@ -93,12 +98,19 @@ export const runApplication = () => {
ReactDOM.render(<App />, document.getElementById('app'));
+ if (initialToken) {
+ applicationStore.dispatch(new UpdateUser(User.fromString(initialToken) || undefined));
+ }
+
};
-const addTransportPCEUrl = () =>{
- const url = window.localStorage.getItem(transportPCEUrl);
- if(url === null){
- window.localStorage.setItem(transportPCEUrl, "http://10.20.6.32:18082/");
- console.log("set transport url :D")
+if (process.env.NODE_ENV === "development") {
+ const addTransportPCEUrl = () =>{
+ const url = window.localStorage.getItem(transportPCEUrl);
+ if(url === null){
+ window.localStorage.setItem(transportPCEUrl, "http://10.20.6.32:18082/");
+ console.log("set transport url :D")
+ }
}
+ addTransportPCEUrl();
}
diff --git a/sdnr/wt/odlux/framework/src/assets/version.json b/sdnr/wt/odlux/framework/src/assets/version.json
index 260aec4ea..6d4eb0cc6 100644
--- a/sdnr/wt/odlux/framework/src/assets/version.json
+++ b/sdnr/wt/odlux/framework/src/assets/version.json
@@ -1,4 +1,4 @@
{
- "version":"88.1c38886(20/12/04)",
- "build":"2020-12-04T06:06:24Z"
+ "version":"56.139cd6d(20/07/08)",
+ "build":"2020-07-16T06:06:24Z"
} \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
index 49c096691..62db1de40 100644
--- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx
+++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
@@ -35,7 +35,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBan } from '@fortawesome/free-solid-svg-icons';
import { faDotCircle } from '@fortawesome/free-solid-svg-icons';
-import { UpdateAuthentication } from '../actions/authentication';
+import { UpdateUser } from '../actions/authentication';
import { ReplaceAction } from '../actions/navigationActions';
import connect, { Connect, IDispatcher } from '../flux/connect';
@@ -71,7 +71,7 @@ const styles = (theme: Theme) => createStyles({
const mapDispatch = (dispatcher: IDispatcher) => {
return {
logout: () => {
- dispatcher.dispatch(new UpdateAuthentication(null));
+ dispatcher.dispatch(new UpdateUser(undefined));
dispatcher.dispatch(new ReplaceAction("/login"));
},
toggleMainMenu: (value: boolean, value2: boolean) => {
diff --git a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
index a93f96a82..b5c1ee7b1 100644
--- a/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
+++ b/sdnr/wt/odlux/framework/src/handlers/applicationStateHandler.ts
@@ -16,16 +16,19 @@
* ============LICENSE_END==========================================================================
*/
import { IActionHandler } from '../flux/action';
-import { SetTitleAction } from '../actions/titleActions';
+import { SetTitleAction } from '../actions/titleActions';
+import { SetExternalLoginProviderAction } from '../actions/loginProvider';
import { AddSnackbarNotification, RemoveSnackbarNotification } from '../actions/snackbarActions';
import { AddErrorInfoAction, RemoveErrorInfoAction, ClearErrorInfoAction } from '../actions/errorActions';
import { MenuAction, MenuClosedByUser } from '../actions/menuAction'
-import { IconType } from '../models/iconDefinition';
+import { SetWebsocketAction } from '../actions/websocketAction';
+import { IconType } from '../models/iconDefinition';
import { ErrorInfo } from '../models/errorInfo';
import { SnackbarItem } from '../models/snackbarItem';
-import { SetWebsocketAction } from '../actions/websocketAction';
+import { ExternalLoginProvider } from '../models/externalLoginProvider';
+import { ApplicationConfig } from '../models/applicationConfig';
export interface IApplicationState {
title: string;
@@ -36,9 +39,27 @@ export interface IApplicationState {
errors: ErrorInfo[];
snackBars: SnackbarItem[];
isWebsocketAvailable: boolean | undefined;
+ externalLoginProviders: ExternalLoginProvider[] | null;
+ authentication: "basic"|"oauth", // basic
+ enablePolicy: boolean // false
}
-const applicationStateInit: IApplicationState = { title: "Loading ...", errors: [], snackBars: [], isMenuOpen: true, isMenuClosedByUser: false, isWebsocketAvailable: undefined };
+const applicationStateInit: IApplicationState = {
+ title: "Loading ...",
+ errors: [],
+ snackBars: [],
+ isMenuOpen: true,
+ isMenuClosedByUser: false,
+ isWebsocketAvailable: undefined,
+ externalLoginProviders: null,
+ authentication: "basic",
+ enablePolicy: false,
+};
+
+export const configureApplication = (config: ApplicationConfig) => {
+ applicationStateInit.authentication = config.authentication === "oauth" ? "oauth" : "basic";
+ applicationStateInit.enablePolicy = config.authentication ? true : false;
+}
export const applicationStateHandler: IActionHandler<IApplicationState> = (state = applicationStateInit, action) => {
if (action instanceof SetTitleAction) {
@@ -46,14 +67,14 @@ export const applicationStateHandler: IActionHandler<IApplicationState> = (state
...state,
title: action.title,
icon: action.icon,
- appId: action.appId
+ appId: action.appId,
};
} else if (action instanceof AddErrorInfoAction) {
state = {
...state,
errors: [
...state.errors,
- action.errorInfo
+ action.errorInfo,
]
};
} else if (action instanceof RemoveErrorInfoAction) {
@@ -63,7 +84,7 @@ export const applicationStateHandler: IActionHandler<IApplicationState> = (state
...state,
errors: [
...state.errors.slice(0, index),
- ...state.errors.slice(index + 1)
+ ...state.errors.slice(index + 1),
]
};
}
@@ -71,7 +92,7 @@ export const applicationStateHandler: IActionHandler<IApplicationState> = (state
if (state.errors && state.errors.length) {
state = {
...state,
- errors: []
+ errors: [],
};
}
} else if (action instanceof AddSnackbarNotification) {
@@ -79,29 +100,34 @@ export const applicationStateHandler: IActionHandler<IApplicationState> = (state
...state,
snackBars: [
...state.snackBars,
- action.notification
+ action.notification,
]
};
} else if (action instanceof RemoveSnackbarNotification) {
state = {
...state,
- snackBars: state.snackBars.filter(s => s.key !== action.key)
+ snackBars: state.snackBars.filter(s => s.key !== action.key),
};
} else if (action instanceof MenuAction) {
state = {
...state,
- isMenuOpen: action.isOpen
+ isMenuOpen: action.isOpen,
}
} else if (action instanceof MenuClosedByUser) {
state = {
...state,
- isMenuClosedByUser: action.isClosed
+ isMenuClosedByUser: action.isClosed,
}
}
else if (action instanceof SetWebsocketAction) {
state = {
...state,
- isWebsocketAvailable: action.isConnected
+ isWebsocketAvailable: action.isConnected,
+ }
+ } else if (action instanceof SetExternalLoginProviderAction){
+ state = {
+ ...state,
+ externalLoginProviders: action.externalLoginProvders,
}
}
return state;
diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
index 82b228dc0..5217bd414 100644
--- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
+++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts
@@ -16,31 +16,26 @@
* ============LICENSE_END==========================================================================
*/
import { IActionHandler } from '../flux/action';
-import { UpdateAuthentication } from '../actions/authentication';
+import { UpdatePolicies, UpdateUser } from '../actions/authentication';
-import { User } from '../models/authentication';
+import { AuthPolicy, User } from '../models/authentication';
import { onLogin, onLogout } from '../services/applicationApi';
import { startWebsocketSession, endWebsocketSession } from '../services/notificationService';
export interface IAuthenticationState {
user?: User;
-}
-
-const initialToken = localStorage.getItem("userToken");
-
-if (initialToken !== null) {
- startWebsocketSession();
+ policies?: AuthPolicy[];
}
const authenticationStateInit: IAuthenticationState = {
- user: initialToken && User.fromString(initialToken) || undefined
+ user: undefined
};
export const authenticationStateHandler: IActionHandler<IAuthenticationState> = (state = authenticationStateInit, action) => {
- if (action instanceof UpdateAuthentication) {
-
- const user = action.bearerToken && new User(action.bearerToken) || undefined;
+ if (action instanceof UpdateUser) {
+ const {user} = action;
+
if (user) {
localStorage.setItem("userToken", user.toString());
startWebsocketSession();
@@ -53,9 +48,14 @@ export const authenticationStateHandler: IActionHandler<IAuthenticationState> =
state = {
...state,
- user
+ user,
};
+ } else if (action instanceof UpdatePolicies) {
+ state = {
+ ...state,
+ policies: action.authPolicies,
+ };
}
-
return state;
};
+
diff --git a/sdnr/wt/odlux/framework/src/index.html b/sdnr/wt/odlux/framework/src/index.html
index d51c448a9..5cd2805c1 100644
--- a/sdnr/wt/odlux/framework/src/index.html
+++ b/sdnr/wt/odlux/framework/src/index.html
@@ -16,6 +16,7 @@
<script>
// run the application
require(["run"], function (run) {
+ run.configureApplication({ authentication:"oauth", enablePolicy: true,});
run.runApplication();
});
diff --git a/sdnr/wt/odlux/framework/src/middleware/navigation.ts b/sdnr/wt/odlux/framework/src/middleware/navigation.ts
index d5cdcd44b..c5ab788f3 100644
--- a/sdnr/wt/odlux/framework/src/middleware/navigation.ts
+++ b/sdnr/wt/odlux/framework/src/middleware/navigation.ts
@@ -15,15 +15,19 @@
* the License.
* ============LICENSE_END==========================================================================
*/
+import * as jwt from 'jsonwebtoken';
import { Location, History, createHashHistory } from "history";
-import { ApplicationStore } from "../store/applicationStore";
-import { Dispatch } from '../flux/store';
+import { User } from "../models/authentication";
import { LocationChanged, NavigateToApplication } from "../actions/navigationActions";
import { PushAction, ReplaceAction, GoAction, GoBackAction, GoForwardeAction } from '../actions/navigationActions';
import { applicationManager } from "../services/applicationManager";
+import { UpdateUser } from "../actions/authentication";
+
+import { ApplicationStore } from "../store/applicationStore";
+import { Dispatch } from '../flux/store';
const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Dispatch => (action) => {
@@ -49,7 +53,16 @@ const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Di
history.goForward();
} else if (action instanceof LocationChanged) {
// ensure user is logged in and token is valid
- if (!action.pathname.startsWith("/login") && applicationStore && (!applicationStore.state.framework.authenticationState.user || !applicationStore.state.framework.authenticationState.user.isValid)) {
+ if (action.pathname.startsWith("/oauth") && (action.search.startsWith("?token="))){
+ const ind = action.search.lastIndexOf("token=");
+ const tokenStr = ind > -1 ? action.search.substr(ind+6) : null;
+ const token = tokenStr && jwt.decode(tokenStr);
+ if (tokenStr && token) {
+ // @ts-ignore
+ const user = new User({ username: token["name"], access_token: tokenStr, token_type: "Bearer", expires: (new Date().valueOf()) + ( (+token['exp']) * 1000) }) || undefined;
+ return next(new UpdateUser(user)) as any;
+ }
+ } if (!action.pathname.startsWith("/login") && applicationStore && (!applicationStore.state.framework.authenticationState.user || !applicationStore.state.framework.authenticationState.user.isValid)) {
history.replace(`/login?returnTo=${action.pathname}`);
} else {
return next(action);
diff --git a/sdnr/wt/odlux/framework/src/middleware/policies.ts b/sdnr/wt/odlux/framework/src/middleware/policies.ts
new file mode 100644
index 000000000..662ecddb3
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/middleware/policies.ts
@@ -0,0 +1,41 @@
+/**
+ * ============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 authenticationService from '../services/authenticationService';
+
+import { UpdateUser, UpdatePolicies } from '../actions/authentication';
+import { Dispatch } from '../flux/store';
+import { MiddlewareApi } from '../store/applicationStore';
+
+function updatePoliciesMiddleware() {
+ return ({ dispatch, getState }: MiddlewareApi) =>
+ (next : Dispatch) : Dispatch =>
+ action => {
+ const { framework: { applicationState: { enablePolicy } } } = getState() || { framework: { applicationState: { } } };
+ if (enablePolicy && action instanceof UpdateUser) {
+ next(action);
+ authenticationService.getAccessPolicies().then((policies) => dispatch(new UpdatePolicies(policies||undefined)));
+ return action;
+ }
+ if (enablePolicy === false) next(new UpdatePolicies());
+ return next(action);
+ };
+}
+
+export const updatePolicies = updatePoliciesMiddleware();
+export default updatePolicies; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/models/applicationConfig.ts b/sdnr/wt/odlux/framework/src/models/applicationConfig.ts
new file mode 100644
index 000000000..5224840eb
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/models/applicationConfig.ts
@@ -0,0 +1,4 @@
+export type ApplicationConfig = {
+ authentication: "basic"|"oauth", // basic
+ enablePolicy: false // false
+}; \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/models/authentication.ts b/sdnr/wt/odlux/framework/src/models/authentication.ts
index a50c5ded4..b6840a0ce 100644
--- a/sdnr/wt/odlux/framework/src/models/authentication.ts
+++ b/sdnr/wt/odlux/framework/src/models/authentication.ts
@@ -23,6 +23,16 @@ export type AuthToken = {
expires: number;
}
+export type AuthPolicy = {
+ path: string;
+ methods: {
+ get?: boolean;
+ post?: boolean;
+ put?: boolean;
+ patch?: boolean;
+ delete?: boolean;
+ }
+}
export class User {
diff --git a/sdnr/wt/odlux/framework/src/models/externalLoginProvider.ts b/sdnr/wt/odlux/framework/src/models/externalLoginProvider.ts
new file mode 100644
index 000000000..357feed06
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/models/externalLoginProvider.ts
@@ -0,0 +1,23 @@
+/**
+ * ============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==========================================================================
+ */
+
+export type ExternalLoginProvider = {
+ id: string;
+ title: string;
+ loginUrl: string;
+ } \ No newline at end of file
diff --git a/sdnr/wt/odlux/framework/src/run.ts b/sdnr/wt/odlux/framework/src/run.ts
index 10692e8d6..991b7494e 100644
--- a/sdnr/wt/odlux/framework/src/run.ts
+++ b/sdnr/wt/odlux/framework/src/run.ts
@@ -15,4 +15,5 @@
* the License.
* ============LICENSE_END==========================================================================
*/
+export { configureApplication } from './handlers/applicationStateHandler';
export { runApplication } from './app';
diff --git a/sdnr/wt/odlux/framework/src/services/authenticationService.ts b/sdnr/wt/odlux/framework/src/services/authenticationService.ts
index 9b006fc16..4e7d109d9 100644
--- a/sdnr/wt/odlux/framework/src/services/authenticationService.ts
+++ b/sdnr/wt/odlux/framework/src/services/authenticationService.ts
@@ -15,19 +15,30 @@
* the License.
* ============LICENSE_END==========================================================================
*/
+import { AuthPolicy, AuthToken } from "../models/authentication";
+import { ExternalLoginProvider } from "../models/externalLoginProvider";
+
import { requestRest, formEncode } from "./restService";
-import { AuthToken } from "../models/authentication";
type AuthTokenResponse = {
access_token: string;
token_type: string;
- expires_in: number;
+ expires_at: number;
}
-
class AuthenticationService {
+ public async getAvaliableExteralProvider() {
+ const result = await requestRest<ExternalLoginProvider[]>(`oauth/providers`, {
+ method: "GET",
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ }, false);
+ return result;
+ }
+
public async authenticateUserOAuth(email: string, password: string, scope: string): Promise<AuthToken | null> {
- const result = await requestRest<string>(`oauth2/token`, {
+ const result = await requestRest<AuthTokenResponse>(`oauth/login`, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
@@ -39,12 +50,11 @@ class AuthenticationService {
scope: scope
})
}, false);
- const resultObj: AuthTokenResponse| null = result && JSON.parse(result);
- return resultObj && {
+ return result && {
username: email,
- access_token: resultObj.access_token,
- token_type: resultObj.token_type,
- expires: (new Date().valueOf()) + (resultObj.expires_in * 1000)
+ access_token: result.access_token,
+ token_type: result.token_type,
+ expires: (result.expires_at * 1000)
} || null;
}
@@ -65,6 +75,10 @@ class AuthenticationService {
}
return null;
}
+
+ public async getAccessPolicies(){
+ return await requestRest<AuthPolicy[]>(`oauth/policies`, { method: "GET" }, true);
+ }
}
export const authenticationService = new AuthenticationService();
diff --git a/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts b/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts
index 0c7c38dff..a57739025 100644
--- a/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts
+++ b/sdnr/wt/odlux/framework/src/services/forceLogoutService.ts
@@ -17,7 +17,7 @@
*/
import { ApplicationStore } from "../store/applicationStore";
-import { UpdateAuthentication } from "../actions/authentication";
+import { UpdateUser } from "../actions/authentication";
import { ReplaceAction } from "../actions/navigationActions";
const maxMinutesTillLogout = 15;
@@ -45,7 +45,7 @@ const createForceLogoutInterval = () => {
if (tickTimer === 0) {
console.log("got logged out by timer")
if (applicationStore) {
- applicationStore.dispatch(new UpdateAuthentication(null));
+ applicationStore.dispatch(new UpdateUser(undefined));
applicationStore.dispatch(new ReplaceAction("/login"));
}
}
diff --git a/sdnr/wt/odlux/framework/src/store/applicationStore.ts b/sdnr/wt/odlux/framework/src/store/applicationStore.ts
index 287eba31c..a4545eff9 100644
--- a/sdnr/wt/odlux/framework/src/store/applicationStore.ts
+++ b/sdnr/wt/odlux/framework/src/store/applicationStore.ts
@@ -32,6 +32,7 @@ import apiMiddleware from '../middleware/api';
import thunkMiddleware from '../middleware/thunk';
import loggerMiddleware from '../middleware/logger';
import routerMiddleware from '../middleware/navigation';
+import { updatePolicies } from '../middleware/policies';
export type MiddlewareApi = MiddlewareArg<IApplicationStoreState>;
@@ -65,7 +66,7 @@ export const applicationStoreCreator = (): ApplicationStore => {
return acc;
}, { framework: frameworkHandlers } as any);
- const applicationStore = new ApplicationStore(combineActionHandler(actionHandlers), chainMiddleware(loggerMiddleware, thunkMiddleware, routerMiddleware, apiMiddleware, ...middlewares));
+ const applicationStore = new ApplicationStore(combineActionHandler(actionHandlers), chainMiddleware(loggerMiddleware, thunkMiddleware, routerMiddleware, apiMiddleware, updatePolicies, ...middlewares));
setApplicationStore(applicationStore);
return applicationStore;
}
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx
index c8e24fdb9..b4cc43e0b 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -80,32 +80,32 @@ class FrameComponent extends React.Component<FrameProps>{
}
<Switch>
<Route exact path="/" component={() => (
- <AppFrame title={"Home"} icon={faHome} >
- <Home />
- </AppFrame>
+ <AppFrame title={"Home"} icon={faHome} >
+ <Home />
+ </AppFrame>
)} />
<Route path="/about" component={() => (
- <AppFrame title={"About"} icon={faAddressBook} >
- <About />
- </AppFrame>
+ <AppFrame title={"About"} icon={faAddressBook} >
+ <About />
+ </AppFrame>
)} />
{process.env.NODE_ENV === "development" ? <Route path="/test" component={() => (
- <AppFrame title={"Test"} icon={faAddressBook} >
- <Test />
- </AppFrame>
+ <AppFrame title={"Test"} icon={faAddressBook} >
+ <Test />
+ </AppFrame>
)} /> : null}
<Route path="/login" component={() => (
- <AppFrame title={"Login"} icon={faSignInAlt} >
- <Login />
- </AppFrame>
- )} />
- {Object.keys(registrations).map(p => {
- const application = registrations[p];
- return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
- <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
- <application.rootComponent />
- </AppFrame>
- )} />)
+ <AppFrame title={"Login"} icon={faSignInAlt} >
+ <Login />
+ </AppFrame>
+ )} />
+ { Object.keys(registrations).map(p => {
+ const application = registrations[p];
+ return (<Route key={application.name} path={application.path || `/${application.name}`} component={() => (
+ <AppFrame title={application.title || (typeof application.menuEntry === 'string' && application.menuEntry) || application.name} icon={application.icon} appId={application.name} >
+ <application.rootComponent />
+ </AppFrame>
+ )} />)
})}
<Redirect to="/" />
</Switch>
diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx
index b06cf7631..be1fb801f 100644
--- a/sdnr/wt/odlux/framework/src/views/login.tsx
+++ b/sdnr/wt/odlux/framework/src/views/login.tsx
@@ -32,10 +32,16 @@ import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
-import connect, { Connect } from '../flux/connect';
+import connect, { Connect, IDispatcher } from '../flux/connect';
import authenticationService from '../services/authenticationService';
-import { UpdateAuthentication } from '../actions/authentication';
+import { updateExternalLoginProviderAsyncActionCreator } from '../actions/loginProvider';
+import { UpdatePolicies, UpdateUser } from '../actions/authentication';
+
+import { IApplicationStoreState } from '../store/applicationStore';
+import { AuthPolicy, AuthToken, User } from '../models/authentication';
+import Menu from '@material-ui/core/Menu';
+import { MenuItem } from '@material-ui/core';
const styles = (theme: Theme) => createStyles({
layout: {
@@ -69,14 +75,37 @@ const styles = (theme: Theme) => createStyles({
},
});
-type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect;
+const mapProps = (state: IApplicationStoreState) => ({
+ search: state.framework.navigationState.search,
+ authentication: state.framework.applicationState.authentication,
+ externalLoginProviders: state.framework.applicationState.externalLoginProviders ,
+});
+
+const mapDispatch = (dispatcher: IDispatcher) => ({
+ updateExternalProviders: () => dispatcher.dispatch(updateExternalLoginProviderAsyncActionCreator()),
+ updateAuthentication: (token: AuthToken | null) => {
+ const user = token && new User(token) || undefined;
+ dispatcher.dispatch(new UpdateUser(user));
+ },
+ updatePolicies: (policies?: AuthPolicy[]) => {
+ return dispatcher.dispatch(new UpdatePolicies(policies));
+ },
+});
+
+type LoginProps = RouteComponentProps<{}> & WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>;
interface ILoginState {
+ externalProviderAnchor: HTMLElement | null;
busy: boolean;
username: string;
password: string;
scope: string;
message: string;
+ providers: {
+ id: string;
+ title: string;
+ loginUrl: string;
+ }[] | null;
}
@@ -87,14 +116,26 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> {
super(props);
this.state = {
+ externalProviderAnchor: null,
busy: false,
username: '',
password: '',
scope: 'sdn',
- message: ''
+ message: '',
+ providers: null,
};
}
+ async componentDidMount(){
+ if (this.props.authentication === "oauth" && (this.props.externalLoginProviders == null || this.props.externalLoginProviders.length === 0)){
+ this.props.updateExternalProviders();
+ }
+ }
+
+ private setExternalProviderAnchor = (el: HTMLElement | null) => {
+ this.setState({externalProviderAnchor: el })
+ }
+
render(): JSX.Element {
const { classes } = this.props;
return (
@@ -153,6 +194,33 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> {
>
Sign in
</Button>
+ { this.props.externalLoginProviders && this.props.externalLoginProviders.length > 0
+ ?
+ [
+ <Button
+ aria-controls="externalLogin"
+ aria-haspopup="true"
+ fullWidth
+ variant="contained"
+ color="primary"
+ className={classes.submit} onClick={(ev) => { this.setExternalProviderAnchor(ev.currentTarget); }}>
+ Use external Login
+ </Button>,
+ <Menu
+ anchorEl={this.state.externalProviderAnchor}
+ keepMounted
+ open={Boolean(this.state.externalProviderAnchor)}
+ onClose={() => { this.setExternalProviderAnchor(null); }}
+ >
+ {
+ this.props.externalLoginProviders.map((provider) => (
+ <MenuItem key={provider.id} onClick={() => { window.location = provider.loginUrl as any; } }>{ provider.title} </MenuItem>
+ ))
+ }
+ </Menu>
+ ]
+ : null
+ }
</form>
{this.state.message && <Alert severity="error">{this.state.message}</Alert>}
</Paper>
@@ -165,16 +233,16 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> {
event.preventDefault();
this.setState({ busy: true });
- // const token = await authenticationService.authenticateUserOAuth(this.state.username, this.state.password, this.state.scope);
- const token = await authenticationService.authenticateUserBasicAuth(this.state.username, this.state.password, this.state.scope);
- this.props.dispatch(new UpdateAuthentication(token));
+
+ const token = this.props.authentication === "oauth"
+ ? await authenticationService.authenticateUserOAuth(this.state.username, this.state.password, this.state.scope)
+ : await authenticationService.authenticateUserBasicAuth(this.state.username, this.state.password, this.state.scope);
+
+ this.props.updateAuthentication(token);
this.setState({ busy: false });
if (token) {
- const query =
- this.props.state.framework.navigationState.search &&
- this.props.state.framework.navigationState.search.replace(/^\?/, "")
- .split('&').map(e => e.split("="));
+ const query = this.props.search && this.props.search.replace(/^\?/, "").split('&').map(e => e.split("="));
const returnTo = query && query.find(e => e[0] === "returnTo");
this.props.history.replace(returnTo && returnTo[1] || "/");
}
@@ -187,5 +255,5 @@ class LoginComponent extends React.Component<LoginProps, ILoginState> {
}
}
-export const Login = withStyles(styles)(withRouter(connect()(LoginComponent)));
+export const Login = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(LoginComponent)));
export default Login; \ No newline at end of file