+
+
{this.renderTitle()}
-
+
{routes.map((r, key) => {
return
@@ -41,10 +46,12 @@ export default class PortalLayout extends Component {
-
)
}
-}
\ No newline at end of file
+}
+
+export default withStyles(styles)(PortalLayout);
\ No newline at end of file
diff --git a/client-react/src/reducers/Portal/RemoteState.ts b/client-react/src/reducers/Portal/RemoteState.ts
new file mode 100644
index 00000000..aa10b406
--- /dev/null
+++ b/client-react/src/reducers/Portal/RemoteState.ts
@@ -0,0 +1,8 @@
+import AuthenticationLevel from '../../types/AuthenticationLevel';
+
+interface RemoteState {
+ username: string;
+ authentication_level: AuthenticationLevel;
+}
+
+export default RemoteState;
\ No newline at end of file
diff --git a/client-react/src/reducers/Portal/actions.ts b/client-react/src/reducers/Portal/actions.ts
new file mode 100644
index 00000000..5548dcd5
--- /dev/null
+++ b/client-react/src/reducers/Portal/actions.ts
@@ -0,0 +1,41 @@
+import { createAction } from 'typesafe-actions';
+import {
+ AUTHENTICATE_REQUEST,
+ AUTHENTICATE_SUCCESS,
+ AUTHENTICATE_FAILURE,
+ FETCH_STATE_REQUEST,
+ FETCH_STATE_SUCCESS,
+ FETCH_STATE_FAILURE,
+ LOGOUT_REQUEST,
+ LOGOUT_SUCCESS,
+ LOGOUT_FAILURE
+} from "../constants";
+import RemoteState from './RemoteState';
+
+/* FETCH_STATE */
+export const fetchState = createAction(FETCH_STATE_REQUEST);
+export const fetchStateSuccess = createAction(FETCH_STATE_SUCCESS, resolve => {
+ return (state: RemoteState) => {
+ return resolve(state);
+ }
+});
+export const fetchStateFailure = createAction(FETCH_STATE_FAILURE, resolve => {
+ return (err: string) => {
+ return resolve(err);
+ }
+})
+
+/* AUTHENTICATE_REQUEST */
+export const authenticate = createAction(AUTHENTICATE_REQUEST);
+export const authenticateSuccess = createAction(AUTHENTICATE_SUCCESS);
+export const authenticateFailure = createAction(AUTHENTICATE_FAILURE, resolve => {
+ return (error: string) => resolve(error);
+});
+
+
+/* AUTHENTICATE_REQUEST */
+export const logout = createAction(LOGOUT_REQUEST);
+export const logoutSuccess = createAction(LOGOUT_SUCCESS);
+export const logoutFailure = createAction(LOGOUT_FAILURE, resolve => {
+ return (error: string) => resolve(error);
+});
diff --git a/client-react/src/reducers/Portal/reducer.ts b/client-react/src/reducers/Portal/reducer.ts
new file mode 100644
index 00000000..203fbcbf
--- /dev/null
+++ b/client-react/src/reducers/Portal/reducer.ts
@@ -0,0 +1,106 @@
+
+import * as Actions from './actions';
+import { ActionType, getType, StateType } from 'typesafe-actions';
+import RemoteState from './RemoteState';
+
+export type FirstFactorAction = ActionType
;
+
+enum Result {
+ NONE,
+ SUCCESS,
+ FAILURE,
+}
+
+interface State {
+ lastResult: Result;
+ loading: boolean;
+ error: string | null;
+
+ remoteState: RemoteState | null;
+ remoteStateLoading: boolean;
+ remoteStateError: string | null;
+
+ logoutLoading: boolean;
+ logoutSuccess: boolean | null;
+ logoutError: string | null;
+}
+
+const initialState: State = {
+ lastResult: Result.NONE,
+ loading: false,
+ error: null,
+ remoteState: null,
+ remoteStateLoading: false,
+ remoteStateError: null,
+ logoutLoading: false,
+ logoutError: null,
+ logoutSuccess: null,
+}
+
+export type PortalState = StateType;
+
+export default (state = initialState, action: FirstFactorAction) => {
+ switch(action.type) {
+ case getType(Actions.authenticate):
+ return {
+ ...state,
+ loading: true,
+ error: null
+ };
+ case getType(Actions.authenticateSuccess):
+ return {
+ ...state,
+ lastResult: Result.SUCCESS,
+ loading: false,
+ error: null,
+ };
+ case getType(Actions.authenticateFailure):
+ return {
+ ...state,
+ lastResult: Result.FAILURE,
+ loading: false,
+ error: action.payload,
+ };
+
+ case getType(Actions.fetchState):
+ return {
+ ...state,
+ remoteState: null,
+ remoteStateError: null,
+ remoteStateLoading: true,
+ };
+ case getType(Actions.fetchStateSuccess):
+ return {
+ ...state,
+ remoteState: action.payload,
+ remoteStateLoading: false,
+ };
+ case getType(Actions.fetchStateFailure):
+ return {
+ ...state,
+ remoteStateError: action.payload,
+ remoteStateLoading: false,
+ };
+
+ case getType(Actions.logout):
+ return {
+ ...state,
+ logoutLoading: true,
+ logoutSuccess: null,
+ logoutError: null,
+ };
+ case getType(Actions.logoutSuccess):
+ return {
+ ...state,
+ logoutLoading: false,
+ logoutSuccess: true,
+ };
+ case getType(Actions.logoutFailure):
+ return {
+ ...state,
+ logoutLoading: false,
+ logoutError: action.payload,
+ }
+ }
+ return state;
+}
\ No newline at end of file
diff --git a/client-react/src/reducers/constants.ts b/client-react/src/reducers/constants.ts
new file mode 100644
index 00000000..a4af97ad
--- /dev/null
+++ b/client-react/src/reducers/constants.ts
@@ -0,0 +1,12 @@
+
+export const FETCH_STATE_REQUEST = '@portal/fetch_state_request';
+export const FETCH_STATE_SUCCESS = '@portal/fetch_state_success';
+export const FETCH_STATE_FAILURE = '@portal/fetch_state_failure';
+
+export const AUTHENTICATE_REQUEST = '@portal/authenticate_request';
+export const AUTHENTICATE_SUCCESS = '@portal/authenticate_success';
+export const AUTHENTICATE_FAILURE = '@portal/authenticate_failure';
+
+export const LOGOUT_REQUEST = '@portal/logout_request';
+export const LOGOUT_SUCCESS = '@portal/logout_success';
+export const LOGOUT_FAILURE = '@portal/logout_failure';
diff --git a/client-react/src/reducers/index.ts b/client-react/src/reducers/index.ts
new file mode 100644
index 00000000..09dcf83f
--- /dev/null
+++ b/client-react/src/reducers/index.ts
@@ -0,0 +1,5 @@
+import PortalReducer, { PortalState } from './Portal/reducer';
+
+export type RootState = PortalState;
+
+export default PortalReducer;
\ No newline at end of file
diff --git a/client-react/src/routes/index.ts b/client-react/src/routes/index.ts
index 990500dc..731c0a05 100644
--- a/client-react/src/routes/index.ts
+++ b/client-react/src/routes/index.ts
@@ -1,4 +1,4 @@
-import PortalLayout from "../layouts/PortalLayout/PortalLayout";
+import PortalLayout from "../containers/layouts/PortalLayout/PortalLayout";
export const routes = [{
path: '/',
diff --git a/client-react/src/routes/routes.ts b/client-react/src/routes/routes.ts
index eb8950be..f7d54929 100644
--- a/client-react/src/routes/routes.ts
+++ b/client-react/src/routes/routes.ts
@@ -1,8 +1,8 @@
-import FirstFactorView from "../views/FirstFactorView/FirstFactorView";
-import SecondFactorView from "../views/SecondFactorView/SecondFactorView";
-import ConfirmationSent from "../views/ConfirmationSentView/ConfirmationSentView";
-import OneTimePasswordRegistrationView from "../views/OneTimePasswordRegistrationView/OneTimePasswordRegistrationView";
-import SecurityKeyRegistrationView from "../views/SecurityKeyRegistrationView/SecurityKeyRegistrationView";
+import FirstFactorView from "../containers/views/FirstFactorView/FirstFactorView";
+import SecondFactorView from "../containers/views/SecondFactorView/SecondFactorView";
+import ConfirmationSentView from "../views/ConfirmationSentView/ConfirmationSentView";
+import OneTimePasswordRegistrationView from "../containers/views/OneTimePasswordRegistrationView/OneTimePasswordRegistrationView";
+import SecurityKeyRegistrationView from "../containers/views/SecurityKeyRegistrationView/SecurityKeyRegistrationView";
import ForgotPasswordView from "../views/ForgotPasswordView/ForgotPasswordView";
import ResetPasswordView from "../views/ResetPasswordView/ResetPasswordView";
@@ -15,9 +15,9 @@ export const routes = [{
title: '2-factor',
component: SecondFactorView,
}, {
- path: '/confirm',
+ path: '/confirmation-sent',
title: 'e-mail sent',
- component: ConfirmationSent
+ component: ConfirmationSentView
}, {
path: '/one-time-password-registration',
title: 'One-time password registration',
diff --git a/client-react/src/types/AuthenticationLevel.ts b/client-react/src/types/AuthenticationLevel.ts
new file mode 100644
index 00000000..550cc947
--- /dev/null
+++ b/client-react/src/types/AuthenticationLevel.ts
@@ -0,0 +1,7 @@
+enum AuthenticationLevel {
+ NOT_AUTHENTICATED = 0,
+ ONE_FACTOR = 1,
+ TWO_FACTOR = 2
+};
+
+export default AuthenticationLevel;
\ No newline at end of file
diff --git a/client-react/src/views/ConfirmationSentView/ConfirmationSentView.tsx b/client-react/src/views/ConfirmationSentView/ConfirmationSentView.tsx
index ae253834..113fb7cd 100644
--- a/client-react/src/views/ConfirmationSentView/ConfirmationSentView.tsx
+++ b/client-react/src/views/ConfirmationSentView/ConfirmationSentView.tsx
@@ -9,7 +9,7 @@ import { RouterProps } from "react-router";
interface Props extends RouterProps {}
-export default class ConfirmationSent extends Component {
+class ConfirmationSentView extends Component {
render() {
return (
@@ -20,15 +20,17 @@ export default class ConfirmationSent extends Component
{
Please check your e-mails and follow the instructions to confirm the operation.
this.props.history.push('/')}
+ onClick={() => this.props.history.goBack()}
className={styles.button}
variant="contained"
color="primary">
- Back to login
+ Back
)
}
-}
\ No newline at end of file
+}
+
+export default ConfirmationSentView;
\ No newline at end of file
diff --git a/client-react/src/views/FirstFactorView/FirstFactorView.tsx b/client-react/src/views/FirstFactorView/FirstFactorView.tsx
index 179a0820..b232468d 100644
--- a/client-react/src/views/FirstFactorView/FirstFactorView.tsx
+++ b/client-react/src/views/FirstFactorView/FirstFactorView.tsx
@@ -11,14 +11,24 @@ import { RouterProps } from "react-router";
import { WithStyles, withStyles } from "@material-ui/core";
import firstFactorViewStyles from '../../assets/jss/views/FirstFactorView/FirstFactorView';
+import FormNotification from "../../components/FormNotification/FormNotification";
-interface Props extends RouterProps, WithStyles {}
+import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'
+import CheckBoxIcon from '@material-ui/icons/CheckBox';
+import StateSynchronizer from "../../containers/components/StateSynchronizer/StateSynchronizer";
+import RemoteState from "../../reducers/Portal/RemoteState";
+
+export interface Props extends RouterProps, WithStyles {
+ onAuthenticationRequested(username: string, password: string): void;
+}
interface State {
rememberMe: boolean;
username: string;
password: string;
loginButtonDisabled: boolean;
+ errorMessage: string | null;
+ remoteState: RemoteState | null;
}
class FirstFactorView extends Component