chore: Allow super admin to suspend an account (#5174)

This commit is contained in:
Pranav Raj S
2022-08-03 11:40:03 +05:30
committed by GitHub
parent 4152883f38
commit e0cebfaa1a
20 changed files with 259 additions and 23 deletions

View File

@@ -0,0 +1,51 @@
export const getCurrentAccount = ({ accounts } = {}, accountId) => {
return accounts.find(account => account.id === accountId);
};
export const getUserRole = ({ accounts } = {}, accountId) => {
const currentAccount = getCurrentAccount({ accounts }, accountId) || {};
return currentAccount.role || null;
};
export const routeIsAccessibleFor = (route, role, roleWiseRoutes) => {
return roleWiseRoutes[role].includes(route);
};
const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
// If the current account is active, then check for the route permissions
const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`;
// If the user is trying to access suspended route, redirect them to dashboard
if (to.name === 'account_suspended') {
return accountDashboardURL;
}
const userRole = getUserRole(user, Number(to.params.accountId));
const isAccessible = routeIsAccessibleFor(to.name, userRole, roleWiseRoutes);
// If the route is not accessible for the user, return to dashboard screen
return isAccessible ? null : accountDashboardURL;
};
export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
const currentAccount = getCurrentAccount(user, Number(to.params.accountId));
// If current account is missing, either user does not have
// access to the account or the account is deleted, return to login screen
if (!currentAccount) {
return `app/login`;
}
const isCurrentAccountActive = currentAccount.status === 'active';
if (isCurrentAccountActive) {
return validateActiveAccountRoutes(to, user, roleWiseRoutes);
}
// If the current account is not active, then redirect the user to the suspended screen
if (to.name !== 'account_suspended') {
return `accounts/${to.params.accountId}/suspended`;
}
// Proceed to the route if none of the above conditions are met
return null;
};

View File

@@ -0,0 +1,96 @@
import {
getCurrentAccount,
getUserRole,
routeIsAccessibleFor,
validateLoggedInRoutes,
} from '../routeHelpers';
describe('#getCurrentAccount', () => {
it('should return the current account', () => {
expect(getCurrentAccount({ accounts: [{ id: 1 }] }, 1)).toEqual({ id: 1 });
expect(getCurrentAccount({ accounts: [] }, 1)).toEqual(undefined);
});
});
describe('#getUserRole', () => {
it('should return the current role', () => {
expect(
getUserRole({ accounts: [{ id: 1, role: 'administrator' }] }, 1)
).toEqual('administrator');
expect(getUserRole({ accounts: [] }, 1)).toEqual(null);
});
});
describe('#routeIsAccessibleFor', () => {
it('should return the correct access', () => {
const roleWiseRoutes = { agent: ['conversations'], admin: ['billing'] };
expect(routeIsAccessibleFor('billing', 'agent', roleWiseRoutes)).toEqual(
false
);
expect(routeIsAccessibleFor('billing', 'admin', roleWiseRoutes)).toEqual(
true
);
});
});
describe('#validateLoggedInRoutes', () => {
describe('when account access is missing', () => {
it('should return the login route', () => {
expect(
validateLoggedInRoutes(
{ params: { accountId: 1 } },
{ accounts: [] },
{}
)
).toEqual(`app/login`);
});
});
describe('when account access is available', () => {
describe('when account is suspended', () => {
it('return suspended route', () => {
expect(
validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } },
{ accounts: [{ id: 1, role: 'agent', status: 'suspended' }] },
{ agent: ['conversations'] }
)
).toEqual(`accounts/1/suspended`);
});
});
describe('when account is active', () => {
describe('when route is accessible', () => {
it('returns null (no action required)', () => {
expect(
validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } },
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
{ agent: ['conversations'] }
)
).toEqual(null);
});
});
describe('when route is not accessible', () => {
it('returns dashboard url', () => {
expect(
validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } },
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
{ admin: ['conversations'], agent: [] }
)
).toEqual(`accounts/1/dashboard`);
});
});
describe('when route is suspended route', () => {
it('returns dashboard url', () => {
expect(
validateLoggedInRoutes(
{ name: 'account_suspended', params: { accountId: 1 } },
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] },
{ agent: ['account_suspended'] }
)
).toEqual(`accounts/1/dashboard`);
});
});
});
});
});

View File

@@ -120,7 +120,11 @@
"APP_GLOBAL": {
"TRIAL_MESSAGE": "days trial remaining.",
"TRAIL_BUTTON": "Buy Now",
"DELETED_USER": "Deleted User"
"DELETED_USER": "Deleted User",
"ACCOUNT_SUSPENDED": {
"TITLE": "Account Suspended",
"MESSAGE": "Your account is suspended. Please reach out to the support team for more information."
}
},
"COMPONENTS": {
"CODE": {
@@ -199,7 +203,7 @@
},
"BILLING_SETTINGS": {
"TITLE": "Billing",
"CURRENT_PLAN" : {
"CURRENT_PLAN": {
"TITLE": "Current Plan",
"PLAN_NOTE": "You are currently subscribed to the **%{plan}** plan with **%{quantity}** licenses"
},

View File

@@ -6,6 +6,8 @@ import { routes as notificationRoutes } from './notifications/routes';
import { frontendURL } from '../../helper/URLHelper';
import helpcenterRoutes from './helpcenter/helpcenter.routes';
const Suspended = () => import('./suspended/Index');
export default {
routes: [
...helpcenterRoutes.routes,
@@ -19,5 +21,11 @@ export default {
...notificationRoutes,
],
},
{
path: frontendURL('accounts/:accountId/suspended'),
name: 'account_suspended',
roles: ['administrator', 'agent'],
component: Suspended,
},
],
};

View File

@@ -0,0 +1,24 @@
<template>
<div class="suspended-page">
<empty-state
:title="$t('APP_GLOBAL.ACCOUNT_SUSPENDED.TITLE')"
:message="$t('APP_GLOBAL.ACCOUNT_SUSPENDED.MESSAGE')"
/>
</div>
</template>
<script>
import EmptyState from 'dashboard/components/widgets/EmptyState';
export default {
components: { EmptyState },
};
</script>
<style scoped>
.suspended-page {
align-items: center;
background: var(--s-50);
display: flex;
justify-content: center;
height: 100%;
width: 100%;
}
</style>

View File

@@ -6,6 +6,7 @@ import authRoute from './auth/auth.routes';
import dashboard from './dashboard/dashboard.routes';
import login from './login/login.routes';
import store from '../store';
import { validateLoggedInRoutes } from '../helper/routeHelpers';
const routes = [...login.routes, ...dashboard.routes, ...authRoute.routes];
@@ -14,11 +15,6 @@ window.roleWiseRoutes = {
administrator: [],
};
const getUserRole = ({ accounts } = {}, accountId) => {
const currentAccount = accounts.find(account => account.id === accountId);
return currentAccount ? currentAccount.role : null;
};
// generateRoleWiseRoute - updates window object with agent/admin route
const generateRoleWiseRoute = route => {
route.forEach(element => {
@@ -47,10 +43,6 @@ const authIgnoreRoutes = [
'auth_password_edit',
];
function routeIsAccessibleFor(route, role) {
return window.roleWiseRoutes[role].includes(route);
}
const routeValidators = [
{
protected: false,
@@ -68,12 +60,8 @@ const routeValidators = [
{
protected: true,
loggedIn: true,
handler: (to, getters) => {
const user = getters.getCurrentUser;
const userRole = getUserRole(user, Number(to.params.accountId));
const isAccessible = routeIsAccessibleFor(to.name, userRole);
return isAccessible ? null : `accounts/${to.params.accountId}/dashboard`;
},
handler: (to, getters) =>
validateLoggedInRoutes(to, getters.getCurrentUser, window.roleWiseRoutes),
},
{
protected: false,

View File

@@ -26,7 +26,7 @@ describe('#validateAuthenticateRoutePermission', () => {
getCurrentUser: {
account_id: 1,
id: 1,
accounts: [{ id: 1, role: 'admin' }],
accounts: [{ id: 1, role: 'admin', status: 'active' }],
},
};
validateAuthenticateRoutePermission(to, from, next, { getters });
@@ -72,7 +72,7 @@ describe('#validateAuthenticateRoutePermission', () => {
getCurrentUser: {
account_id: 1,
id: 1,
accounts: [{ id: 1, role: 'agent' }],
accounts: [{ id: 1, role: 'agent', status: 'active' }],
},
};
validateAuthenticateRoutePermission(to, from, next, { getters });
@@ -90,7 +90,7 @@ describe('#validateAuthenticateRoutePermission', () => {
getCurrentUser: {
account_id: 1,
id: 1,
accounts: [{ id: 1, role: 'agent' }],
accounts: [{ id: 1, role: 'agent', status: 'active' }],
},
};
validateAuthenticateRoutePermission(to, from, next, { getters });