Replace IsAdmin with role-based admin
Switch user admin handling from an AppUser boolean to ASP.NET Identity roles. Removed AppUser.IsAdmin and related configuration/model entries; added migration ReplaceIsAdminWithRoles to copy Users.IsAdmin=true into a persistent admin role and drop the IsAdmin column. CurrentUserResponse now exposes roles (string[]), AuthController returns ordered roles from UserManager, and IdentitySeedService now ensures the admin role exists and assigns/creates an initial admin user in that role. Program.cs registers an Admin-only policy (PolicyNames/RoleNames), adjusts cookie auth events to return 401/403 for API requests, and wires up authorization. Frontend updated to use roles: authSession normalizes roles, adds hasRole and ROLE_ADMIN, router and layout support meta.requiredRoles, and new Forbidden and AdminUsers pages/route are added. codexInfo.md updated to reflect the migration to role-based auth.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { fetchCurrentUser } from '@/services/authSession'
|
||||
|
||||
const router = useRouter()
|
||||
const isLoading = ref(true)
|
||||
const isAuthenticated = ref(false)
|
||||
|
||||
const primaryActionLabel = computed(() => (isAuthenticated.value ? 'Zum Dashboard' : 'Zum Login'))
|
||||
|
||||
async function resolveAuthState() {
|
||||
try {
|
||||
const user = await fetchCurrentUser({ force: true })
|
||||
isAuthenticated.value = user !== null
|
||||
} catch {
|
||||
isAuthenticated.value = false
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function navigatePrimaryAction() {
|
||||
if (isAuthenticated.value) {
|
||||
await router.replace({ name: 'Dashboard' })
|
||||
return
|
||||
}
|
||||
|
||||
await router.replace({ name: 'Login' })
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void resolveAuthState()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container fluid class="forbidden-page hoard-page hoard-page--centered">
|
||||
<section class="forbidden-shell hoard-shell-grid hoard-panel">
|
||||
<header class="forbidden-head">
|
||||
<p class="hoard-kicker">Fehlende Berechtigung</p>
|
||||
<h1>Kein Zugriff</h1>
|
||||
<p>Dein Konto hat keine ausreichende Rolle für diese Seite.</p>
|
||||
</header>
|
||||
|
||||
<div class="forbidden-actions hoard-action-row">
|
||||
<v-btn
|
||||
color="primary"
|
||||
prepend-icon="mdi-arrow-right"
|
||||
:loading="isLoading"
|
||||
:disabled="isLoading"
|
||||
@click="navigatePrimaryAction"
|
||||
>
|
||||
{{ primaryActionLabel }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.forbidden-page {
|
||||
--hoard-shell-width: min(640px, 100%);
|
||||
}
|
||||
|
||||
.forbidden-shell {
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.forbidden-head h1,
|
||||
.forbidden-head p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.forbidden-head h1 {
|
||||
margin-top: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.forbidden-head p {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user