+ {data && (isSuperAdmin || data.id === loggedUserId) && (
+
{!data.privateData.isLocal && (
+
+
+ {data.privateData.isAllowed && (
+
+ }
+ onClick={this.invalidateTokens}>
+ {this.state.invalidateTokensInProgress ? (
+
+ ) : (
+
+ )}
+
+
+ )}
+
)}
@@ -269,6 +321,9 @@ EditUser.propTypes = {
takeOver: PropTypes.func.isRequired,
createCalendar: PropTypes.func.isRequired,
setCalendarExpired: PropTypes.func.isRequired,
+ invalidateTokens: PropTypes.func.isRequired,
+ logout: PropTypes.func.isRequired,
+ addNotification: PropTypes.func.isRequired,
};
export default connect(
@@ -307,5 +362,8 @@ export default connect(
takeOver: userId => dispatch(takeOver(userId)),
createCalendar: () => dispatch(createUserCalendar(userId)),
setCalendarExpired: calendarId => dispatch(setUserCalendarExpired(userId, calendarId)),
+ invalidateTokens: () => dispatch(invalidateTokens(userId)),
+ logout: () => dispatch(logout()),
+ addNotification: (msg, successful) => dispatch(addNotification(msg, successful)),
})
)(EditUser);
diff --git a/src/pages/Login/Login.js b/src/pages/Login/Login.js
index fcaa5706c..04b5674e9 100644
--- a/src/pages/Login/Login.js
+++ b/src/pages/Login/Login.js
@@ -30,6 +30,7 @@ import { withRouterProps } from '../../helpers/withRouter.js';
const EXTERNAL_AUTH_URL = getConfigVar('EXTERNAL_AUTH_URL');
const EXTERNAL_AUTH_SERVICE_ID = getConfigVar('EXTERNAL_AUTH_SERVICE_ID');
const EXTERNAL_AUTH_HELPDESK_URL = getConfigVar('EXTERNAL_AUTH_HELPDESK_URL');
+const SHORT_SESSION = getConfigVar('SHORT_SESSION');
class Login extends Component {
/**
@@ -77,12 +78,12 @@ class Login extends Component {
/**
* Log the user in (by given credentials) and then perform the redirect.
*/
- loginAndRedirect = credentials => {
+ loginAndRedirect = ({ short, ...credentials }) => {
const {
login,
intl: { formatMessage },
} = this.props;
- return login(credentials)
+ return login(credentials, short && SHORT_SESSION ? SHORT_SESSION * 60 : null)
.then(this.redirectAfterLogin)
.catch(error => {
// Translate fetch response error into form error message...
@@ -155,8 +156,9 @@ class Login extends Component {
lg={{ span: 4, offset: external ? 1 : 4 }}
md={{ span: 6, offset: external ? 0 : 3 }}
sm={{ span: 10, offset: 2 }}
- xs={{ spna: 12, offset: 0 }}>
-
+ xs={{ span: 12, offset: 0 }}>
+
+
)}
@@ -215,7 +218,7 @@ export default withLinks(
loggedInUser: loggedInUserSelector(state),
}),
dispatch => ({
- login: ({ email, password }) => dispatch(login(email, password)),
+ login: ({ email, password }, expiration) => dispatch(login(email, password, expiration)),
logout: () => dispatch(logout()),
reset: () => {
dispatch(reset('login'));
diff --git a/src/redux/modules/auth.js b/src/redux/modules/auth.js
index 62a06837d..b6ea61a1f 100644
--- a/src/redux/modules/auth.js
+++ b/src/redux/modules/auth.js
@@ -26,12 +26,12 @@ export const takeOver = userId =>
meta: { service: LOCAL_LOGIN },
});
-export const login = (username, password) =>
+export const login = (username, password, expiration = null) =>
createApiAction({
type: actionTypes.LOGIN,
method: 'POST',
endpoint: '/login',
- body: { username, password },
+ body: { username, password, expiration },
meta: { service: LOCAL_LOGIN },
});
@@ -62,12 +62,12 @@ export const validatePasswordStrength = password =>
body: { password },
});
-export const externalLogin = (service, token, popupWindow = null) =>
+export const externalLogin = (service, token, expiration = null, popupWindow = null) =>
createApiAction({
type: actionTypes.LOGIN,
method: 'POST',
endpoint: `/login/${service}`,
- body: { token },
+ body: { token, expiration },
meta: { service, popupWindow },
});
diff --git a/src/redux/modules/users.js b/src/redux/modules/users.js
index 6934eed7d..997244a6d 100644
--- a/src/redux/modules/users.js
+++ b/src/redux/modules/users.js
@@ -27,6 +27,7 @@ export const additionalActionTypes = {
...createActionsWithPostfixes('SET_IS_ALLOWED', 'recodex/users'),
...createActionsWithPostfixes('INVITE_USER', 'recodex/users'),
...createActionsWithPostfixes('ACCEPT_INVITATION', 'recodex/users'),
+ ...createActionsWithPostfixes('INVALIDATE_TOKENS', 'recodex/users'),
};
const resourceName = 'users';
@@ -113,6 +114,14 @@ export const acceptInvitation = (password, token) =>
body: { token, password, passwordConfirm: password },
});
+export const invalidateTokens = id =>
+ createApiAction({
+ type: additionalActionTypes.INVALIDATE_TOKENS,
+ endpoint: `/users/${id}/invalidate-tokens`,
+ method: 'POST',
+ meta: { id },
+ });
+
/**
* Reducer
*/
diff --git a/test/redux/modules/auth-test.js b/test/redux/modules/auth-test.js
index b18b2e9f0..65d7e2c8e 100644
--- a/test/redux/modules/auth-test.js
+++ b/test/redux/modules/auth-test.js
@@ -30,7 +30,7 @@ describe('Authentication', () => {
type: actionTypes.LOGIN,
method: 'POST',
endpoint: '/login',
- body: { username: 'usr', password: 'pwd' },
+ body: { username: 'usr', password: 'pwd', expiration: null },
meta: { service: LOCAL_LOGIN },
});
});
@@ -45,6 +45,7 @@ describe('Authentication', () => {
endpoint: `/login/${serviceId}`,
body: {
token,
+ expiration: null,
},
meta: { service: serviceId, popupWindow: null },
});