chore: Move frontend authorization to permission based system (#9709)

We previously relied on user roles to determine whether to render
specific routes in our frontend components. A permissions-based model is replacing this approach.


Follow up: #9695

Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
Sojan Jose
2024-07-03 15:13:16 -07:00
committed by GitHub
parent 5520bf68f3
commit cc4851b19d
37 changed files with 582 additions and 229 deletions

View File

@@ -20,7 +20,7 @@
:teams="teams" :teams="teams"
:custom-views="customViews" :custom-views="customViews"
:menu-config="activeSecondaryMenu" :menu-config="activeSecondaryMenu"
:current-role="currentRole" :current-user="currentUser"
:is-on-chatwoot-cloud="isOnChatwootCloud" :is-on-chatwoot-cloud="isOnChatwootCloud"
@add-label="showAddLabelPopup" @add-label="showAddLabelPopup"
@toggle-accounts="toggleAccountModal" @toggle-accounts="toggleAccountModal"
@@ -37,7 +37,8 @@ import alertMixin from 'shared/mixins/alertMixin';
import PrimarySidebar from './sidebarComponents/Primary.vue'; import PrimarySidebar from './sidebarComponents/Primary.vue';
import SecondarySidebar from './sidebarComponents/Secondary.vue'; import SecondarySidebar from './sidebarComponents/Secondary.vue';
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins'; import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
import router from '../../routes'; import router, { routesWithPermissions } from '../../routes';
import { hasPermissions } from '../../helper/permissionsHelper';
export default { export default {
components: { components: {
@@ -98,9 +99,13 @@ export default {
return getSidebarItems(this.accountId); return getSidebarItems(this.accountId);
}, },
primaryMenuItems() { primaryMenuItems() {
const userPermissions = this.currentUser.permissions;
const menuItems = this.sideMenuConfig.primaryMenu; const menuItems = this.sideMenuConfig.primaryMenu;
return menuItems.filter(menuItem => { return menuItems.filter(menuItem => {
const isAvailableForTheUser = menuItem.roles.includes(this.currentRole); const isAvailableForTheUser = hasPermissions(
routesWithPermissions[menuItem.toStateName],
userPermissions
);
if (!isAvailableForTheUser) { if (!isAvailableForTheUser) {
return false; return false;

View File

@@ -9,7 +9,6 @@ const primaryMenuItems = accountId => [
featureFlag: FEATURE_FLAGS.INBOX_VIEW, featureFlag: FEATURE_FLAGS.INBOX_VIEW,
toState: frontendURL(`accounts/${accountId}/inbox-view`), toState: frontendURL(`accounts/${accountId}/inbox-view`),
toStateName: 'inbox_view', toStateName: 'inbox_view',
roles: ['administrator', 'agent'],
}, },
{ {
icon: 'chat', icon: 'chat',
@@ -17,7 +16,6 @@ const primaryMenuItems = accountId => [
label: 'CONVERSATIONS', label: 'CONVERSATIONS',
toState: frontendURL(`accounts/${accountId}/dashboard`), toState: frontendURL(`accounts/${accountId}/dashboard`),
toStateName: 'home', toStateName: 'home',
roles: ['administrator', 'agent'],
}, },
{ {
icon: 'book-contacts', icon: 'book-contacts',
@@ -26,7 +24,6 @@ const primaryMenuItems = accountId => [
featureFlag: FEATURE_FLAGS.CRM, featureFlag: FEATURE_FLAGS.CRM,
toState: frontendURL(`accounts/${accountId}/contacts`), toState: frontendURL(`accounts/${accountId}/contacts`),
toStateName: 'contacts_dashboard', toStateName: 'contacts_dashboard',
roles: ['administrator', 'agent'],
}, },
{ {
icon: 'arrow-trending-lines', icon: 'arrow-trending-lines',
@@ -34,8 +31,7 @@ const primaryMenuItems = accountId => [
label: 'REPORTS', label: 'REPORTS',
featureFlag: FEATURE_FLAGS.REPORTS, featureFlag: FEATURE_FLAGS.REPORTS,
toState: frontendURL(`accounts/${accountId}/reports`), toState: frontendURL(`accounts/${accountId}/reports`),
toStateName: 'settings_account_reports', toStateName: 'account_overview_reports',
roles: ['administrator'],
}, },
{ {
icon: 'megaphone', icon: 'megaphone',
@@ -44,7 +40,6 @@ const primaryMenuItems = accountId => [
featureFlag: FEATURE_FLAGS.CAMPAIGNS, featureFlag: FEATURE_FLAGS.CAMPAIGNS,
toState: frontendURL(`accounts/${accountId}/campaigns`), toState: frontendURL(`accounts/${accountId}/campaigns`),
toStateName: 'ongoing_campaigns', toStateName: 'ongoing_campaigns',
roles: ['administrator'],
}, },
{ {
icon: 'library', icon: 'library',
@@ -54,7 +49,6 @@ const primaryMenuItems = accountId => [
alwaysVisibleOnChatwootInstances: true, alwaysVisibleOnChatwootInstances: true,
toState: frontendURL(`accounts/${accountId}/portals`), toState: frontendURL(`accounts/${accountId}/portals`),
toStateName: 'default_portal_articles', toStateName: 'default_portal_articles',
roles: ['administrator'],
}, },
{ {
icon: 'settings', icon: 'settings',
@@ -62,7 +56,6 @@ const primaryMenuItems = accountId => [
label: 'SETTINGS', label: 'SETTINGS',
toState: frontendURL(`accounts/${accountId}/settings`), toState: frontendURL(`accounts/${accountId}/settings`),
toStateName: 'settings_home', toStateName: 'settings_home',
roles: ['administrator', 'agent'],
}, },
]; ];

View File

@@ -24,7 +24,6 @@ const settings = accountId => ({
'settings_inbox_list', 'settings_inbox_list',
'settings_inbox_new', 'settings_inbox_new',
'settings_inbox_show', 'settings_inbox_show',
'settings_inbox',
'settings_inboxes_add_agents', 'settings_inboxes_add_agents',
'settings_inboxes_page_channel', 'settings_inboxes_page_channel',
'settings_integrations_dashboard_apps', 'settings_integrations_dashboard_apps',
@@ -46,6 +45,9 @@ const settings = accountId => ({
icon: 'briefcase', icon: 'briefcase',
label: 'ACCOUNT_SETTINGS', label: 'ACCOUNT_SETTINGS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/general`), toState: frontendURL(`accounts/${accountId}/settings/general`),
toStateName: 'general_settings_index', toStateName: 'general_settings_index',
}, },
@@ -53,6 +55,9 @@ const settings = accountId => ({
icon: 'people', icon: 'people',
label: 'AGENTS', label: 'AGENTS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/agents/list`), toState: frontendURL(`accounts/${accountId}/settings/agents/list`),
toStateName: 'agent_list', toStateName: 'agent_list',
featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT, featureFlag: FEATURE_FLAGS.AGENT_MANAGEMENT,
@@ -61,6 +66,9 @@ const settings = accountId => ({
icon: 'people-team', icon: 'people-team',
label: 'TEAMS', label: 'TEAMS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/teams/list`), toState: frontendURL(`accounts/${accountId}/settings/teams/list`),
toStateName: 'settings_teams_list', toStateName: 'settings_teams_list',
featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT, featureFlag: FEATURE_FLAGS.TEAM_MANAGEMENT,
@@ -69,6 +77,9 @@ const settings = accountId => ({
icon: 'mail-inbox-all', icon: 'mail-inbox-all',
label: 'INBOXES', label: 'INBOXES',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`), toState: frontendURL(`accounts/${accountId}/settings/inboxes/list`),
toStateName: 'settings_inbox_list', toStateName: 'settings_inbox_list',
featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT, featureFlag: FEATURE_FLAGS.INBOX_MANAGEMENT,
@@ -77,6 +88,9 @@ const settings = accountId => ({
icon: 'tag', icon: 'tag',
label: 'LABELS', label: 'LABELS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/labels/list`), toState: frontendURL(`accounts/${accountId}/settings/labels/list`),
toStateName: 'labels_list', toStateName: 'labels_list',
featureFlag: FEATURE_FLAGS.LABELS, featureFlag: FEATURE_FLAGS.LABELS,
@@ -85,6 +99,9 @@ const settings = accountId => ({
icon: 'code', icon: 'code',
label: 'CUSTOM_ATTRIBUTES', label: 'CUSTOM_ATTRIBUTES',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL( toState: frontendURL(
`accounts/${accountId}/settings/custom-attributes/list` `accounts/${accountId}/settings/custom-attributes/list`
), ),
@@ -95,6 +112,9 @@ const settings = accountId => ({
icon: 'automation', icon: 'automation',
label: 'AUTOMATION', label: 'AUTOMATION',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/automation/list`), toState: frontendURL(`accounts/${accountId}/settings/automation/list`),
toStateName: 'automation_list', toStateName: 'automation_list',
featureFlag: FEATURE_FLAGS.AUTOMATIONS, featureFlag: FEATURE_FLAGS.AUTOMATIONS,
@@ -103,6 +123,9 @@ const settings = accountId => ({
icon: 'bot', icon: 'bot',
label: 'AGENT_BOTS', label: 'AGENT_BOTS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
globalConfigFlag: 'csmlEditorHost', globalConfigFlag: 'csmlEditorHost',
toState: frontendURL(`accounts/${accountId}/settings/agent-bots`), toState: frontendURL(`accounts/${accountId}/settings/agent-bots`),
toStateName: 'agent_bots', toStateName: 'agent_bots',
@@ -112,6 +135,9 @@ const settings = accountId => ({
icon: 'flash-settings', icon: 'flash-settings',
label: 'MACROS', label: 'MACROS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator', 'agent'],
},
toState: frontendURL(`accounts/${accountId}/settings/macros`), toState: frontendURL(`accounts/${accountId}/settings/macros`),
toStateName: 'macros_wrapper', toStateName: 'macros_wrapper',
featureFlag: FEATURE_FLAGS.MACROS, featureFlag: FEATURE_FLAGS.MACROS,
@@ -120,6 +146,9 @@ const settings = accountId => ({
icon: 'chat-multiple', icon: 'chat-multiple',
label: 'CANNED_RESPONSES', label: 'CANNED_RESPONSES',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator', 'agent'],
},
toState: frontendURL( toState: frontendURL(
`accounts/${accountId}/settings/canned-response/list` `accounts/${accountId}/settings/canned-response/list`
), ),
@@ -130,6 +159,9 @@ const settings = accountId => ({
icon: 'flash-on', icon: 'flash-on',
label: 'INTEGRATIONS', label: 'INTEGRATIONS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/integrations`), toState: frontendURL(`accounts/${accountId}/settings/integrations`),
toStateName: 'settings_integrations', toStateName: 'settings_integrations',
featureFlag: FEATURE_FLAGS.INTEGRATIONS, featureFlag: FEATURE_FLAGS.INTEGRATIONS,
@@ -138,6 +170,9 @@ const settings = accountId => ({
icon: 'star-emphasis', icon: 'star-emphasis',
label: 'APPLICATIONS', label: 'APPLICATIONS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/applications`), toState: frontendURL(`accounts/${accountId}/settings/applications`),
toStateName: 'settings_applications', toStateName: 'settings_applications',
featureFlag: FEATURE_FLAGS.INTEGRATIONS, featureFlag: FEATURE_FLAGS.INTEGRATIONS,
@@ -146,6 +181,9 @@ const settings = accountId => ({
icon: 'key', icon: 'key',
label: 'AUDIT_LOGS', label: 'AUDIT_LOGS',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`), toState: frontendURL(`accounts/${accountId}/settings/audit-log/list`),
toStateName: 'auditlogs_list', toStateName: 'auditlogs_list',
isEnterpriseOnly: true, isEnterpriseOnly: true,
@@ -156,6 +194,9 @@ const settings = accountId => ({
icon: 'document-list-clock', icon: 'document-list-clock',
label: 'SLA', label: 'SLA',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/sla/list`), toState: frontendURL(`accounts/${accountId}/settings/sla/list`),
toStateName: 'sla_list', toStateName: 'sla_list',
isEnterpriseOnly: true, isEnterpriseOnly: true,
@@ -166,6 +207,9 @@ const settings = accountId => ({
icon: 'credit-card-person', icon: 'credit-card-person',
label: 'BILLING', label: 'BILLING',
hasSubMenu: false, hasSubMenu: false,
meta: {
permissions: ['administrator'],
},
toState: frontendURL(`accounts/${accountId}/settings/billing`), toState: frontendURL(`accounts/${accountId}/settings/billing`),
toStateName: 'billing_settings_index', toStateName: 'billing_settings_index',
showOnlyOnCloud: true, showOnlyOnCloud: true,

View File

@@ -29,6 +29,8 @@ import SecondaryNavItem from './SecondaryNavItem.vue';
import AccountContext from './AccountContext.vue'; import AccountContext from './AccountContext.vue';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { FEATURE_FLAGS } from '../../../featureFlags'; import { FEATURE_FLAGS } from '../../../featureFlags';
import { hasPermissions } from '../../../helper/permissionsHelper';
import { routesWithPermissions } from '../../../routes';
export default { export default {
components: { components: {
@@ -60,9 +62,9 @@ export default {
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
currentRole: { currentUser: {
type: String, type: Object,
default: '', default: () => {},
}, },
isOnChatwootCloud: { isOnChatwootCloud: {
type: Boolean, type: Boolean,
@@ -80,16 +82,16 @@ export default {
return this.customViews.filter(view => view.filter_type === 'contact'); return this.customViews.filter(view => view.filter_type === 'contact');
}, },
accessibleMenuItems() { accessibleMenuItems() {
if (!this.currentRole) { const menuItemsFilteredByPermissions = this.menuConfig.menuItems.filter(
return []; menuItem => {
} const { permissions: userPermissions = [] } = this.currentUser;
const menuItemsFilteredByRole = this.menuConfig.menuItems.filter( return hasPermissions(
menuItem => routesWithPermissions[menuItem.toStateName],
window.roleWiseRoutes[this.currentRole].indexOf( userPermissions
menuItem.toStateName
) > -1
); );
return menuItemsFilteredByRole.filter(item => { }
);
return menuItemsFilteredByPermissions.filter(item => {
if (item.showOnlyOnCloud) { if (item.showOnlyOnCloud) {
return this.isOnChatwootCloud; return this.isOnChatwootCloud;
} }

View File

@@ -65,8 +65,9 @@
:show-child-count="showChildCount(child.count)" :show-child-count="showChildCount(child.count)"
:child-item-count="child.count" :child-item-count="child.count"
/> />
<Policy :permissions="['administrator']">
<router-link <router-link
v-if="showItem(menuItem)" v-if="menuItem.newLink"
v-slot="{ href, navigate }" v-slot="{ href, navigate }"
:to="menuItem.toState" :to="menuItem.toState"
custom custom
@@ -86,6 +87,7 @@
</a> </a>
</li> </li>
</router-link> </router-link>
</Policy>
</ul> </ul>
</li> </li>
</template> </template>
@@ -105,9 +107,10 @@ import {
isOnMentionsView, isOnMentionsView,
isOnUnattendedView, isOnUnattendedView,
} from '../../../store/modules/conversations/helpers/actionHelpers'; } from '../../../store/modules/conversations/helpers/actionHelpers';
import Policy from '../../policy.vue';
export default { export default {
components: { SecondaryChildNavItem }, components: { SecondaryChildNavItem, Policy },
mixins: [adminMixin, configMixin], mixins: [adminMixin, configMixin],
props: { props: {
menuItem: { menuItem: {

View File

@@ -0,0 +1,23 @@
<script setup>
import { useStoreGetters } from 'dashboard/composables/store';
import { computed } from 'vue';
import { hasPermissions } from '../helper/permissionsHelper';
const props = defineProps({
permissions: {
type: Array,
required: true,
},
});
const getters = useStoreGetters();
const user = getters.getCurrentUser.value;
const hasPermission = computed(() =>
hasPermissions(props.permissions, user.permissions)
);
</script>
<template>
<div v-if="hasPermission">
<slot />
</div>
</template>

View File

@@ -0,0 +1,34 @@
export const hasPermissions = (
requiredPermissions = [],
availablePermissions = []
) => {
return requiredPermissions.some(permission =>
availablePermissions.includes(permission)
);
};
const isPermissionsPresentInRoute = route =>
route.meta && route.meta.permissions;
export const buildPermissionsFromRouter = (routes = []) =>
routes.reduce((acc, route) => {
if (route.name) {
if (!isPermissionsPresentInRoute(route)) {
// eslint-disable-next-line
console.error(route);
throw new Error(
"The route doesn't have the required permissions defined"
);
}
acc[route.name] = route.meta.permissions;
}
if (route.children) {
acc = {
...acc,
...buildPermissionsFromRouter(route.children),
};
}
return acc;
}, {});

View File

@@ -1,19 +1,16 @@
import { hasPermissions } from './permissionsHelper';
// eslint-disable-next-line default-param-last // eslint-disable-next-line default-param-last
export const getCurrentAccount = ({ accounts } = {}, accountId) => { export const getCurrentAccount = ({ accounts } = {}, accountId) => {
return accounts.find(account => account.id === accountId); return accounts.find(account => account.id === accountId);
}; };
// eslint-disable-next-line default-param-last export const routeIsAccessibleFor = (route, userPermissions = []) => {
export const getUserRole = ({ accounts } = {}, accountId) => { const { meta: { permissions: routePermissions = [] } = {} } = route;
const currentAccount = getCurrentAccount({ accounts }, accountId) || {}; return hasPermissions(routePermissions, userPermissions);
return currentAccount.role || null;
}; };
export const routeIsAccessibleFor = (route, role, roleWiseRoutes) => { const validateActiveAccountRoutes = (to, user) => {
return roleWiseRoutes[role].includes(route);
};
const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
// If the current account is active, then check for the route permissions // If the current account is active, then check for the route permissions
const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`; const accountDashboardURL = `accounts/${to.params.accountId}/dashboard`;
@@ -22,15 +19,13 @@ const validateActiveAccountRoutes = (to, user, roleWiseRoutes) => {
return accountDashboardURL; return accountDashboardURL;
} }
const userRole = getUserRole(user, Number(to.params.accountId)); const isAccessible = routeIsAccessibleFor(to, user.permissions);
const isAccessible = routeIsAccessibleFor(to.name, userRole, roleWiseRoutes);
// If the route is not accessible for the user, return to dashboard screen // If the route is not accessible for the user, return to dashboard screen
return isAccessible ? null : accountDashboardURL; return isAccessible ? null : accountDashboardURL;
}; };
export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => { export const validateLoggedInRoutes = (to, user) => {
const currentAccount = getCurrentAccount(user, Number(to.params.accountId)); const currentAccount = getCurrentAccount(user, Number(to.params.accountId));
// If current account is missing, either user does not have // If current account is missing, either user does not have
// access to the account or the account is deleted, return to login screen // access to the account or the account is deleted, return to login screen
if (!currentAccount) { if (!currentAccount) {
@@ -40,7 +35,7 @@ export const validateLoggedInRoutes = (to, user, roleWiseRoutes) => {
const isCurrentAccountActive = currentAccount.status === 'active'; const isCurrentAccountActive = currentAccount.status === 'active';
if (isCurrentAccountActive) { if (isCurrentAccountActive) {
return validateActiveAccountRoutes(to, user, roleWiseRoutes); return validateActiveAccountRoutes(to, user);
} }
// If the current account is not active, then redirect the user to the suspended screen // If the current account is not active, then redirect the user to the suspended screen

View File

@@ -0,0 +1,84 @@
import {
buildPermissionsFromRouter,
hasPermissions,
} from '../permissionsHelper';
describe('hasPermissions', () => {
it('returns true if permission is present', () => {
expect(
hasPermissions(['contact_manage'], ['team_manage', 'contact_manage'])
).toBe(true);
});
it('returns true if permission is not present', () => {
expect(
hasPermissions(['contact_manage'], ['team_manage', 'user_manage'])
).toBe(false);
expect(hasPermissions()).toBe(false);
expect(hasPermissions([])).toBe(false);
});
});
describe('buildPermissionsFromRouter', () => {
it('returns a valid object when routes have permissions defined', () => {
expect(
buildPermissionsFromRouter([
{
path: 'agent',
name: 'agent_list',
meta: { permissions: ['agent_admin'] },
},
{
path: 'inbox',
children: [
{
path: '',
name: 'inbox_list',
meta: { permissions: ['inbox_admin'] },
},
],
},
{
path: 'conversations',
children: [
{
path: '',
children: [
{
path: 'attachments',
name: 'attachments_list',
meta: { permissions: ['conversation_admin'] },
},
],
},
],
},
])
).toEqual({
agent_list: ['agent_admin'],
inbox_list: ['inbox_admin'],
attachments_list: ['conversation_admin'],
});
});
it('throws an error if a named routed does not have permissions defined', () => {
expect(() => {
buildPermissionsFromRouter([
{
path: 'agent',
name: 'agent_list',
},
]);
}).toThrow("The route doesn't have the required permissions defined");
expect(() => {
buildPermissionsFromRouter([
{
path: 'agent',
name: 'agent_list',
meta: {},
},
]);
}).toThrow("The route doesn't have the required permissions defined");
});
});

View File

@@ -1,7 +1,6 @@
import { import {
getConversationDashboardRoute, getConversationDashboardRoute,
getCurrentAccount, getCurrentAccount,
getUserRole,
isAConversationRoute, isAConversationRoute,
routeIsAccessibleFor, routeIsAccessibleFor,
validateLoggedInRoutes, validateLoggedInRoutes,
@@ -15,24 +14,11 @@ describe('#getCurrentAccount', () => {
}); });
}); });
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', () => { describe('#routeIsAccessibleFor', () => {
it('should return the correct access', () => { it('should return the correct access', () => {
const roleWiseRoutes = { agent: ['conversations'], admin: ['billing'] }; let route = { meta: { permissions: ['administrator'] } };
expect(routeIsAccessibleFor('billing', 'agent', roleWiseRoutes)).toEqual( expect(routeIsAccessibleFor(route, ['agent'])).toEqual(false);
false expect(routeIsAccessibleFor(route, ['administrator'])).toEqual(true);
);
expect(routeIsAccessibleFor('billing', 'admin', roleWiseRoutes)).toEqual(
true
);
}); });
}); });
@@ -40,11 +26,7 @@ describe('#validateLoggedInRoutes', () => {
describe('when account access is missing', () => { describe('when account access is missing', () => {
it('should return the login route', () => { it('should return the login route', () => {
expect( expect(
validateLoggedInRoutes( validateLoggedInRoutes({ params: { accountId: 1 } }, { accounts: [] })
{ params: { accountId: 1 } },
{ accounts: [] },
{}
)
).toEqual(`app/login`); ).toEqual(`app/login`);
}); });
}); });
@@ -53,9 +35,12 @@ describe('#validateLoggedInRoutes', () => {
it('return suspended route', () => { it('return suspended route', () => {
expect( expect(
validateLoggedInRoutes( validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } }, {
{ accounts: [{ id: 1, role: 'agent', status: 'suspended' }] }, name: 'conversations',
{ agent: ['conversations'] } params: { accountId: 1 },
meta: { permissions: ['agent'] },
},
{ accounts: [{ id: 1, role: 'agent', status: 'suspended' }] }
) )
).toEqual(`accounts/1/suspended`); ).toEqual(`accounts/1/suspended`);
}); });
@@ -65,9 +50,22 @@ describe('#validateLoggedInRoutes', () => {
it('returns null (no action required)', () => { it('returns null (no action required)', () => {
expect( expect(
validateLoggedInRoutes( validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } }, {
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }, name: 'conversations',
{ agent: ['conversations'] } params: { accountId: 1 },
meta: { permissions: ['agent'] },
},
{
permissions: ['agent'],
accounts: [
{
id: 1,
role: 'agent',
permissions: ['agent'],
status: 'active',
},
],
}
) )
).toEqual(null); ).toEqual(null);
}); });
@@ -76,9 +74,12 @@ describe('#validateLoggedInRoutes', () => {
it('returns dashboard url', () => { it('returns dashboard url', () => {
expect( expect(
validateLoggedInRoutes( validateLoggedInRoutes(
{ name: 'conversations', params: { accountId: 1 } }, {
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }, name: 'billing',
{ admin: ['conversations'], agent: [] } params: { accountId: 1 },
meta: { permissions: ['administrator'] },
},
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }
) )
).toEqual(`accounts/1/dashboard`); ).toEqual(`accounts/1/dashboard`);
}); });
@@ -88,8 +89,7 @@ describe('#validateLoggedInRoutes', () => {
expect( expect(
validateLoggedInRoutes( validateLoggedInRoutes(
{ name: 'account_suspended', params: { accountId: 1 } }, { name: 'account_suspended', params: { accountId: 1 } },
{ accounts: [{ id: 1, role: 'agent', status: 'active' }] }, { accounts: [{ id: 1, role: 'agent', status: 'active' }] }
{ agent: ['account_suspended'] }
) )
).toEqual(`accounts/1/dashboard`); ).toEqual(`accounts/1/dashboard`);
}); });

View File

@@ -7,7 +7,9 @@ export const routes = [
{ {
path: frontendURL('accounts/:accountId/search'), path: frontendURL('accounts/:accountId/search'),
name: 'search', name: 'search',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: SearchView, component: SearchView,
}, },
]; ];

View File

@@ -7,13 +7,17 @@ export const routes = [
{ {
path: frontendURL('accounts/:accountId/contacts'), path: frontendURL('accounts/:accountId/contacts'),
name: 'contacts_dashboard', name: 'contacts_dashboard',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ContactsView, component: ContactsView,
}, },
{ {
path: frontendURL('accounts/:accountId/contacts/custom_view/:id'), path: frontendURL('accounts/:accountId/contacts/custom_view/:id'),
name: 'contacts_segments_dashboard', name: 'contacts_segments_dashboard',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ContactsView, component: ContactsView,
props: route => { props: route => {
return { segmentsId: route.params.id }; return { segmentsId: route.params.id };
@@ -22,7 +26,9 @@ export const routes = [
{ {
path: frontendURL('accounts/:accountId/labels/:label/contacts'), path: frontendURL('accounts/:accountId/labels/:label/contacts'),
name: 'contacts_labels_dashboard', name: 'contacts_labels_dashboard',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ContactsView, component: ContactsView,
props: route => { props: route => {
return { label: route.params.label }; return { label: route.params.label };
@@ -31,7 +37,9 @@ export const routes = [
{ {
path: frontendURL('accounts/:accountId/contacts/:contactId'), path: frontendURL('accounts/:accountId/contacts/:contactId'),
name: 'contact_profile_dashboard', name: 'contact_profile_dashboard',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ContactManageView, component: ContactManageView,
props: route => { props: route => {
return { contactId: route.params.contactId }; return { contactId: route.params.contactId };

View File

@@ -7,7 +7,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/dashboard'), path: frontendURL('accounts/:accountId/dashboard'),
name: 'home', name: 'home',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: () => { props: () => {
return { inboxId: 0 }; return { inboxId: 0 };
@@ -16,7 +18,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/conversations/:conversation_id'), path: frontendURL('accounts/:accountId/conversations/:conversation_id'),
name: 'inbox_conversation', name: 'inbox_conversation',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => { props: route => {
return { inboxId: 0, conversationId: route.params.conversation_id }; return { inboxId: 0, conversationId: route.params.conversation_id };
@@ -25,7 +29,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/inbox/:inbox_id'), path: frontendURL('accounts/:accountId/inbox/:inbox_id'),
name: 'inbox_dashboard', name: 'inbox_dashboard',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => { props: route => {
return { inboxId: route.params.inbox_id }; return { inboxId: route.params.inbox_id };
@@ -36,7 +42,9 @@ export default {
'accounts/:accountId/inbox/:inbox_id/conversations/:conversation_id' 'accounts/:accountId/inbox/:inbox_id/conversations/:conversation_id'
), ),
name: 'conversation_through_inbox', name: 'conversation_through_inbox',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => { props: route => {
return { return {
@@ -48,7 +56,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/label/:label'), path: frontendURL('accounts/:accountId/label/:label'),
name: 'label_conversations', name: 'label_conversations',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ label: route.params.label }), props: route => ({ label: route.params.label }),
}, },
@@ -57,7 +67,9 @@ export default {
'accounts/:accountId/label/:label/conversations/:conversation_id' 'accounts/:accountId/label/:label/conversations/:conversation_id'
), ),
name: 'conversations_through_label', name: 'conversations_through_label',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversation_id, conversationId: route.params.conversation_id,
@@ -67,7 +79,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/team/:teamId'), path: frontendURL('accounts/:accountId/team/:teamId'),
name: 'team_conversations', name: 'team_conversations',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ teamId: route.params.teamId }), props: route => ({ teamId: route.params.teamId }),
}, },
@@ -76,7 +90,9 @@ export default {
'accounts/:accountId/team/:teamId/conversations/:conversationId' 'accounts/:accountId/team/:teamId/conversations/:conversationId'
), ),
name: 'conversations_through_team', name: 'conversations_through_team',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversationId, conversationId: route.params.conversationId,
@@ -86,7 +102,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/custom_view/:id'), path: frontendURL('accounts/:accountId/custom_view/:id'),
name: 'folder_conversations', name: 'folder_conversations',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ foldersId: route.params.id }), props: route => ({ foldersId: route.params.id }),
}, },
@@ -95,7 +113,9 @@ export default {
'accounts/:accountId/custom_view/:id/conversations/:conversation_id' 'accounts/:accountId/custom_view/:id/conversations/:conversation_id'
), ),
name: 'conversations_through_folders', name: 'conversations_through_folders',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversation_id, conversationId: route.params.conversation_id,
@@ -105,7 +125,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/mentions/conversations'), path: frontendURL('accounts/:accountId/mentions/conversations'),
name: 'conversation_mentions', name: 'conversation_mentions',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: () => ({ conversationType: 'mention' }), props: () => ({ conversationType: 'mention' }),
}, },
@@ -114,7 +136,9 @@ export default {
'accounts/:accountId/mentions/conversations/:conversationId' 'accounts/:accountId/mentions/conversations/:conversationId'
), ),
name: 'conversation_through_mentions', name: 'conversation_through_mentions',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversationId, conversationId: route.params.conversationId,
@@ -124,7 +148,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/unattended/conversations'), path: frontendURL('accounts/:accountId/unattended/conversations'),
name: 'conversation_unattended', name: 'conversation_unattended',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: () => ({ conversationType: 'unattended' }), props: () => ({ conversationType: 'unattended' }),
}, },
@@ -133,7 +159,9 @@ export default {
'accounts/:accountId/unattended/conversations/:conversationId' 'accounts/:accountId/unattended/conversations/:conversationId'
), ),
name: 'conversation_through_unattended', name: 'conversation_through_unattended',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversationId, conversationId: route.params.conversationId,
@@ -143,7 +171,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/participating/conversations'), path: frontendURL('accounts/:accountId/participating/conversations'),
name: 'conversation_participating', name: 'conversation_participating',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: () => ({ conversationType: 'participating' }), props: () => ({ conversationType: 'participating' }),
}, },
@@ -152,7 +182,9 @@ export default {
'accounts/:accountId/participating/conversations/:conversationId' 'accounts/:accountId/participating/conversations/:conversationId'
), ),
name: 'conversation_through_participating', name: 'conversation_through_participating',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ConversationView, component: ConversationView,
props: route => ({ props: route => ({
conversationId: route.params.conversationId, conversationId: route.params.conversationId,

View File

@@ -28,7 +28,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/suspended'), path: frontendURL('accounts/:accountId/suspended'),
name: 'account_suspended', name: 'account_suspended',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: Suspended, component: Suspended,
}, },
], ],

View File

@@ -30,13 +30,17 @@ const portalRoutes = [
{ {
path: getPortalRoute(''), path: getPortalRoute(''),
name: 'default_portal_articles', name: 'default_portal_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator'],
},
component: DefaultPortalArticles, component: DefaultPortalArticles,
}, },
{ {
path: getPortalRoute('all'), path: getPortalRoute('all'),
name: 'list_all_portals', name: 'list_all_portals',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllPortals, component: ListAllPortals,
}, },
{ {
@@ -47,55 +51,73 @@ const portalRoutes = [
path: '', path: '',
name: 'new_portal_information', name: 'new_portal_information',
component: PortalDetails, component: PortalDetails,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':portalSlug/customization', path: ':portalSlug/customization',
name: 'portal_customization', name: 'portal_customization',
component: PortalCustomization, component: PortalCustomization,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':portalSlug/finish', path: ':portalSlug/finish',
name: 'portal_finish', name: 'portal_finish',
component: PortalSettingsFinish, component: PortalSettingsFinish,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },
{ {
path: getPortalRoute(':portalSlug'), path: getPortalRoute(':portalSlug'),
name: 'portalSlug', name: 'portalSlug',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ShowPortal, component: ShowPortal,
}, },
{ {
path: getPortalRoute(':portalSlug/edit'), path: getPortalRoute(':portalSlug/edit'),
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: EditPortal, component: EditPortal,
children: [ children: [
{ {
path: '', path: '',
name: 'edit_portal_information', name: 'edit_portal_information',
component: EditPortalBasic, component: EditPortalBasic,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'customizations', path: 'customizations',
name: 'edit_portal_customization', name: 'edit_portal_customization',
component: EditPortalCustomization, component: EditPortalCustomization,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'locales', path: 'locales',
name: 'edit_portal_locales', name: 'edit_portal_locales',
component: EditPortalLocales, component: EditPortalLocales,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'categories', path: 'categories',
name: 'list_all_locale_categories', name: 'list_all_locale_categories',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllCategories, component: ListAllCategories,
}, },
], ],
@@ -106,39 +128,51 @@ const articleRoutes = [
{ {
path: getPortalRoute(':portalSlug/:locale/articles'), path: getPortalRoute(':portalSlug/:locale/articles'),
name: 'list_all_locale_articles', name: 'list_all_locale_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllArticles, component: ListAllArticles,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/articles/new'), path: getPortalRoute(':portalSlug/:locale/articles/new'),
name: 'new_article', name: 'new_article',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: NewArticle, component: NewArticle,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/articles/mine'), path: getPortalRoute(':portalSlug/:locale/articles/mine'),
name: 'list_mine_articles', name: 'list_mine_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllArticles, component: ListAllArticles,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/articles/archived'), path: getPortalRoute(':portalSlug/:locale/articles/archived'),
name: 'list_archived_articles', name: 'list_archived_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllArticles, component: ListAllArticles,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/articles/draft'), path: getPortalRoute(':portalSlug/:locale/articles/draft'),
name: 'list_draft_articles', name: 'list_draft_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllArticles, component: ListAllArticles,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/articles/:articleSlug'), path: getPortalRoute(':portalSlug/:locale/articles/:articleSlug'),
name: 'edit_article', name: 'edit_article',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: EditArticle, component: EditArticle,
}, },
]; ];
@@ -147,19 +181,25 @@ const categoryRoutes = [
{ {
path: getPortalRoute(':portalSlug/:locale/categories'), path: getPortalRoute(':portalSlug/:locale/categories'),
name: 'all_locale_categories', name: 'all_locale_categories',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllCategories, component: ListAllCategories,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/categories/new'), path: getPortalRoute(':portalSlug/:locale/categories/new'),
name: 'new_category_in_locale', name: 'new_category_in_locale',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: NewCategory, component: NewCategory,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'), path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
name: 'show_category', name: 'show_category',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListAllArticles, component: ListAllArticles,
}, },
{ {
@@ -167,13 +207,17 @@ const categoryRoutes = [
':portalSlug/:locale/categories/:categorySlug/articles' ':portalSlug/:locale/categories/:categorySlug/articles'
), ),
name: 'show_category_articles', name: 'show_category_articles',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: ListCategoryArticles, component: ListCategoryArticles,
}, },
{ {
path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'), path: getPortalRoute(':portalSlug/:locale/categories/:categorySlug'),
name: 'edit_category', name: 'edit_category',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: EditCategory, component: EditCategory,
}, },
]; ];

View File

@@ -12,13 +12,17 @@ export const routes = [
path: '', path: '',
name: 'inbox_view', name: 'inbox_view',
component: InboxEmptyStateView, component: InboxEmptyStateView,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
{ {
path: ':notification_id', path: ':notification_id',
name: 'inbox_view_conversation', name: 'inbox_view_conversation',
component: InboxDetailView, component: InboxDetailView,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
], ],
}, },

View File

@@ -18,7 +18,9 @@ export const routes = [
path: '', path: '',
name: 'notifications_index', name: 'notifications_index',
component: NotificationsView, component: NotificationsView,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
], ],
}, },

View File

@@ -6,7 +6,9 @@ export default {
routes: [ routes: [
{ {
path: frontendURL('accounts/:accountId/settings/general'), path: frontendURL('accounts/:accountId/settings/general'),
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: SettingsContent, component: SettingsContent,
props: { props: {
headerTitle: 'GENERAL_SETTINGS.TITLE', headerTitle: 'GENERAL_SETTINGS.TITLE',
@@ -18,7 +20,9 @@ export default {
path: '', path: '',
name: 'general_settings_index', name: 'general_settings_index',
component: Index, component: Index,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -8,7 +8,9 @@ export default {
routes: [ routes: [
{ {
path: frontendURL('accounts/:accountId/settings/agent-bots'), path: frontendURL('accounts/:accountId/settings/agent-bots'),
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: SettingsContent, component: SettingsContent,
props: { props: {
headerTitle: 'AGENT_BOTS.HEADER', headerTitle: 'AGENT_BOTS.HEADER',
@@ -20,19 +22,25 @@ export default {
path: '', path: '',
name: 'agent_bots', name: 'agent_bots',
component: Bot, component: Bot,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'csml/new', path: 'csml/new',
name: 'agent_bots_csml_new', name: 'agent_bots_csml_new',
component: CsmlNewBot, component: CsmlNewBot,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'csml/:botId', path: 'csml/:botId',
name: 'agent_bots_csml_edit', name: 'agent_bots_csml_edit',
component: CsmlEditBot, component: CsmlEditBot,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -15,14 +15,15 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'agents_wrapper',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'agent_list', name: 'agent_list',
component: AgentHome, component: AgentHome,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -15,14 +15,15 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'attributes_wrapper',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'attributes_list', name: 'attributes_list',
component: AttributesHome, component: AttributesHome,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -16,13 +16,14 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'auditlogs_wrapper',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'auditlogs_list', name: 'auditlogs_list',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: AuditLogsHome, component: AuditLogsHome,
}, },
], ],

View File

@@ -15,14 +15,15 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'automation_wrapper',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'automation_list', name: 'automation_list',
component: Automation, component: Automation,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -6,7 +6,9 @@ export default {
routes: [ routes: [
{ {
path: frontendURL('accounts/:accountId/settings/billing'), path: frontendURL('accounts/:accountId/settings/billing'),
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: SettingsContent, component: SettingsContent,
props: { props: {
headerTitle: 'BILLING_SETTINGS.TITLE', headerTitle: 'BILLING_SETTINGS.TITLE',
@@ -18,7 +20,9 @@ export default {
path: '', path: '',
name: 'billing_settings_index', name: 'billing_settings_index',
component: Index, component: Index,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -19,7 +19,9 @@ export default {
{ {
path: 'ongoing', path: 'ongoing',
name: 'ongoing_campaigns', name: 'ongoing_campaigns',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: Index, component: Index,
}, },
], ],
@@ -35,7 +37,9 @@ export default {
{ {
path: 'one_off', path: 'one_off',
name: 'one_off', name: 'one_off',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: Index, component: Index,
}, },
], ],

View File

@@ -16,13 +16,14 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'canned_wrapper',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'canned_list', name: 'canned_list',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: CannedHome, component: CannedHome,
}, },
], ],

View File

@@ -28,14 +28,15 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'settings_inbox',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'settings_inbox_list', name: 'settings_inbox_list',
component: InboxHome, component: InboxHome,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'new', path: 'new',
@@ -45,19 +46,25 @@ export default {
path: '', path: '',
name: 'settings_inbox_new', name: 'settings_inbox_new',
component: ChannelList, component: ChannelList,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':inbox_id/finish', path: ':inbox_id/finish',
name: 'settings_inbox_finish', name: 'settings_inbox_finish',
component: FinishSetup, component: FinishSetup,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':sub_page', path: ':sub_page',
name: 'settings_inboxes_page_channel', name: 'settings_inboxes_page_channel',
component: channelFactory.create(), component: channelFactory.create(),
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
props: route => { props: route => {
return { channel_name: route.params.sub_page }; return { channel_name: route.params.sub_page };
}, },
@@ -65,7 +72,9 @@ export default {
{ {
path: ':inbox_id/agents', path: ':inbox_id/agents',
name: 'settings_inboxes_add_agents', name: 'settings_inboxes_add_agents',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: AddAgents, component: AddAgents,
}, },
], ],
@@ -74,7 +83,9 @@ export default {
path: ':inboxId', path: ':inboxId',
name: 'settings_inbox_show', name: 'settings_inbox_show',
component: Settings, component: Settings,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
], ],
}, },

View File

@@ -26,13 +26,17 @@ export default {
path: '', path: '',
name: 'settings_applications', name: 'settings_applications',
component: Index, component: Index,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':integration_id', path: ':integration_id',
name: 'settings_applications_integration', name: 'settings_applications_integration',
component: IntegrationHooks, component: IntegrationHooks,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
props: route => ({ props: route => ({
integrationId: route.params.integration_id, integrationId: route.params.integration_id,
}), }),

View File

@@ -30,32 +30,42 @@ export default {
path: '', path: '',
name: 'settings_integrations', name: 'settings_integrations',
component: Index, component: Index,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'webhook', path: 'webhook',
component: Webhook, component: Webhook,
name: 'settings_integrations_webhook', name: 'settings_integrations_webhook',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'dashboard-apps', path: 'dashboard-apps',
component: DashboardApps, component: DashboardApps,
name: 'settings_integrations_dashboard_apps', name: 'settings_integrations_dashboard_apps',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'slack', path: 'slack',
name: 'settings_integrations_slack', name: 'settings_integrations_slack',
component: Slack, component: Slack,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
props: route => ({ code: route.query.code }), props: route => ({ code: route.query.code }),
}, },
{ {
path: ':integration_id', path: ':integration_id',
name: 'settings_integrations_integration', name: 'settings_integrations_integration',
component: ShowIntegration, component: ShowIntegration,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
props: route => { props: route => {
return { return {
integrationId: route.params.integration_id, integrationId: route.params.integration_id,

View File

@@ -17,13 +17,17 @@ export default {
{ {
path: '', path: '',
name: 'labels_wrapper', name: 'labels_wrapper',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'labels_list', name: 'labels_list',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: Index, component: Index,
}, },
], ],

View File

@@ -23,19 +23,25 @@ export default {
path: '', path: '',
name: 'macros_wrapper', name: 'macros_wrapper',
component: Macros, component: Macros,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
{ {
path: 'new', path: 'new',
name: 'macros_new', name: 'macros_new',
component: MacroEditor, component: MacroEditor,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
{ {
path: ':macroId/edit', path: ':macroId/edit',
name: 'macros_edit', name: 'macros_edit',
component: MacroEditor, component: MacroEditor,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
], ],
}, },

View File

@@ -8,14 +8,18 @@ export default {
{ {
path: frontendURL('accounts/:accountId/profile'), path: frontendURL('accounts/:accountId/profile'),
name: 'profile_settings', name: 'profile_settings',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
component: SettingsContent, component: SettingsContent,
children: [ children: [
{ {
path: 'settings', path: 'settings',
name: 'profile_settings_index', name: 'profile_settings_index',
component: Index, component: Index,
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
}, },
], ],
}, },

View File

@@ -29,7 +29,9 @@ export default {
{ {
path: 'overview', path: 'overview',
name: 'account_overview_reports', name: 'account_overview_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: LiveReports, component: LiveReports,
}, },
], ],
@@ -46,7 +48,9 @@ export default {
{ {
path: 'conversation', path: 'conversation',
name: 'conversation_reports', name: 'conversation_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: Index, component: Index,
}, },
], ],
@@ -63,7 +67,9 @@ export default {
{ {
path: 'csat', path: 'csat',
name: 'csat_reports', name: 'csat_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: CsatResponses, component: CsatResponses,
}, },
], ],
@@ -80,7 +86,9 @@ export default {
{ {
path: 'bot', path: 'bot',
name: 'bot_reports', name: 'bot_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: BotReports, component: BotReports,
}, },
], ],
@@ -97,7 +105,9 @@ export default {
{ {
path: 'agent', path: 'agent',
name: 'agent_reports', name: 'agent_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: AgentReports, component: AgentReports,
}, },
], ],
@@ -114,7 +124,9 @@ export default {
{ {
path: 'label', path: 'label',
name: 'label_reports', name: 'label_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: LabelReports, component: LabelReports,
}, },
], ],
@@ -131,7 +143,9 @@ export default {
{ {
path: 'inboxes', path: 'inboxes',
name: 'inbox_reports', name: 'inbox_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: InboxReports, component: InboxReports,
}, },
], ],
@@ -147,7 +161,9 @@ export default {
{ {
path: 'teams', path: 'teams',
name: 'team_reports', name: 'team_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: TeamReports, component: TeamReports,
}, },
], ],
@@ -164,7 +180,9 @@ export default {
{ {
path: 'sla', path: 'sla',
name: 'sla_reports', name: 'sla_reports',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: SLAReports, component: SLAReports,
}, },
], ],

View File

@@ -24,7 +24,9 @@ export default {
{ {
path: frontendURL('accounts/:accountId/settings'), path: frontendURL('accounts/:accountId/settings'),
name: 'settings_home', name: 'settings_home',
roles: ['administrator', 'agent'], meta: {
permissions: ['administrator', 'agent'],
},
redirect: () => { redirect: () => {
if (store.getters.getCurrentRole === 'administrator') { if (store.getters.getCurrentRole === 'administrator') {
return frontendURL('accounts/:accountId/settings/general'); return frontendURL('accounts/:accountId/settings/general');

View File

@@ -13,13 +13,17 @@ export default {
{ {
path: '', path: '',
name: 'sla_wrapper', name: 'sla_wrapper',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'sla_list', name: 'sla_list',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: Index, component: Index,
}, },
], ],

View File

@@ -29,14 +29,15 @@ export default {
children: [ children: [
{ {
path: '', path: '',
name: 'settings_teams',
redirect: 'list', redirect: 'list',
}, },
{ {
path: 'list', path: 'list',
name: 'settings_teams_list', name: 'settings_teams_list',
component: TeamsHome, component: TeamsHome,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'new', path: 'new',
@@ -46,18 +47,24 @@ export default {
path: '', path: '',
name: 'settings_teams_new', name: 'settings_teams_new',
component: CreateTeam, component: CreateTeam,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':teamId/finish', path: ':teamId/finish',
name: 'settings_teams_finish', name: 'settings_teams_finish',
component: FinishSetup, component: FinishSetup,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: ':teamId/agents', path: ':teamId/agents',
name: 'settings_teams_add_agents', name: 'settings_teams_add_agents',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: AddAgents, component: AddAgents,
}, },
], ],
@@ -70,18 +77,24 @@ export default {
path: '', path: '',
name: 'settings_teams_edit', name: 'settings_teams_edit',
component: EditTeam, component: EditTeam,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'agents', path: 'agents',
name: 'settings_teams_edit_members', name: 'settings_teams_edit_members',
component: EditAgents, component: EditAgents,
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
}, },
{ {
path: 'finish', path: 'finish',
name: 'settings_teams_edit_finish', name: 'settings_teams_edit_finish',
roles: ['administrator'], meta: {
permissions: ['administrator'],
},
component: FinishSetup, component: FinishSetup,
}, },
], ],

View File

@@ -5,33 +5,12 @@ import dashboard from './dashboard/dashboard.routes';
import store from '../store'; import store from '../store';
import { validateLoggedInRoutes } from '../helper/routeHelpers'; import { validateLoggedInRoutes } from '../helper/routeHelpers';
import AnalyticsHelper from '../helper/AnalyticsHelper'; import AnalyticsHelper from '../helper/AnalyticsHelper';
import { buildPermissionsFromRouter } from '../helper/permissionsHelper';
const routes = [...dashboard.routes]; const routes = [...dashboard.routes];
window.roleWiseRoutes = {
agent: [],
administrator: [],
};
// generateRoleWiseRoute - updates window object with agent/admin route
const generateRoleWiseRoute = route => {
route.forEach(element => {
if (element.children) {
generateRoleWiseRoute(element.children);
}
if (element.roles) {
element.roles.forEach(roleEl => {
window.roleWiseRoutes[roleEl].push(element.name);
});
}
});
};
// Create a object of routes
// accessible by each role.
// returns an object with roles as keys and routeArr as values
generateRoleWiseRoute(routes);
export const router = new VueRouter({ mode: 'history', routes }); export const router = new VueRouter({ mode: 'history', routes });
export const routesWithPermissions = buildPermissionsFromRouter(routes);
export const validateAuthenticateRoutePermission = (to, next, { getters }) => { export const validateAuthenticateRoutePermission = (to, next, { getters }) => {
const { isLoggedIn, getCurrentUser: user } = getters; const { isLoggedIn, getCurrentUser: user } = getters;
@@ -45,11 +24,7 @@ export const validateAuthenticateRoutePermission = (to, next, { getters }) => {
return next(frontendURL(`accounts/${user.account_id}/dashboard`)); return next(frontendURL(`accounts/${user.account_id}/dashboard`));
} }
const nextRoute = validateLoggedInRoutes( const nextRoute = validateLoggedInRoutes(to, getters.getCurrentUser);
to,
getters.getCurrentUser,
window.roleWiseRoutes
);
return nextRoute ? next(frontendURL(nextRoute)) : next(); return nextRoute ? next(frontendURL(nextRoute)) : next();
}; };