implemented operation based permissions

This commit is contained in:
root
2025-12-24 19:22:20 -05:00
parent a9afa7ce13
commit 3d6aa856b4
18 changed files with 578 additions and 17 deletions

View File

@@ -16,10 +16,23 @@ export function useUser() {
const identifier = computed(() => store.identifier);
const identity = computed(() => store.identity);
const label = computed(() => store.label);
const roles = computed(() => store.roles);
const permissions = computed(() => store.permissions);
const hasPermission = (permission: string): boolean => {
return store.permissions.includes(permission);
return store.hasPermission(permission);
};
const hasAnyPermission = (perms: string[]): boolean => {
return store.hasAnyPermission(perms);
};
const hasAllPermissions = (perms: string[]): boolean => {
return store.hasAllPermissions(perms);
};
const hasRole = (role: string): boolean => {
return store.hasRole(role);
};
const logout = async (): Promise<void> => {
@@ -66,8 +79,12 @@ export function useUser() {
identifier,
identity,
label,
roles,
permissions,
hasPermission,
hasAnyPermission,
hasAllPermissions,
hasRole,
logout,
// Profile

View File

@@ -41,6 +41,7 @@ export const useUserStore = defineStore('userStore', () => {
const identifier = computed(() => auth.value?.identifier ?? null);
const identity = computed(() => auth.value?.identity ?? null);
const label = computed(() => auth.value?.label ?? null);
const roles = computed(() => auth.value?.roles ?? []);
const permissions = computed(() => auth.value?.permissions ?? []);
// =========================================================================
@@ -148,6 +149,61 @@ export const useUserStore = defineStore('userStore', () => {
});
}
// =========================================================================
// Permission Checking
// =========================================================================
/**
* Check if user has a specific permission
* Supports wildcards: user_manager.users.* matches all user actions
*/
function hasPermission(permission: string): boolean {
const userPermissions = permissions.value;
// Exact match
if (userPermissions.includes(permission)) {
return true;
}
// Wildcard match
for (const userPerm of userPermissions) {
if (userPerm.endsWith('.*')) {
const prefix = userPerm.slice(0, -2);
if (permission.startsWith(prefix + '.')) {
return true;
}
}
}
// Full wildcard
if (userPermissions.includes('*')) {
return true;
}
return false;
}
/**
* Check if user has ANY of the permissions (OR logic)
*/
function hasAnyPermission(perms: string[]): boolean {
return perms.some(p => hasPermission(p));
}
/**
* Check if user has ALL permissions (AND logic)
*/
function hasAllPermissions(perms: string[]): boolean {
return perms.every(p => hasPermission(p));
}
/**
* Check if user has a specific role
*/
function hasRole(role: string): boolean {
return roles.value.includes(role);
}
// =========================================================================
// Initialize from /init endpoint
// =========================================================================
@@ -180,6 +236,7 @@ export const useUserStore = defineStore('userStore', () => {
identifier,
identity,
label,
roles,
permissions,
// Profile getters
@@ -206,6 +263,12 @@ export const useUserStore = defineStore('userStore', () => {
getSetting,
setSetting,
// Permission actions
hasPermission,
hasAnyPermission,
hasAllPermissions,
hasRole,
// Init
init,
};

View File

@@ -15,6 +15,7 @@ export interface AuthenticatedUser {
identifier: string;
identity: string;
label: string;
roles?: string[];
permissions?: string[];
}

View File

@@ -19,3 +19,10 @@ const layoutStore = useLayoutStore();
</v-app>
</v-locale-provider>
</template>
<style>
.v-main.page-wrapper {
height: calc(100vh - 64px) !important;
overflow-y: auto !important;
}
</style>