summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Fertser <fercerpav@gmail.com>2024-07-05 14:04:04 +0300
committerPaul Fertser <fercerpav@gmail.com>2024-11-07 23:16:56 +0300
commitce7db82c9582c4dac04ac81d9af6b557ae7965e3 (patch)
tree82ae65bb48ccb9df7d75ef6d95c1f43c54be9fb1
parent918526f20c16a05c261a56814657942a707323dd (diff)
downloadwebui-vue-ce7db82c9582c4dac04ac81d9af6b557ae7965e3.tar.xz
Retrieve role information the Redfish standard way
Currently webui-vue has a hardcoded list of pages and sidebar menu items restricted to a specific Redfish role (from a predefined default set). To disallow navigating to restricted pages and to hide disallowed menu items the application needs to know the roles assigned to the session. bmcweb only implements a single role identity per session so the Roles array returned within a Session object always has just one element. This patch changes the mechanism used to retrieve the current role from buggy direct query to AccountService (which can only return information about BMC local users) to extracting it from standard Redfish Session object. In case the role is not available (e.g. when backend implementation predates #Session.v1_7_0.Session) the application assumes Administrator role which is meant as a best effort to continue working given the circumstances. This doesn't pose a security risk because all validation is always performed by the backend itself, so the worst that can happen is end user getting error messages trying to access something without enough privileges. Tested: logging in and out of accounts with different roles without reloading the page, observing the list of queries made, the role variable assignments and presence of the menu items depending on account, navigating to different pages. Also tested reloading the page and confirmed the correct role was retrieved without going through login again. Also tested deleting and mangling localStorage variable sessionURI prior to doing page reload, in those cases redirect to login page was observed. Change-Id: I8b6c84060a987489cc1d35c46c1b00618a88b607 Signed-off-by: Paul Fertser <fercerpav@gmail.com>
-rw-r--r--src/router/index.js16
-rw-r--r--src/store/api.js6
-rw-r--r--src/store/modules/Authentication/AuthenticanStore.js43
3 files changed, 37 insertions, 28 deletions
diff --git a/src/router/index.js b/src/router/index.js
index 22a3a8ceb..27fd96e65 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -42,11 +42,17 @@ router.beforeEach((to, from, next) => {
// condition will get satisfied if user refreshed after login
if (!currentUserRole && store.getters['authentication/isLoggedIn']) {
// invoke API call to get the role ID
- let username = localStorage.getItem('storedUsername');
- store.dispatch('authentication/getUserInfo', username).then(() => {
- let currentUserRole = store.getters['global/userPrivilege'];
- allowRouterToNavigate(to, next, currentUserRole);
- });
+ store
+ .dispatch('authentication/getSessionPrivilege')
+ .then(() => {
+ let currentUserRole = store.getters['global/userPrivilege'];
+ allowRouterToNavigate(to, next, currentUserRole);
+ })
+ // our store got out of sync, start afresh
+ .catch(() => {
+ console.log('Failed to obtain current Roles, logging out.');
+ store.dispatch('authentication/logout');
+ });
} else {
allowRouterToNavigate(to, next, currentUserRole);
}
diff --git a/src/store/api.js b/src/store/api.js
index babed4c8a..0e119c287 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -39,7 +39,7 @@ api.interceptors.response.use(undefined, (error) => {
// Check if action is unauthorized.
if (response.status == 403) {
- if (isPasswordExpired(response)) {
+ if (isPasswordExpired(response.data)) {
router.push('/change-password');
} else {
// Toast error message will appear on screen.
@@ -92,8 +92,8 @@ export const getResponseCount = (responses) => {
};
};
-export const isPasswordExpired = (response) => {
- let extInfoMsgs = response?.data?.['@Message.ExtendedInfo'];
+export const isPasswordExpired = (data) => {
+ let extInfoMsgs = data?.['@Message.ExtendedInfo'];
return (
extInfoMsgs &&
extInfoMsgs.find(
diff --git a/src/store/modules/Authentication/AuthenticanStore.js b/src/store/modules/Authentication/AuthenticanStore.js
index 3122ab2f7..e876f780e 100644
--- a/src/store/modules/Authentication/AuthenticanStore.js
+++ b/src/store/modules/Authentication/AuthenticanStore.js
@@ -68,12 +68,13 @@ const AuthenticationStore = {
UserName: username,
Password: password,
})
- .then((response) => {
+ .then(({ headers, data }) => {
commit('authSuccess', {
- session: response.headers['location'],
- token: response.headers['x-auth-token'],
+ session: headers['location'],
+ token: headers['x-auth-token'],
});
- return isPasswordExpired(response);
+ setSessionPrivilege(commit, data);
+ return isPasswordExpired(data);
})
.catch((error) => {
commit('authError');
@@ -83,27 +84,19 @@ const AuthenticationStore = {
logout({ commit, state }) {
api
.delete(state.sessionURI)
+ .catch(() =>
+ console.log(
+ "Couldn't DELETE Session, proceeding with the logout anyway to get in sync with the backend.",
+ ),
+ )
.then(() => commit('logout'))
.then(() => router.push('/login'))
.catch((error) => console.log(error));
},
- getUserInfo({ commit }, username) {
+ getSessionPrivilege({ commit, state }) {
return api
- .get(`/redfish/v1/AccountService/Accounts/${username}`)
- .then(({ data }) => {
- commit('global/setPrivilege', data.RoleId, { root: true });
- return data;
- })
- .catch((error) => {
- if (error.response?.status === 404) {
- // We have valid credentials but user isn't known, assume remote
- // authentication (e.g. LDAP) and do not restrict the routing
- commit('global/setPrivilege', roles.administrator, { root: true });
- return {};
- } else {
- console.log(error);
- }
- });
+ .get(state.sessionURI)
+ .then(({ data }) => setSessionPrivilege(commit, data));
},
resetStoreState({ state }) {
state.authError = false;
@@ -113,4 +106,14 @@ const AuthenticationStore = {
},
};
+const setSessionPrivilege = (commit, data) => {
+ // If the backend didn't provide the role information in the Session object
+ // our best bet is to assume the Administrator role to avoid hiding
+ // potentially useful UI elements. Everything security-sensitive is validated
+ // on the backend side anyway, so this is safe.
+ commit('global/setPrivilege', data.Roles?.[0] ?? roles.administrator, {
+ root: true,
+ });
+};
+
export default AuthenticationStore;