Enhance user contact data visibility based on role permissions
This commit introduces role-based access control for user contact information in the CMS. It updates the user list display to show email and phone details only to users with the 'vorstand' role, while masking this information for others. Additionally, it modifies the API endpoints to ensure that contact data is only returned for authorized users, improving data privacy and security.
This commit is contained in:
@@ -143,12 +143,28 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="text-sm text-gray-600">
|
<div class="text-sm text-gray-600">
|
||||||
{{ user.email }}
|
<template v-if="canViewContactData">
|
||||||
|
{{ user.email || '-' }}
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-gray-400"
|
||||||
|
>
|
||||||
|
Nur für Vorstand
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="text-sm text-gray-600">
|
<div class="text-sm text-gray-600">
|
||||||
{{ user.phone || '-' }}
|
<template v-if="canViewContactData">
|
||||||
|
{{ user.phone || '-' }}
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="text-gray-400"
|
||||||
|
>
|
||||||
|
Nur für Vorstand
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
@@ -307,6 +323,13 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { AlertCircle, Check, X } from 'lucide-vue-next'
|
import { AlertCircle, Check, X } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
const canViewContactData = computed(() => {
|
||||||
|
// Kontaktdaten nur für Vorstand sichtbar
|
||||||
|
return authStore.hasRole('vorstand')
|
||||||
|
})
|
||||||
|
|
||||||
const allUsers = ref([])
|
const allUsers = ref([])
|
||||||
const currentUserId = ref(null)
|
const currentUserId = ref(null)
|
||||||
const successMessage = ref('')
|
const successMessage = ref('')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getUserFromToken, readUsers, hasAnyRole, migrateUserRoles } from '../../../utils/auth.js'
|
import { getUserFromToken, readUsers, hasAnyRole, hasRole, migrateUserRoles } from '../../../utils/auth.js'
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
@@ -13,18 +13,24 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const users = await readUsers()
|
const users = await readUsers()
|
||||||
|
|
||||||
// Return users without passwords
|
const isVorstand = hasRole(currentUser, 'vorstand')
|
||||||
|
|
||||||
|
// Return users without Passwörter; Kontaktdaten nur für Vorstand
|
||||||
const safeUsers = users.map(u => {
|
const safeUsers = users.map(u => {
|
||||||
const migrated = migrateUserRoles({ ...u })
|
const migrated = migrateUserRoles({ ...u })
|
||||||
const roles = Array.isArray(migrated.roles) ? migrated.roles : (migrated.role ? [migrated.role] : ['mitglied'])
|
const roles = Array.isArray(migrated.roles) ? migrated.roles : (migrated.role ? [migrated.role] : ['mitglied'])
|
||||||
|
|
||||||
|
const email = isVorstand ? u.email : undefined
|
||||||
|
const phone = isVorstand ? (u.phone || '') : undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: u.id,
|
id: u.id,
|
||||||
email: u.email,
|
email,
|
||||||
name: u.name,
|
name: u.name,
|
||||||
roles: roles,
|
roles: roles,
|
||||||
role: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
role: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
||||||
phone: u.phone || '',
|
phone,
|
||||||
active: u.active,
|
active: u.active,
|
||||||
created: u.created,
|
created: u.created,
|
||||||
lastLogin: u.lastLogin
|
lastLogin: u.lastLogin
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { verifyToken } from '../utils/auth.js'
|
import { verifyToken, getUserFromToken, hasRole } from '../utils/auth.js'
|
||||||
import { readMembers } from '../utils/members.js'
|
import { readMembers } from '../utils/members.js'
|
||||||
import { readUsers, migrateUserRoles } from '../utils/auth.js'
|
import { readUsers, migrateUserRoles } from '../utils/auth.js'
|
||||||
|
|
||||||
@@ -22,6 +22,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentUser = await getUserFromToken(token)
|
||||||
|
|
||||||
// Get manual members and registered users
|
// Get manual members and registered users
|
||||||
const manualMembers = await readMembers()
|
const manualMembers = await readMembers()
|
||||||
const registeredUsers = await readUsers()
|
const registeredUsers = await readUsers()
|
||||||
@@ -141,9 +143,20 @@ export default defineEventHandler(async (event) => {
|
|||||||
// Sort by name
|
// Sort by name
|
||||||
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
|
|
||||||
|
// Serverseitiger Datenschutz: Kontaktdaten nur für Vorstand
|
||||||
|
const isVorstand = hasRole(currentUser, 'vorstand')
|
||||||
|
const safeMembers = isVorstand
|
||||||
|
? mergedMembers
|
||||||
|
: mergedMembers.map(m => ({
|
||||||
|
...m,
|
||||||
|
email: undefined,
|
||||||
|
phone: undefined,
|
||||||
|
address: undefined
|
||||||
|
}))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
members: mergedMembers
|
members: safeMembers
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Abrufen der Mitgliederliste:', error)
|
console.error('Fehler beim Abrufen der Mitgliederliste:', error)
|
||||||
|
|||||||
Reference in New Issue
Block a user