Skip to content

Commit 4898062

Browse files
committed
Move adminGroups to category level for better access control
- adminGroups now defined at category level instead of per-app - Users in adminGroups see ALL apps in that category - Simplifies configuration and makes admin override more intuitive - Update TypeScript interfaces and validation schema - Update README with corrected examples
1 parent 2112311 commit 4898062

File tree

5 files changed

+36
-38
lines changed

5 files changed

+36
-38
lines changed

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -331,31 +331,32 @@ docker-compose -f docker-compose.prod.yml up -d
331331

332332
**Restricted Apps (Groups Specified)**
333333
```yaml
334-
- id: "sonarr"
335-
name: "Sonarr"
336-
url: "https://sonarr.example.com"
337-
icon: "fa-satellite-dish"
338-
groups: ["media-power-users"] # Only these users see it
334+
- id: "grafana"
335+
name: "Grafana"
336+
url: "https://grafana.example.com"
337+
icon: "fa-chart-bar"
338+
groups: ["/monitoring-viewers"] # Only these users see it
339339
```
340340
341341
**Admin Override**
342342
343-
Define `adminGroups` to give users access to ALL apps in a category:
343+
Define `adminGroups` at the category level to give users access to ALL apps in that category:
344344

345345
```yaml
346346
categories:
347-
media:
347+
monitoring:
348+
name: "Monitoring"
349+
adminGroups:
350+
- "/monitoring-admins" # Users in this group see ALL Monitoring apps
348351
apps:
349-
- id: "plex"
350-
groups: ["media-users"]
351-
adminGroups: ["media-admins"] # Admins see ALL Media apps
352+
- id: "grafana"
353+
groups: ["/monitoring-viewers"]
352354
353-
- id: "sonarr"
354-
groups: ["media-power-users"]
355-
adminGroups: ["media-admins"] # Same admin group = sees everything
355+
- id: "prometheus"
356+
groups: ["/monitoring-power-users"]
356357
```
357358

358-
If a user is in `media-admins`, they see Plex, Sonarr, and ALL other apps in the Media category, regardless of individual `groups` settings.
359+
If a user is in `/monitoring-admins`, they see Grafana, Prometheus, and ALL other apps in the Monitoring category, regardless of individual app `groups` settings.
359360

360361
### Category Visibility
361362

backend/src/services/apps.service.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import logger from '../utils/logger';
55
export class AppsService {
66
/**
77
* Get all apps accessible to a user based on their groups
8-
* - If app has NO groups/adminGroups: accessible to everyone (public)
8+
* - If app has NO groups: accessible to everyone (public)
99
* - If app has groups: user must be in one of those groups
10-
* - If user is admin for ANY app in category: sees ALL apps in that category
10+
* - If user is admin of category: sees ALL apps in that category
1111
*/
1212
getAppsForUser(userGroups: string[]): CategoryWithApps[] {
1313
logger.debug('Filtering apps for user', {
@@ -26,7 +26,7 @@ export class AppsService {
2626
}
2727

2828
const isAdminOfCategory = this.isUserAdminOfCategory(
29-
categoryData.apps,
29+
categoryData.adminGroups,
3030
userGroups
3131
);
3232

@@ -66,20 +66,16 @@ export class AppsService {
6666
}
6767

6868
/**
69-
* Check if user is admin for ANY app in the given category
69+
* Check if user is admin of the given category
7070
*/
7171
private isUserAdminOfCategory(
72-
apps: Application[],
72+
adminGroups: string[] | undefined,
7373
userGroups: string[]
7474
): boolean {
75-
return apps.some((app) => {
76-
if (!app.adminGroups || app.adminGroups.length === 0) {
77-
return false;
78-
}
79-
return app.adminGroups.some((adminGroup) =>
80-
userGroups.includes(adminGroup)
81-
);
82-
});
75+
if (!adminGroups || adminGroups.length === 0) {
76+
return false;
77+
}
78+
return adminGroups.some((adminGroup) => userGroups.includes(adminGroup));
8379
}
8480

8581
/**

backend/src/services/config.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const configSchema = Joi.object({
1515
icon: Joi.string().required(),
1616
order: Joi.number().required(),
1717
description: Joi.string(),
18+
adminGroups: Joi.array().items(Joi.string()),
1819
apps: Joi.array()
1920
.items(
2021
Joi.object({
@@ -24,7 +25,6 @@ const configSchema = Joi.object({
2425
url: Joi.string().uri().required(),
2526
icon: Joi.string().required(),
2627
groups: Joi.array().items(Joi.string()),
27-
adminGroups: Joi.array().items(Joi.string()),
2828
external: Joi.boolean(),
2929
})
3030
)

backend/src/types/config.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export interface Application {
55
url: string;
66
icon: string;
77
groups?: string[];
8-
adminGroups?: string[];
98
external?: boolean;
109
}
1110

@@ -14,6 +13,7 @@ export interface CategoryData {
1413
icon: string;
1514
order: number;
1615
description?: string;
16+
adminGroups?: string[];
1717
apps: Application[];
1818
}
1919

config/apps.yaml.example

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# Application Directory Configuration
22
# Hierarchical category-based application structure
3-
# If groups/adminGroups are not specified, the app is accessible to all authenticated users
3+
# If groups are not specified on an app, it is accessible to all authenticated users
44
# Keycloak groups have a leading slash (e.g., /grafana-admin)
5+
# adminGroups at category level grant access to ALL apps in that category
56

67
categories:
78
media:
89
name: "Media & Entertainment"
910
icon: "fa-film"
1011
order: 1
1112
description: "Streaming and media management"
13+
adminGroups:
14+
- "/media-admin"
1215
apps:
1316
- id: "plex"
1417
name: "Plex"
@@ -24,14 +27,14 @@ categories:
2427
icon: "fa-tv"
2528
groups:
2629
- "/media-users"
27-
adminGroups:
28-
- "/media-admin"
2930

3031
productivity:
3132
name: "Productivity"
3233
icon: "fa-briefcase"
3334
order: 2
3435
description: "Productivity and collaboration tools"
36+
adminGroups:
37+
- "/productivity-admin"
3538
apps:
3639
- id: "nextcloud"
3740
name: "Nextcloud"
@@ -40,14 +43,14 @@ categories:
4043
icon: "fa-cloud"
4144
groups:
4245
- "/nextcloud-users"
43-
adminGroups:
44-
- "/nextcloud-admin"
4546

4647
monitoring:
4748
name: "Monitoring"
4849
icon: "fa-chart-line"
4950
order: 3
5051
description: "Monitoring and observability"
52+
adminGroups:
53+
- "/grafana-admin"
5154
apps:
5255
- id: "grafana"
5356
name: "Grafana"
@@ -57,8 +60,6 @@ categories:
5760
groups:
5861
- "/grafana-viewer"
5962
- "/grafana-editor"
60-
adminGroups:
61-
- "/grafana-admin"
6263

6364
development:
6465
name: "Development"
@@ -91,6 +92,8 @@ categories:
9192
icon: "fa-screwdriver-wrench"
9293
order: 6
9394
description: "Administrative tools"
95+
adminGroups:
96+
- "/sysadmins"
9497
apps:
9598
- id: "portainer"
9699
name: "Portainer"
@@ -99,5 +102,3 @@ categories:
99102
icon: "fa-docker"
100103
groups:
101104
- "/sysadmins"
102-
adminGroups:
103-
- "/sysadmins"

0 commit comments

Comments
 (0)