Skip to main content

shared/
permissions.rs

1use indexmap::IndexMap;
2use serde::Serialize;
3use std::{collections::HashSet, sync::LazyLock};
4use utoipa::ToSchema;
5
6#[derive(ToSchema, Serialize, Clone)]
7pub struct PermissionGroup {
8    pub description: &'static str,
9    pub permissions: IndexMap<&'static str, &'static str>,
10}
11
12impl PermissionGroup {
13    pub fn add_permission(&mut self, key: &'static str, description: &'static str) {
14        self.permissions.insert(key, description);
15    }
16}
17
18#[derive(ToSchema, Serialize)]
19pub struct PermissionMap {
20    #[serde(skip)]
21    list: HashSet<String>,
22    #[serde(flatten)]
23    map: IndexMap<&'static str, PermissionGroup>,
24}
25
26impl PermissionMap {
27    pub(crate) fn new() -> Self {
28        Self {
29            list: HashSet::new(),
30            map: IndexMap::new(),
31        }
32    }
33
34    pub(crate) fn replace(&mut self, map: IndexMap<&'static str, PermissionGroup>) {
35        self.list = map
36            .iter()
37            .flat_map(|(key, group)| {
38                group
39                    .permissions
40                    .keys()
41                    .map(|permission| format!("{key}.{permission}"))
42                    .collect::<HashSet<_>>()
43            })
44            .collect();
45        self.map = map;
46    }
47
48    #[inline]
49    pub fn list(&self) -> &HashSet<String> {
50        &self.list
51    }
52
53    pub fn validate_permissions(
54        &self,
55        permissions: &[compact_str::CompactString],
56    ) -> Result<(), garde::Error> {
57        for permission in permissions {
58            if !self.list().contains(&**permission) {
59                return Err(garde::Error::new(compact_str::format_compact!(
60                    "invalid permission: {permission}"
61                )));
62            }
63        }
64
65        Ok(())
66    }
67}
68
69pub(crate) static BASE_USER_PERMISSIONS: LazyLock<IndexMap<&'static str, PermissionGroup>> =
70    LazyLock::new(|| {
71        IndexMap::from([
72            (
73                "account",
74                PermissionGroup {
75                    description: "Permissions that control the ability to change account settings.",
76                    permissions: IndexMap::from([
77                        (
78                            "infos",
79                            "Allows changing the account's basic account information.",
80                        ),
81                        ("email", "Allows changing the account's email address."),
82                        ("password", "Allows changing the account's password."),
83                        (
84                            "two-factor",
85                            "Allows adding and removing two-factor authentication.",
86                        ),
87                        (
88                            "avatar",
89                            "Allows updating and removing the account's avatar.",
90                        ),
91                    ]),
92                },
93            ),
94            (
95                "servers",
96                PermissionGroup {
97                    description: "Permissions that control the ability to list servers and manage server groups.",
98                    permissions: IndexMap::from([
99                        ("create", "Allows creating new server groups."),
100                        ("read", "Allows viewing servers and server groups."),
101                        ("update", "Allows modifying server groups."),
102                        ("delete", "Allows deleting server groups."),
103                    ]),
104                },
105            ),
106            (
107                "api-keys",
108                PermissionGroup {
109                    description: "Permissions that control the ability to manage API keys on an account. API keys can never edit themselves or assign permissions they do not have.",
110                    permissions: IndexMap::from([
111                        ("create", "Allows creating new API keys."),
112                        ("read", "Allows viewing API keys and their permissions."),
113                        ("update", "Allows modifying other API keys."),
114                        ("delete", "Allows deleting API keys."),
115                        ("recreate", "Allows recreating API keys."),
116                    ]),
117                },
118            ),
119            (
120                "security-keys",
121                PermissionGroup {
122                    description: "Permissions that control the ability to manage security keys on an account.",
123                    permissions: IndexMap::from([
124                        ("create", "Allows creating new security keys."),
125                        ("read", "Allows viewing security keys."),
126                        ("update", "Allows modifying security keys."),
127                        ("delete", "Allows deleting security keys."),
128                    ]),
129                },
130            ),
131            (
132                "ssh-keys",
133                PermissionGroup {
134                    description: "Permissions that control the ability to manage SSH keys on an account.",
135                    permissions: IndexMap::from([
136                        ("create", "Allows creating or importing new SSH keys."),
137                        ("read", "Allows viewing SSH keys."),
138                        ("update", "Allows modifying other SSH keys."),
139                        ("delete", "Allows deleting SSH keys."),
140                    ]),
141                },
142            ),
143            (
144                "oauth-links",
145                PermissionGroup {
146                    description: "Permissions that control the ability to manage OAuth links on an account.",
147                    permissions: IndexMap::from([
148                        ("create", "Allows creating new OAuth links."),
149                        ("read", "Allows viewing OAuth links."),
150                        ("delete", "Allows deleting OAuth links."),
151                    ]),
152                },
153            ),
154            (
155                "command-snippets",
156                PermissionGroup {
157                    description: "Permissions that control the ability to manage command snippets on an account.",
158                    permissions: IndexMap::from([
159                        ("create", "Allows creating new command snippets."),
160                        ("read", "Allows viewing command snippets."),
161                        ("update", "Allows modifying command snippets."),
162                        ("delete", "Allows deleting command snippets."),
163                    ]),
164                },
165            ),
166            (
167                "sessions",
168                PermissionGroup {
169                    description: "Permissions that control the ability to manage sessions on an account.",
170                    permissions: IndexMap::from([
171                        ("read", "Allows viewing sessions and their IP addresses."),
172                        ("delete", "Allows deleting sessions."),
173                    ]),
174                },
175            ),
176            (
177                "activity",
178                PermissionGroup {
179                    description: "Permissions that control the ability to view the activity log on an account.",
180                    permissions: IndexMap::from([(
181                        "read",
182                        "Allows viewing the account's activity logs.",
183                    )]),
184                },
185            ),
186        ])
187    });
188
189pub(crate) static USER_PERMISSIONS: LazyLock<parking_lot::RwLock<PermissionMap>> =
190    LazyLock::new(|| parking_lot::RwLock::new(PermissionMap::new()));
191
192#[inline]
193pub fn get_user_permissions() -> parking_lot::RwLockReadGuard<'static, PermissionMap> {
194    USER_PERMISSIONS.read()
195}
196
197#[inline]
198pub fn validate_user_permissions(
199    permissions: &[compact_str::CompactString],
200    _context: &(),
201) -> Result<(), garde::Error> {
202    get_user_permissions().validate_permissions(permissions)
203}
204
205pub(crate) static BASE_ADMIN_PERMISSIONS: LazyLock<IndexMap<&'static str, PermissionGroup>> =
206    LazyLock::new(|| {
207        IndexMap::from([
208            (
209                "stats",
210                PermissionGroup {
211                    description: "Permissions that control the ability to view stats for the panel.",
212                    permissions: IndexMap::from([("read", "Allows viewing panel statistics.")]),
213                },
214            ),
215            (
216                "settings",
217                PermissionGroup {
218                    description: "Permissions that control the ability to manage settings for the panel.",
219                    permissions: IndexMap::from([
220                        ("read", "Allows viewing panel settings and secrets."),
221                        ("update", "Allows modifying panel settings and secrets."),
222                    ]),
223                },
224            ),
225            (
226                "email-templates",
227                PermissionGroup {
228                    description: "Permissions that control the ability to manage email templates for the panel.",
229                    permissions: IndexMap::from([
230                        ("read", "Allows viewing email templates."),
231                        ("update", "Allows modifying email templates."),
232                    ]),
233                },
234            ),
235            (
236                "extensions",
237                PermissionGroup {
238                    description: "Permissions that control the ability to manage extensions for the panel.",
239                    permissions: IndexMap::from([
240                        ("read", "Allows viewing panel extensions."),
241                        (
242                            "manage",
243                            "Allows installing, updating, and removing panel extensions, usually also used to manage extension settings.",
244                        ),
245                    ]),
246                },
247            ),
248            (
249                "announcements",
250                PermissionGroup {
251                    description: "Permissions that control the ability to manage announcements for the panel.",
252                    permissions: IndexMap::from([
253                        ("create", "Allows creating new announcements."),
254                        ("read", "Allows viewing announcements."),
255                        ("update", "Allows modifying announcements."),
256                        ("delete", "Allows deleting announcements."),
257                    ]),
258                },
259            ),
260            (
261                "assets",
262                PermissionGroup {
263                    description: "Permissions that control the ability to manage assets for the panel.",
264                    permissions: IndexMap::from([
265                        ("read", "Allows viewing panel assets."),
266                        ("upload", "Allows creating and modifying assets."),
267                        ("delete", "Allows deleting panel assets."),
268                    ]),
269                },
270            ),
271            (
272                "users",
273                PermissionGroup {
274                    description: "Permissions that control the ability to manage users for the panel.",
275                    permissions: IndexMap::from([
276                        ("create", "Allows creating new users."),
277                        ("read", "Allows viewing users."),
278                        ("update", "Allows modifying users."),
279                        (
280                            "disable-two-factor",
281                            "Allows removing two-factor authentication from users.",
282                        ),
283                        ("delete", "Allows deleting users."),
284                        (
285                            "email",
286                            "Allows sending email actions to users, such as password resets.",
287                        ),
288                        ("activity", "Allows viewing a user's activity log."),
289                        (
290                            "oauth-links",
291                            "Allows viewing and managing a user's OAuth links.",
292                        ),
293                        ("impersonate", "Allows impersonating other users."),
294                    ]),
295                },
296            ),
297            (
298                "roles",
299                PermissionGroup {
300                    description: "Permissions that control the ability to manage roles for the panel.",
301                    permissions: IndexMap::from([
302                        ("create", "Allows creating new roles."),
303                        ("read", "Allows viewing roles."),
304                        ("update", "Allows modifying roles."),
305                        ("delete", "Allows deleting roles."),
306                    ]),
307                },
308            ),
309            (
310                "locations",
311                PermissionGroup {
312                    description: "Permissions that control the ability to manage locations for the panel.",
313                    permissions: IndexMap::from([
314                        ("create", "Allows creating new locations."),
315                        ("read", "Allows viewing locations."),
316                        ("update", "Allows modifying locations."),
317                        ("delete", "Allows deleting locations."),
318                        (
319                            "database-hosts",
320                            "Allows viewing and managing a location's database hosts.",
321                        ),
322                    ]),
323                },
324            ),
325            (
326                "backup-configurations",
327                PermissionGroup {
328                    description: "Permissions that control the ability to manage backup configurations for the panel.",
329                    permissions: IndexMap::from([
330                        ("create", "Allows creating new backup configurations."),
331                        (
332                            "read",
333                            "Allows viewing backup configurations and their passwords.",
334                        ),
335                        (
336                            "update",
337                            "Allows modifying backup configurations and their passwords.",
338                        ),
339                        ("delete", "Allows deleting backup configurations."),
340                        (
341                            "backups",
342                            "Allows viewing backups associated with a backup configuration.",
343                        ),
344                    ]),
345                },
346            ),
347            (
348                "nodes",
349                PermissionGroup {
350                    description: "Permissions that control the ability to manage nodes for the panel.",
351                    permissions: IndexMap::from([
352                        ("create", "Allows creating new nodes."),
353                        ("read", "Allows viewing nodes."),
354                        ("update", "Allows modifying nodes."),
355                        ("delete", "Allows deleting nodes."),
356                        ("read-token", "Allows viewing a node's token."),
357                        ("reset-token", "Allows resetting a node's token."),
358                        (
359                            "allocations",
360                            "Allows viewing and managing a node's allocations.",
361                        ),
362                        ("mounts", "Allows viewing and managing a node's mounts."),
363                        ("backups", "Allows viewing and managing a node's backups."),
364                        ("power", "Allows executing mass-power actions on nodes."),
365                        (
366                            "transfers",
367                            "Allows viewing and managing mass-server transfers between nodes.",
368                        ),
369                    ]),
370                },
371            ),
372            (
373                "servers",
374                PermissionGroup {
375                    description: "Permissions that control the ability to manage servers for the panel.",
376                    permissions: IndexMap::from([
377                        ("create", "Allows creating new servers."),
378                        ("read", "Allows viewing servers."),
379                        ("update", "Allows modifying servers."),
380                        ("delete", "Allows deleting servers."),
381                        (
382                            "transfer",
383                            "Allows transferring servers to other nodes or canceling ongoing transfers.",
384                        ),
385                        (
386                            "allocations",
387                            "Allows viewing and managing a server's allocations.",
388                        ),
389                        (
390                            "variables",
391                            "Allows viewing and managing a server's variables.",
392                        ),
393                        ("mounts", "Allows viewing and managing a server's mounts."),
394                    ]),
395                },
396            ),
397            (
398                "nests",
399                PermissionGroup {
400                    description: "Permissions that control the ability to manage nests for the panel.",
401                    permissions: IndexMap::from([
402                        ("create", "Allows creating new nests."),
403                        ("read", "Allows viewing nests."),
404                        ("update", "Allows modifying nests."),
405                        ("delete", "Allows deleting nests."),
406                    ]),
407                },
408            ),
409            (
410                "eggs",
411                PermissionGroup {
412                    description: "Permissions that control the ability to manage eggs for the panel.",
413                    permissions: IndexMap::from([
414                        ("create", "Allows creating and importing new eggs."),
415                        ("read", "Allows viewing eggs."),
416                        ("update", "Allows modifying eggs."),
417                        ("delete", "Allows deleting eggs."),
418                        ("mounts", "Allows viewing and managing an egg's mounts."),
419                    ]),
420                },
421            ),
422            (
423                "egg-configurations",
424                PermissionGroup {
425                    description: "Permissions that control the ability to manage egg configurations for the panel.",
426                    permissions: IndexMap::from([
427                        ("create", "Allows creating new egg configurations."),
428                        ("read", "Allows viewing egg configurations."),
429                        ("update", "Allows modifying egg configurations."),
430                        ("delete", "Allows deleting egg configurations."),
431                    ]),
432                },
433            ),
434            (
435                "egg-repositories",
436                PermissionGroup {
437                    description: "Permissions that control the ability to manage egg repositories for the panel.",
438                    permissions: IndexMap::from([
439                        ("create", "Allows creating new egg repositories."),
440                        ("read", "Allows viewing egg repositories."),
441                        ("update", "Allows modifying egg repositories."),
442                        ("delete", "Allows deleting egg repositories."),
443                        (
444                            "sync",
445                            "Allows synchronizing egg repositories with their remote sources.",
446                        ),
447                    ]),
448                },
449            ),
450            (
451                "database-hosts",
452                PermissionGroup {
453                    description: "Permissions that control the ability to manage database hosts for the panel.",
454                    permissions: IndexMap::from([
455                        ("create", "Allows creating new database hosts."),
456                        ("read", "Allows viewing database hosts."),
457                        ("update", "Allows modifying database hosts."),
458                        ("delete", "Allows deleting database hosts."),
459                        ("test", "Allows testing database host connections."),
460                    ]),
461                },
462            ),
463            (
464                "oauth-providers",
465                PermissionGroup {
466                    description: "Permissions that control the ability to manage OAuth providers for the panel.",
467                    permissions: IndexMap::from([
468                        ("create", "Allows creating new OAuth providers."),
469                        ("read", "Allows viewing OAuth providers."),
470                        ("update", "Allows modifying OAuth providers."),
471                        ("delete", "Allows deleting OAuth providers."),
472                    ]),
473                },
474            ),
475            (
476                "mounts",
477                PermissionGroup {
478                    description: "Permissions that control the ability to manage mounts for the panel.",
479                    permissions: IndexMap::from([
480                        ("create", "Allows creating new mounts."),
481                        ("read", "Allows viewing mounts."),
482                        ("update", "Allows modifying mounts."),
483                        ("delete", "Allows deleting mounts."),
484                    ]),
485                },
486            ),
487            (
488                "activity",
489                PermissionGroup {
490                    description: "Permissions that control the ability to view the activity log for all admin operations.",
491                    permissions: IndexMap::from([(
492                        "read",
493                        "Allows viewing the activity logs for all admin operations.",
494                    )]),
495                },
496            ),
497        ])
498    });
499
500pub(crate) static ADMIN_PERMISSIONS: LazyLock<parking_lot::RwLock<PermissionMap>> =
501    LazyLock::new(|| parking_lot::RwLock::new(PermissionMap::new()));
502
503#[inline]
504pub fn get_admin_permissions() -> parking_lot::RwLockReadGuard<'static, PermissionMap> {
505    ADMIN_PERMISSIONS.read()
506}
507
508#[inline]
509pub fn validate_admin_permissions(
510    permissions: &[compact_str::CompactString],
511    _context: &(),
512) -> Result<(), garde::Error> {
513    get_admin_permissions().validate_permissions(permissions)
514}
515
516pub(crate) static BASE_SERVER_PERMISSIONS: LazyLock<IndexMap<&'static str, PermissionGroup>> =
517    LazyLock::new(|| {
518        IndexMap::from([
519            (
520                "control",
521                PermissionGroup {
522                    description: "Permissions that control the ability to control the power state of a server, read the console, or send commands.",
523                    permissions: IndexMap::from([
524                        ("read-console", "Allows reading the server console logs."),
525                        (
526                            "console",
527                            "Allows sending commands to the server instance via the console.",
528                        ),
529                        ("start", "Allows starting the server if it is stopped."),
530                        ("stop", "Allows stopping the server if it is running."),
531                        (
532                            "restart",
533                            "Allows restarting the server. This permits starting the server if it is offline, but not placing it in a completely stopped state.",
534                        ),
535                    ]),
536                },
537            ),
538            (
539                "subusers",
540                PermissionGroup {
541                    description: "Permissions that control the ability to manage subusers of a server. Users can never edit their own account or assign permissions they do not have.",
542                    permissions: IndexMap::from([
543                        ("create", "Allows creating new subusers for the server."),
544                        ("read", "Allows viewing subusers and their permissions."),
545                        ("update", "Allows modifying other subusers."),
546                        ("delete", "Allows deleting subusers from the server."),
547                    ]),
548                },
549            ),
550            (
551                "files",
552                PermissionGroup {
553                    description: "Permissions that control the ability to modify the filesystem for this server.",
554                    permissions: IndexMap::from([
555                        (
556                            "create",
557                            "Allows creating additional files and folders via the panel or direct upload.",
558                        ),
559                        (
560                            "read",
561                            "Allows viewing the contents of a directory, but not reading or downloading individual files.",
562                        ),
563                        (
564                            "read-content",
565                            "Allows viewing the contents of a specific file. This also permits downloading files.",
566                        ),
567                        (
568                            "update",
569                            "Allows updating the contents of an existing file or directory.",
570                        ),
571                        ("delete", "Allows deleting files or directories."),
572                        (
573                            "archive",
574                            "Allows archiving the contents of a directory and decompressing files.",
575                        ),
576                        ("sftp", "Allows connecting via SFTP to manage files."),
577                    ]),
578                },
579            ),
580            (
581                "backups",
582                PermissionGroup {
583                    description: "Permissions that control the ability to manage server backups.",
584                    permissions: IndexMap::from([
585                        ("create", "Allows creating new backups for the server."),
586                        ("read", "Allows viewing existing backups."),
587                        ("download", "Allows downloading backups."),
588                        ("restore", "Allows restoring backups."),
589                        ("update", "Allows updating existing backups."),
590                        ("delete", "Allows deleting backups."),
591                    ]),
592                },
593            ),
594            (
595                "schedules",
596                PermissionGroup {
597                    description: "Permissions that control the ability to manage server schedules.",
598                    permissions: IndexMap::from([
599                        ("create", "Allows creating new schedules."),
600                        ("read", "Allows viewing existing schedules."),
601                        ("update", "Allows updating existing schedules."),
602                        ("delete", "Allows deleting schedules."),
603                    ]),
604                },
605            ),
606            (
607                "allocations",
608                PermissionGroup {
609                    description: "Permissions that control the ability to modify the port allocations for this server.",
610                    permissions: IndexMap::from([
611                        (
612                            "read",
613                            "Allows viewing all allocations currently assigned to this server. Users with any level of access can always view the primary allocation.",
614                        ),
615                        (
616                            "create",
617                            "Allows assigning additional allocations to the server.",
618                        ),
619                        (
620                            "update",
621                            "Allows changing the primary server allocation and attaching notes to allocations.",
622                        ),
623                        ("delete", "Allows deleting allocations from the server."),
624                    ]),
625                },
626            ),
627            (
628                "startup",
629                PermissionGroup {
630                    description: "Permissions that control the ability to view and modify this server's startup parameters.",
631                    permissions: IndexMap::from([
632                        (
633                            "read",
634                            "Allows viewing the startup variables for the server.",
635                        ),
636                        ("update", "Allows modifying the startup variables."),
637                        (
638                            "command",
639                            "Allows modifying the command used to start the server.",
640                        ),
641                        (
642                            "docker-image",
643                            "Allows modifying the Docker image used when running the server.",
644                        ),
645                    ]),
646                },
647            ),
648            (
649                "databases",
650                PermissionGroup {
651                    description: "Permissions that control the ability to manage databases on this server.",
652                    permissions: IndexMap::from([
653                        ("create", "Allows creating new databases."),
654                        (
655                            "read",
656                            "Allows viewing databases associated with this server.",
657                        ),
658                        (
659                            "read-password",
660                            "Allows viewing the password associated with a database instance.",
661                        ),
662                        (
663                            "update",
664                            "Allows rotating the password on a database instance. Users without the read-password permission will not see the updated password.",
665                        ),
666                        (
667                            "recreate",
668                            "Allows deleting and recreating a database, in the process wiping all data.",
669                        ),
670                        (
671                            "delete",
672                            "Allows removing database instances from this server.",
673                        ),
674                    ]),
675                },
676            ),
677            (
678                "mounts",
679                PermissionGroup {
680                    description: "Permissions that control the ability to manage server mounts.",
681                    permissions: IndexMap::from([
682                        ("attach", "Allows attaching new mounts to the server."),
683                        ("read", "Allows viewing existing mounts."),
684                        ("detach", "Allows detaching mounts from the server."),
685                    ]),
686                },
687            ),
688            (
689                "settings",
690                PermissionGroup {
691                    description: "Permissions that control the ability to manage settings on this server.",
692                    permissions: IndexMap::from([
693                        (
694                            "rename",
695                            "Allows renaming the server and changing its description.",
696                        ),
697                        ("timezone", "Allows changing the server's timezone."),
698                        (
699                            "auto-kill",
700                            "Allows changing the server's auto-kill settings.",
701                        ),
702                        (
703                            "auto-start",
704                            "Allows changing the server's auto-start settings.",
705                        ),
706                        ("install", "Allows triggering a reinstall of the server."),
707                        (
708                            "cancel-install",
709                            "Allows canceling the server's installation process.",
710                        ),
711                    ]),
712                },
713            ),
714            (
715                "activity",
716                PermissionGroup {
717                    description: "Permissions that control the ability to view the activity log on this server.",
718                    permissions: IndexMap::from([
719                        ("read", "Allows viewing the server's activity logs."),
720                        (
721                            "read-ip",
722                            "Allows viewing IP addresses associated with activity logs.",
723                        ),
724                    ]),
725                },
726            ),
727        ])
728    });
729
730pub(crate) static SERVER_PERMISSIONS: LazyLock<parking_lot::RwLock<PermissionMap>> =
731    LazyLock::new(|| parking_lot::RwLock::new(PermissionMap::new()));
732
733#[inline]
734pub fn get_server_permissions() -> parking_lot::RwLockReadGuard<'static, PermissionMap> {
735    SERVER_PERMISSIONS.read()
736}
737
738#[inline]
739pub fn validate_server_permissions(
740    permissions: &[compact_str::CompactString],
741    _context: &(),
742) -> Result<(), garde::Error> {
743    get_server_permissions().validate_permissions(permissions)
744}