Skip to content

Commit

Permalink
Handle expired passwords Redfish standard way
Browse files Browse the repository at this point in the history
A password can expire at any moment during session lifetime and bmcweb
starts returning 403 Forbidden errors to the requests made after that.
The response contains clear indication of the condition in the standard
`@Message.ExtendedInfo` attribute which is an array of Message objects.

Previously the code was trying to detect this condition by querying
AccountService after logging in but this approach doesn't work when
password expires mid-session. Also it was limited to BMC-managed
accounts and used hardcoded account URIs in violation of Redfish spec.

This patch adds to the interceptor of 403 error so that the user is
automatically redirected to the password change page as soon as the
condition is detected.

The same message is also present in the session creation POST response
201 if the password expired before the log in attempt, in this case the
session is created as usual but the user is automatically redirected to
password change page before any further requests are made.

Tested: logging in, navigating, logging out with non-expired password.
Logging in, navigating, then running `passwd -e <accountname>` via ssh
leads to functional password change page on the next request and then
navigating proceeds normally, and logging out too. If password is
expired before logging in the user gets redirected to the password
change page automatically after logging in.

Fixes: #118
Change-Id: I03f5ee2526a4bb1d35d3bbea1142fea077d6bfed
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
  • Loading branch information
paulfertser committed Aug 12, 2024
1 parent 582e954 commit 6de0341
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 9 deletions.
22 changes: 18 additions & 4 deletions src/store/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Axios from 'axios';
import router from '../router';
import { setupCache, buildWebStorage } from 'axios-cache-interceptor';

//Do not change store import.
Expand Down Expand Up @@ -36,11 +37,14 @@ api.interceptors.response.use(undefined, (error) => {
}
}

// Check if action is unauthorized.
if (response.status == 403) {
// Check if action is unauthorized.
// Toast error message will appear on screen
// when the action is unauthorized.
store.commit('global/setUnauthorized');
if (isPasswordExpired(response)) {
router.push('/change-password');
} else {
// Toast error message will appear on screen.
store.commit('global/setUnauthorized');
}
}

return Promise.reject(error);
Expand Down Expand Up @@ -84,3 +88,13 @@ export const getResponseCount = (responses) => {
errorCount,
};
};

export const isPasswordExpired = (response) => {
let extInfoMsgs = response?.data?.['@Message.ExtendedInfo'];
return (
extInfoMsgs &&
extInfoMsgs.find(
(i) => i.MessageId.split('.')[4] === 'PasswordChangeRequired',
)
);
};
3 changes: 2 additions & 1 deletion src/store/modules/Authentication/AuthenticanStore.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import api from '@/store/api';
import api, { isPasswordExpired } from '@/store/api';
import Cookies from 'js-cookie';
import router from '@/router';
import { roles } from '@/router/routes';
Expand Down Expand Up @@ -59,6 +59,7 @@ const AuthenticationStore = {
commit('authSuccess', {
session: response.headers['location'],
});
return isPasswordExpired(response);
})
.catch((error) => {
commit('authError');
Expand Down
5 changes: 1 addition & 4 deletions src/views/Login/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,11 @@ export default {
const password = this.userInfo.password;
this.$store
.dispatch('authentication/login', { username, password })
.then(() => {
.then((PasswordChangeRequired) => {
localStorage.setItem('storedLanguage', i18n.locale);
localStorage.setItem('storedUsername', username);
this.$store.commit('global/setUsername', username);
this.$store.commit('global/setLanguagePreference', i18n.locale);
return this.$store.dispatch('authentication/getUserInfo', username);
})
.then(({ PasswordChangeRequired }) => {
if (PasswordChangeRequired) {
this.$router.push('/change-password');
} else {
Expand Down

0 comments on commit 6de0341

Please sign in to comment.