From 25d85c35de1990d341fccbb443bbee4aeae7aaf1 Mon Sep 17 00:00:00 2001 From: Varsha Kumari Date: Tue, 2 Jun 2026 10:26:36 +0530 Subject: [PATCH 1/4] added option to configure role for dashboard --- Changelog.md | 10 +- package.json | 2 +- src/components/settings/UserProfile.vue | 4 + src/components/teams/AdminTeams.vue | 174 +++++++++++++++++++++--- src/config.js | 10 ++ 5 files changed, 180 insertions(+), 20 deletions(-) diff --git a/Changelog.md b/Changelog.md index c7ad834..a4b1f59 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,14 +1,18 @@ # Changelog ## [Unreleased] +## [3.9.2] - 2026-06-02 -## [3.9.0] - 2026-06-29 +### Added +- Implemetned new role for dashboard access permission + +## [3.9.0] - 2026-05-29 ### Added - Implemetned new design for role permission - Implemented tenant access list -## [3.8.0] - 2026-06-29 +## [3.8.0] - 2026-05-29 ### Fixed - Handled permission denied issue properly @@ -19,7 +23,7 @@ - Implemented remove authenticator - Implemented categorized role -## [3.7.32] - 2026-06-20 +## [3.7.32] - 2026-05-20 ### Fixed diff --git a/package.json b/package.json index d978f08..1f3ae8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "entity-developer-dashboard", - "version": "3.9.1", + "version": "3.10.0", "private": true, "scripts": { "serve": "vue-cli-service serve --mode production", diff --git a/src/components/settings/UserProfile.vue b/src/components/settings/UserProfile.vue index 8712f29..a2f469d 100644 --- a/src/components/settings/UserProfile.vue +++ b/src/components/settings/UserProfile.vue @@ -539,6 +539,10 @@ export default { text = "ID Service"; break; } + case config.SERVICE_TYPES.DASHBOARD: { + text = "Dashboard Service"; + break; + } case config.SERVICE_TYPES.QUEST: { text = "Quest Service"; break; diff --git a/src/components/teams/AdminTeams.vue b/src/components/teams/AdminTeams.vue index f049e02..03718db 100644 --- a/src/components/teams/AdminTeams.vue +++ b/src/components/teams/AdminTeams.vue @@ -180,6 +180,52 @@ + +
+ +
+
+
+
{{ eachService.name }}
+
+
{{ group.label }}
+
+ + +
+
+
+
+
@@ -338,6 +384,45 @@ const SSI_PREDEFINED_ROLES = [ } ]; +const DASHBOARD_PREDEFINED_ROLES = [ + { + key: "dashboard_viewer", + name: "Viewer", + icon: "mdi-eye-outline", + description: "Read-only access to Dashboard service operations.", + badge: "Read-only", + recommendedFor: "Stakeholders who need Dashboard visibility", + permissions: ["READ_SERVICE"] + }, + { + key: "dashboard_editor", + name: "Editor", + icon: "mdi-pencil-outline", + description: "Access to read and modify Dashboard service configuration.", + badge: "Edit access", + recommendedFor: "Operations or product teams", + permissions: ["READ_SERVICE", "WRITE_SERVICE", "UPDATE_SERVICE", "DELETE_SERVICE"] + }, + { + key: "dashboard_admin", + name: "Admin", + icon: "mdi-shield-star-outline", + description: "Full Dashboard service access.", + badge: "Full access", + recommendedFor: "Dashboard administrators", + permissions: ["ALL"] + }, + { + key: "custom", + name: "Custom Role", + icon: "mdi-tune-variant", + description: "Start with no permissions and configure manually.", + badge: "Advanced", + recommendedFor: "Custom enterprise setups", + permissions: [] + } +]; + export default { name: "AdminTeams", components: { @@ -352,6 +437,9 @@ export default { SSI_PREDEFINED_ROLES() { return SSI_PREDEFINED_ROLES; }, + DASHBOARD_PREDEFINED_ROLES() { + return DASHBOARD_PREDEFINED_ROLES; + }, categorizedServices() { const ssiServices = this.localAllServices.filter(s => s.id === config.SERVICE_TYPES.SSI_API); const idServices = this.localAllServices.filter( @@ -371,17 +459,25 @@ export default { hasSSIService() { return this.localAllServices.some(s => s.id === config.SERVICE_TYPES.SSI_API); }, + hasDashboardService() { + return this.localAllServices.some(s => s.id === config.SERVICE_TYPES.DASHBOARD); + }, hasIDService() { return this.localAllServices.some( - s => s.id !== config.SERVICE_TYPES.SSI_API && s.id !== config.SERVICE_TYPES.QUEST + s => s.id !== config.SERVICE_TYPES.SSI_API && s.id !== config.SERVICE_TYPES.QUEST && s.id !== config.SERVICE_TYPES.DASHBOARD ); }, ssiServices() { return this.localAllServices.filter(s => s.id === config.SERVICE_TYPES.SSI_API); }, + dashboardServices() { + return this.localAllServices.filter(s => s.id === config.SERVICE_TYPES.DASHBOARD); + }, idServices() { return this.localAllServices.filter( - s => s.id !== config.SERVICE_TYPES.SSI_API && s.id !== config.SERVICE_TYPES.QUEST + s => s.id !== config.SERVICE_TYPES.SSI_API && + s.id !== config.SERVICE_TYPES.QUEST && + s.id !== config.SERVICE_TYPES.DASHBOARD ); } }, @@ -411,7 +507,8 @@ export default { checked: true, selectedRoles: { id: 'viewer', - ssi: 'auditor' + ssi: 'auditor', + dashboard: 'custom' } } }, @@ -450,9 +547,17 @@ export default { { label: 'Document', icon: 'mdi-file-document-outline', iconColor: '#92400e', keys: ['READ_DOCUMENT', 'VERIFY_DOCUMENT'] }, { label: 'Compliance', icon: 'mdi-shield-check-outline', iconColor: '#1d4ed8', keys: ['READ_COMPLIANCE'] }, ]; - const groupDefs = serviceId === config.SERVICE_TYPES.SSI_API ? SSI_GROUPS : ID_GROUPS; + const DASHBOARD_GROUPS = [ + { label: 'General', icon: 'mdi-star-outline', iconColor: '#6366f1', keys: ['ALL'] }, + { label: 'Dashboard Controls', icon: 'mdi-view-dashboard-outline', iconColor: '#0f766e', keys: ['READ_SERVICE', 'WRITE_SERVICE', 'UPDATE_SERVICE', 'DELETE_SERVICE'] } + ]; + const groupDefs = serviceId === config.SERVICE_TYPES.SSI_API + ? SSI_GROUPS + : serviceId === config.SERVICE_TYPES.DASHBOARD + ? DASHBOARD_GROUPS + : ID_GROUPS; const assignedKeys = new Set(); - const result = []; + const result = []; for (const group of groupDefs) { const perms = group.keys.filter(k => allKeys.includes(k)); if (perms.length) { @@ -460,9 +565,10 @@ export default { perms.forEach(k => assignedKeys.add(k)); } } - // const remaining = allKeys.filter(k => !assignedKeys.has(k)); - // if (remaining.length) { - // result.push({ label: 'Other', icon: 'mdi-dots-horizontal', iconColor: '#6b7280', permissions: remaining }); + const remaining = allKeys.filter(k => !assignedKeys.has(k)); + if (remaining.length) { + result.push({ label: 'Other', icon: 'mdi-dots-horizontal', iconColor: '#6b7280', permissions: remaining }); + } // } return result; }, @@ -491,6 +597,10 @@ export default { const ssiDefault = SSI_PREDEFINED_ROLES.find(r => r.key === 'auditor'); if (ssiDefault) this.selectPredefinedRole(ssiDefault, 'ssi'); } + if (this.hasDashboardService) { + const dashboardDefault = DASHBOARD_PREDEFINED_ROLES.find(r => r.key === 'dashboard_viewer'); + if (dashboardDefault) this.selectPredefinedRole(dashboardDefault, 'dashboard'); + } }, @@ -509,10 +619,26 @@ export default { this.roleModel = { ...role }; this.selectedRoles.id = this.detectSelectedPredefinedRole('id'); this.selectedRoles.ssi = this.detectSelectedPredefinedRole('ssi'); + this.selectedRoles.dashboard = this.detectSelectedPredefinedRole('dashboard'); this.$root.$emit("bv::toggle::collapse", "sidebar-right"); }, getServiceIdsByType(serviceType) { - return (serviceType === 'ssi' ? this.ssiServices : this.idServices).map(s => s.id); + if (serviceType === 'ssi') return this.ssiServices.map(s => s.id); + if (serviceType === 'dashboard') return this.dashboardServices.map(s => s.id); + return this.idServices.map(s => s.id); + }, + getAllPermissionKeysForServiceType(serviceType) { + const targetServices = serviceType === 'ssi' + ? this.ssiServices + : serviceType === 'dashboard' + ? this.dashboardServices + : this.idServices; + const keys = new Set(); + targetServices.forEach(service => { + if (!service?.accessList) return; + Object.keys(service.accessList).forEach(key => keys.add(key)); + }); + return Array.from(keys); }, detectSelectedPredefinedRole(serviceType) { const serviceIds = new Set(this.getServiceIdsByType(serviceType)); @@ -520,11 +646,18 @@ export default { .filter(p => serviceIds.has(p.serviceType)) .map(p => p.access); const currentSet = new Set(currentPermissions); - const roles = (serviceType === 'ssi' ? SSI_PREDEFINED_ROLES : PREDEFINED_ROLES).filter(r => r.key !== 'custom'); + const allServicePermissions = new Set(this.getAllPermissionKeysForServiceType(serviceType)); + const roles = serviceType === 'ssi' + ? SSI_PREDEFINED_ROLES + : serviceType === 'dashboard' + ? DASHBOARD_PREDEFINED_ROLES + : PREDEFINED_ROLES; + const filteredRoles = roles.filter(r => r.key !== 'custom'); if (!currentSet.size) return 'custom'; - const matched = roles.find(role => { + const matched = filteredRoles.find(role => { if (role.permissions.includes('ALL')) { - return currentSet.has('ALL'); + if (currentSet.has('ALL')) return true; + return Array.from(allServicePermissions).every(access => currentSet.has(access)); } if (role.permissions.length !== currentSet.size) return false; return role.permissions.every(p => currentSet.has(p)); @@ -545,11 +678,20 @@ export default { }, buildPermissionsForRole(role, serviceType) { const permissions = []; - const targetServices = serviceType === 'ssi' ? this.ssiServices : this.idServices; + const targetServices = serviceType === 'ssi' + ? this.ssiServices + : serviceType === 'dashboard' + ? this.dashboardServices + : this.idServices; targetServices.forEach(service => { - if (role.permissions.includes('ALL') && service.accessList.ALL !== undefined) { - permissions.push({ serviceType: service.id, access: 'ALL' }); + if (!service?.accessList) return; + if (role.permissions.includes('ALL')) { + Object.keys(service.accessList).forEach(access => { + if (service.accessList[access] !== undefined) { + permissions.push({ serviceType: service.id, access }); + } + }); return; } role.permissions.forEach(access => { @@ -725,7 +867,7 @@ export default { ], "servicePermissions": [] } - this.selectedRoles = { id: 'viewer', ssi: 'auditor' }; + this.selectedRoles = { id: 'viewer', ssi: 'auditor', dashboard: 'custom' }; this.edit = false; }, }, diff --git a/src/config.js b/src/config.js index 07d4b26..3c8f575 100644 --- a/src/config.js +++ b/src/config.js @@ -104,6 +104,16 @@ config['SERVICE_TYPES'] = Object.freeze({ SSI_API: 'SSI_API', CAVACH_API: 'CAVACH_API', QUEST: 'QUEST', + DASHBOARD: 'DASHBOARD', +}) + +config['DASHBOARD_ACCESS'] = Object.freeze({ + ALL: 'ALL', + READ_SERVICE: 'READ_SERVICE', + WRITE_SERVICE: 'WRITE_SERVICE', + UPDATE_SERVICE: 'UPDATE_SERVICE', + DELETE_SERVICE: 'DELETE_SERVICE', + }) config['GRANT_TYPES_ENUM'] = Object.freeze({ From 5c1571e779ba7e64fa4742b8f9fd9f4ed3f1236f Mon Sep 17 00:00:00 2001 From: Varsha Kumari Date: Tue, 2 Jun 2026 10:47:21 +0530 Subject: [PATCH 2/4] small fix --- src/components/teams/AdminTeams.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/teams/AdminTeams.vue b/src/components/teams/AdminTeams.vue index 03718db..0e26258 100644 --- a/src/components/teams/AdminTeams.vue +++ b/src/components/teams/AdminTeams.vue @@ -565,10 +565,10 @@ export default { perms.forEach(k => assignedKeys.add(k)); } } - const remaining = allKeys.filter(k => !assignedKeys.has(k)); - if (remaining.length) { - result.push({ label: 'Other', icon: 'mdi-dots-horizontal', iconColor: '#6b7280', permissions: remaining }); - } + // const remaining = allKeys.filter(k => !assignedKeys.has(k)); + // if (remaining.length) { + // result.push({ label: 'Other', icon: 'mdi-dots-horizontal', iconColor: '#6b7280', permissions: remaining }); + // } // } return result; }, From 1b816412101342b14aaa6605b781ac554a32270d Mon Sep 17 00:00:00 2001 From: Varsha Kumari Date: Tue, 2 Jun 2026 10:49:31 +0530 Subject: [PATCH 3/4] fix --- src/components/teams/AdminTeams.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/teams/AdminTeams.vue b/src/components/teams/AdminTeams.vue index 0e26258..c95f535 100644 --- a/src/components/teams/AdminTeams.vue +++ b/src/components/teams/AdminTeams.vue @@ -569,7 +569,6 @@ export default { // if (remaining.length) { // result.push({ label: 'Other', icon: 'mdi-dots-horizontal', iconColor: '#6b7280', permissions: remaining }); // } - // } return result; }, createTeamPopup() { From 2c7fbc6f4519613f0230d7702178044a7168dea2 Mon Sep 17 00:00:00 2001 From: Varsha Kumari Date: Tue, 2 Jun 2026 19:21:39 +0530 Subject: [PATCH 4/4] removed unused filter --- src/store/mainStore.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/mainStore.js b/src/store/mainStore.js index 25fd41e..78464ce 100644 --- a/src/store/mainStore.js +++ b/src/store/mainStore.js @@ -1050,7 +1050,6 @@ const mainStore = { let resp = await RequestHandler(url, 'GET', {}, headers) if (resp) { - resp = resp.filter(x => !(x.id == 'DASHBOARD')) commit('insertAllServices', resp); } else { return null