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..c95f535 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) { @@ -491,6 +596,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 +618,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 +645,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 +677,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 +866,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({ 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