MT#60623 Hide features not covered by the license

Following the changes about the licenses, some API will now
return a "403 - License not available" error when the needed
license is not available to the user. In the frontend we handle
the licenses with two levels of protection:
1) We hide menus of missing licenses from the sidebar
2) We add a guard in boot/routes.js that would redirect to the
homepage any attempts to access the mentioned menus, in case
they are mistakenly shown.

Change-Id: I9e88473ee90935db9b2a234ff03aef1b3a44a97b
mr13.0
Debora Crescenzo 1 year ago
parent 7c9a494897
commit 2d1ec7c499

@ -34,7 +34,14 @@ export default ({ app, router, store }) => {
break break
default: default:
if (to.meta?.profileAttribute) { if (to.meta?.profileAttribute) {
store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute) ? next() : next('/') const hasSubscriberProfileAttribute = store.getters['user/hasSubscriberProfileAttribute'](to.meta.profileAttribute)
if (to.meta.license && hasSubscriberProfileAttribute) {
// Guard to assure that users cannot click on menu if
// it is mistakenly visible when the license is inactive
store.getters['user/isLicenseActive'](to.meta.profileAttribute) ? next() : next('/')
}
hasSubscriberProfileAttribute ? next() : next('/')
} else if (to.meta?.profileAttributes) { } else if (to.meta?.profileAttributes) {
store.getters['user/hasSubscriberProfileAttributes'](to.meta.profileAttributes) ? next() : next('/') store.getters['user/hasSubscriberProfileAttributes'](to.meta.profileAttributes) ? next() : next('/')
} else { } else {

@ -5,11 +5,9 @@
</template> </template>
<script> <script>
import { import { mapGetters } from 'vuex'
mapGetters
} from 'vuex'
import CscMainMenu from 'components/CscMainMenu' import CscMainMenu from 'components/CscMainMenu'
import { PROFILE_ATTRIBUTE_MAP, PROFILE_ATTRIBUTES_MAP } from 'src/constants' import { LICENSES, PROFILE_ATTRIBUTE_MAP, PROFILE_ATTRIBUTES_MAP } from 'src/constants'
export default { export default {
name: 'CscMainMenuTop', name: 'CscMainMenuTop',
@ -47,12 +45,13 @@ export default {
}, },
computed: { computed: {
...mapGetters('user', [ ...mapGetters('user', [
'isPbxEnabled', 'getCustomerId',
'hasFaxCapability', 'hasFaxCapability',
'hasSubscriberProfileAttribute', 'hasSubscriberProfileAttribute',
'hasSubscriberProfileAttributes', 'hasSubscriberProfileAttributes',
'getCustomerId', 'isLicenseActive',
'isOldCSCProxyingAllowed' 'isOldCSCProxyingAllowed',
'isPbxEnabled'
]), ]),
items () { items () {
return [ return [
@ -67,7 +66,7 @@ export default {
icon: 'call', icon: 'call',
label: this.callStateTitle, label: this.callStateTitle,
sublabel: this.callStateSubtitle, sublabel: this.callStateSubtitle,
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cscCalls) visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.cscCalls) && this.isLicenseActive(LICENSES.csc_calls)
}, },
{ {
to: '/user/conversations', to: '/user/conversations',
@ -80,7 +79,7 @@ export default {
to: '/user/subscriber-phonebook', to: '/user/subscriber-phonebook',
icon: 'fas fa-user', icon: 'fas fa-user',
label: this.$t('Subscriber Phonebook'), label: this.$t('Subscriber Phonebook'),
visible: true visible: this.isLicenseActive(LICENSES.phonebook)
}, },
{ {
icon: 'settings_phone', icon: 'settings_phone',
@ -139,7 +138,7 @@ export default {
to: '/user/recordings', to: '/user/recordings',
icon: 'play_circle', icon: 'play_circle',
label: this.$t('Recordings'), label: this.$t('Recordings'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.recordings) visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.recordings) && this.isLicenseActive(LICENSES.call_recording)
} }
] ]
}, },
@ -147,12 +146,15 @@ export default {
to: '/user/fax-settings', to: '/user/fax-settings',
icon: 'fas fa-fax', icon: 'fas fa-fax',
label: this.$t('Fax Settings'), label: this.$t('Fax Settings'),
visible: this.hasFaxCapability && this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.faxServer) visible: this.hasFaxCapability &&
this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.faxServer) &&
this.isLicenseActive(LICENSES.fax)
}, },
{ {
icon: 'fas fa-chart-line', icon: 'fas fa-chart-line',
label: this.$t('PBX Statistics'), label: this.$t('PBX Statistics'),
visible: this.isPbxAdmin, visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx),
opened: this.isPbxConfiguration, opened: this.isPbxConfiguration,
children: [ children: [
{ {
@ -166,7 +168,7 @@ export default {
{ {
icon: 'miscellaneous_services', icon: 'miscellaneous_services',
label: this.$t('PBX Configuration'), label: this.$t('PBX Configuration'),
visible: this.isPbxAdmin, visible: this.isPbxAdmin && this.isLicenseActive(LICENSES.pbx),
opened: this.isPbxConfiguration, opened: this.isPbxConfiguration,
children: [ children: [
{ {
@ -185,7 +187,7 @@ export default {
to: '/user/pbx-configuration/devices', to: '/user/pbx-configuration/devices',
icon: 'fas fa-fax', icon: 'fas fa-fax',
label: this.$t('Devices'), label: this.$t('Devices'),
visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.deviceProvisioning) visible: this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.deviceProvisioning) && this.isLicenseActive(LICENSES.device_provisioning)
}, },
{ {
to: '/user/pbx-configuration/call-queues', to: '/user/pbx-configuration/call-queues',
@ -215,7 +217,7 @@ export default {
to: '/user/pbx-configuration/customer-phonebook', to: '/user/pbx-configuration/customer-phonebook',
icon: 'person', icon: 'person',
label: this.$t('Customer Phonebook'), label: this.$t('Customer Phonebook'),
visible: true visible: this.isLicenseActive(LICENSES.phonebook)
}, },
{ {
to: '/user/pbx-configuration/customer-preferences', to: '/user/pbx-configuration/customer-preferences',
@ -228,7 +230,9 @@ export default {
{ {
icon: 'settings', icon: 'settings',
label: this.$t('Extension Settings'), label: this.$t('Extension Settings'),
visible: this.isPbxEnabled && this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings), visible: this.isPbxEnabled &&
this.hasSubscriberProfileAttributes(PROFILE_ATTRIBUTES_MAP.pbxSettings) &&
this.isLicenseActive(LICENSES.pbx),
children: [ children: [
{ {
to: '/user/extension-settings/call-queues', to: '/user/extension-settings/call-queues',

@ -32,3 +32,12 @@ export const PROFILE_ATTRIBUTES_MAP = {
pbxSettings: ['auto_attendant', 'cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time', 'manager_secretary'], pbxSettings: ['auto_attendant', 'cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time', 'manager_secretary'],
pbxSettingsCallQueue: ['cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time'] pbxSettingsCallQueue: ['cloud_pbx_callqueue', 'max_queue_length', 'queue_wrap_up_time']
} }
export const LICENSES = {
csc_calls: 'csc_calls',
device_provisioning: 'device_provisioning',
fax: 'fax',
pbx: 'pbx',
phonebook: 'phonebook',
call_recording: 'call_recording'
}

@ -123,6 +123,7 @@ import CscConversationsFilter from 'components/pages/Conversations/CscConversati
import CscConversationsCallsFilter from 'components/pages/Conversations/CscConversationsCallsFilter' import CscConversationsCallsFilter from 'components/pages/Conversations/CscConversationsCallsFilter'
import CscRemoveDialog from 'components/CscRemoveDialog' import CscRemoveDialog from 'components/CscRemoveDialog'
import { mapWaitingActions } from 'vue-wait' import { mapWaitingActions } from 'vue-wait'
import { LICENSES } from 'src/constants'
export default { export default {
name: 'CscPageConversations', name: 'CscPageConversations',
components: { components: {
@ -146,6 +147,20 @@ export default {
} }
}, },
computed: { computed: {
...mapGetters('user', [
'isLicenseActive'
]),
...mapState('conversations', [
'reachedLastPage'
]),
...mapGetters('conversations', [
'items',
'isNumberIncomingBlocked',
'isNumberOutgoingBlocked'
]),
...mapGetters('call', [
'isCallEnabled'
]),
tabs () { tabs () {
return [ return [
{ {
@ -163,24 +178,15 @@ export default {
value: 'voicemail', value: 'voicemail',
icon: 'voicemail' icon: 'voicemail'
}, },
{ this.isLicenseActive(LICENSES.fax)
label: this.$t('Faxes'), ? {
value: 'fax', label: this.$t('Faxes'),
icon: 'description' value: 'fax',
} icon: 'description'
] }
: null
].filter((label) => label !== null)
}, },
...mapState('conversations', [
'reachedLastPage'
]),
...mapGetters('conversations', [
'items',
'isNumberIncomingBlocked',
'isNumberOutgoingBlocked'
]),
...mapGetters('call', [
'isCallEnabled'
]),
pageStyle () { pageStyle () {
return { return {
paddingTop: this.topMargin + 'px' paddingTop: this.topMargin + 'px'

@ -4,7 +4,7 @@
class="row justify-center" class="row justify-center"
> >
<csc-card-dashboard <csc-card-dashboard
v-if="showConvList" v-if="showConversationsCard"
:title="$t('Voicebox Messages')" :title="$t('Voicebox Messages')"
:count="voicemailsCount" :count="voicemailsCount"
:count-title="$t('Messages')" :count-title="$t('Messages')"
@ -18,7 +18,7 @@
@action="downloadVoicemail" @action="downloadVoicemail"
/> />
<csc-card-dashboard <csc-card-dashboard
v-if="showConvList" v-if="showConversationsCard"
:title="$t('Call List')" :title="$t('Call List')"
:count="callsCount" :count="callsCount"
:count-title="$t('Calls')" :count-title="$t('Calls')"
@ -100,7 +100,7 @@ export default {
...mapGetters('user', [ ...mapGetters('user', [
'hasSubscriberProfileAttribute' 'hasSubscriberProfileAttribute'
]), ]),
showConvList () { showConversationsCard () {
return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.conversations) return this.hasSubscriberProfileAttribute(PROFILE_ATTRIBUTE_MAP.conversations)
}, },
showRegDevices () { showRegDevices () {

@ -1,4 +1,4 @@
import { PROFILE_ATTRIBUTE_MAP, PROFILE_ATTRIBUTES_MAP } from 'src/constants' import { LICENSES, PROFILE_ATTRIBUTE_MAP, PROFILE_ATTRIBUTES_MAP } from 'src/constants'
import CscLayoutLogin from 'src/layouts/CscLayoutLogin' import CscLayoutLogin from 'src/layouts/CscLayoutLogin'
import CscLayoutMain from 'src/layouts/CscLayoutMain' import CscLayoutMain from 'src/layouts/CscLayoutMain'
@ -99,7 +99,8 @@ const routes = [
meta: { meta: {
get title () { get title () {
return i18n.global.tc('Subscriber Phonebook') return i18n.global.tc('Subscriber Phonebook')
} },
license: LICENSES.phonebook
} }
}, },
{ {
@ -217,7 +218,8 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.tc('CDR') return i18n.global.tc('CDR')
} }
} },
license: LICENSES.pbx
}, },
{ {
path: 'pbx-configuration/groups', path: 'pbx-configuration/groups',
@ -431,7 +433,8 @@ const routes = [
get subtitle () { get subtitle () {
return i18n.global.tc('Set your fax settings') return i18n.global.tc('Set your fax settings')
}, },
profileAttribute: PROFILE_ATTRIBUTE_MAP.faxServer profileAttribute: PROFILE_ATTRIBUTE_MAP.faxServer,
license: LICENSES.phonebook
} }
}, },
{ {

@ -195,6 +195,9 @@ export default {
isOldCSCProxyingAllowed (state, getters) { isOldCSCProxyingAllowed (state, getters) {
return getters.isAdmin && state.platformInfo?.csc_v2_mode === 'mixed' && !!getters.getCustomerId return getters.isAdmin && state.platformInfo?.csc_v2_mode === 'mixed' && !!getters.getCustomerId
}, },
isLicenseActive: (state) => (license) => {
return state?.platformInfo.licenses.includes(license)
},
isPbxPilot (state) { isPbxPilot (state) {
return !!state.subscriber?.is_pbx_pilot return !!state.subscriber?.is_pbx_pilot
}, },

Loading…
Cancel
Save