Fix earlier merges done from main which were incomplete.
This commit is contained in:
@@ -85,11 +85,12 @@ onUnmounted(() => {
|
||||
const permissionSummary = computed(() => {
|
||||
const summary = {}
|
||||
for (const o of orgs.value) {
|
||||
const orgBase = { uuid: o.uuid, display_name: o.display_name }
|
||||
const orgPerms = new Set(o.permissions || [])
|
||||
const orgBase = { uuid: o.uuid, display_name: o.org.display_name }
|
||||
// o.permissions is a dict[UUID, Permission]
|
||||
const orgPermUuids = new Set(Object.keys(o.permissions || {}))
|
||||
|
||||
// Org-level permissions (direct) - only count if org can grant them
|
||||
for (const pid of o.permissions || []) {
|
||||
for (const pid of Object.keys(o.permissions || {})) {
|
||||
if (!summary[pid]) summary[pid] = { orgs: [], orgSet: new Set(), userCount: 0 }
|
||||
if (!summary[pid].orgSet.has(o.uuid)) {
|
||||
summary[pid].orgs.push(orgBase)
|
||||
@@ -98,17 +99,18 @@ const permissionSummary = computed(() => {
|
||||
}
|
||||
|
||||
// Role-based permissions (inheritance) - only count if org can grant them
|
||||
for (const r of o.roles) {
|
||||
for (const pid of r.permissions) {
|
||||
for (const [roleUuid, r] of Object.entries(o.roles || {})) {
|
||||
// r.permissions is dict[UUID, bool]
|
||||
for (const pid of Object.keys(r.permissions || {})) {
|
||||
// Only count if the org can grant this permission
|
||||
if (!orgPerms.has(pid)) continue
|
||||
if (!orgPermUuids.has(pid)) continue
|
||||
|
||||
if (!summary[pid]) summary[pid] = { orgs: [], orgSet: new Set(), userCount: 0 }
|
||||
if (!summary[pid].orgSet.has(o.uuid)) {
|
||||
summary[pid].orgs.push(orgBase)
|
||||
summary[pid].orgSet.add(o.uuid)
|
||||
}
|
||||
summary[pid].userCount += r.users.length
|
||||
summary[pid].userCount += roleUserCount(o, roleUuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,18 +167,33 @@ function parseHash() {
|
||||
|
||||
async function loadOrgs() {
|
||||
const data = await apiJson('/auth/api/admin/orgs')
|
||||
orgs.value = data.map(o => {
|
||||
const roles = o.roles.map(r => ({ ...r, org: o.uuid, users: [] }))
|
||||
const roleMap = Object.fromEntries(roles.map(r => [r.display_name, r]))
|
||||
for (const u of o.users || []) {
|
||||
if (roleMap[u.role]) roleMap[u.role].users.push(u)
|
||||
}
|
||||
return { ...o, roles }
|
||||
})
|
||||
orgs.value = Object.entries(data).map(([uuid, o]) => ({ uuid, ...o }))
|
||||
}
|
||||
|
||||
// Helper to get users for a role as sorted array of [uuid, user]
|
||||
function roleUsers(org, roleUuid) {
|
||||
return Object.entries(org.users)
|
||||
.filter(([_, u]) => u.role === roleUuid)
|
||||
.sort(([, a], [, b]) => {
|
||||
const nameA = a.display_name.toLowerCase()
|
||||
const nameB = b.display_name.toLowerCase()
|
||||
return nameA.localeCompare(nameB)
|
||||
})
|
||||
}
|
||||
|
||||
// Helper to count users in a role
|
||||
function roleUserCount(org, roleUuid) {
|
||||
return Object.values(org.users).filter(u => u.role === roleUuid).length
|
||||
}
|
||||
|
||||
// Helper to count total users in an org
|
||||
function orgUserCount(org) {
|
||||
return Object.keys(org.users).length
|
||||
}
|
||||
|
||||
async function loadPermissions() {
|
||||
permissions.value = await apiJson('/auth/api/admin/permissions')
|
||||
const data = await apiJson('/auth/api/admin/permissions')
|
||||
permissions.value = Object.entries(data).map(([uuid, p]) => ({ uuid, ...p }))
|
||||
}
|
||||
|
||||
async function loadOidcClients() {
|
||||
@@ -268,13 +285,13 @@ async function performOrgDeletion(orgUuid) {
|
||||
}
|
||||
|
||||
function deleteOrg(org) {
|
||||
const userCount = org.roles.reduce((acc, r) => acc + r.users.length, 0)
|
||||
const userCount = orgUserCount(org)
|
||||
|
||||
if (userCount === 0) {
|
||||
// No users in the organization, safe to delete directly
|
||||
performOrgDeletion(org.uuid)
|
||||
.then(() => {
|
||||
authStore.showMessage(`Organization "${org.display_name}" deleted.`, 'success', 2500)
|
||||
authStore.showMessage(`Organization "${org.org.display_name}" deleted.`, 'success', 2500)
|
||||
})
|
||||
.catch(e => {
|
||||
authStore.showMessage(e.message || 'Failed to delete organization', 'error')
|
||||
@@ -283,13 +300,14 @@ function deleteOrg(org) {
|
||||
}
|
||||
|
||||
// Build detailed breakdown of users by role
|
||||
const roleParts = org.roles
|
||||
.filter(r => r.users.length > 0)
|
||||
.map(r => `${r.users.length} ${r.display_name}`)
|
||||
const roleParts = Object.entries(org.roles)
|
||||
.map(([uuid, r]) => ({ role: r, count: roleUserCount(org, uuid) }))
|
||||
.filter(x => x.count > 0)
|
||||
.map(x => `${x.count} ${x.role.display_name}`)
|
||||
|
||||
const affects = roleParts.join(', ')
|
||||
|
||||
openDialog('confirm', { message: `Delete organization "${org.display_name}", including accounts of ${affects})?`, action: async () => {
|
||||
openDialog('confirm', { message: `Delete organization "${org.org.display_name}", including accounts of ${affects})?`, action: async () => {
|
||||
await performOrgDeletion(org.uuid)
|
||||
} })
|
||||
}
|
||||
@@ -297,7 +315,7 @@ function deleteOrg(org) {
|
||||
function createUserInRole(org, role) { openDialog('user-create', { org, role }) }
|
||||
|
||||
function deleteUser(user, userDetail) {
|
||||
const credentialCount = userDetail?.credentials?.length || 0
|
||||
const credentialCount = userDetail?.credentials ? Object.keys(userDetail.credentials).length : 0
|
||||
const userUuid = user.uuid
|
||||
const userName = user.display_name
|
||||
const orgUuid = user.org // org UUID is stored in selectedUser
|
||||
@@ -341,9 +359,9 @@ async function moveUserToRole(user, targetRoleUuid) {
|
||||
}
|
||||
}
|
||||
|
||||
function onUserDragStart(e, user, org) {
|
||||
function onUserDragStart(e, userUuid, org) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify({ user_uuid: user.uuid, org }))
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify({ user_uuid: userUuid, org }))
|
||||
}
|
||||
|
||||
function onRoleDragOver(e) {
|
||||
@@ -356,7 +374,7 @@ function onRoleDrop(e, org, role) {
|
||||
try {
|
||||
const data = JSON.parse(e.dataTransfer.getData('text/plain'))
|
||||
if (data.org !== org.uuid) return // only within same org
|
||||
const user = org.roles.flatMap(r => r.users).find(u => u.uuid === data.user_uuid)
|
||||
const user = org.users[data.user_uuid]
|
||||
if (user) moveUserToRole(user, role.uuid)
|
||||
} catch (_) { /* ignore */ }
|
||||
}
|
||||
@@ -379,11 +397,14 @@ function deleteRole(role) {
|
||||
}
|
||||
|
||||
async function toggleRolePermission(role, pid, checked) {
|
||||
// Optimistic update
|
||||
const prevPermissions = [...role.permissions]
|
||||
const newPermissions = checked
|
||||
? [...role.permissions, pid]
|
||||
: role.permissions.filter(p => p !== pid)
|
||||
// Optimistic update - role.permissions is dict[UUID, bool]
|
||||
const prevPermissions = { ...role.permissions }
|
||||
const newPermissions = { ...role.permissions }
|
||||
if (checked) {
|
||||
newPermissions[pid] = true
|
||||
} else {
|
||||
delete newPermissions[pid]
|
||||
}
|
||||
role.permissions = newPermissions
|
||||
|
||||
try {
|
||||
@@ -411,8 +432,8 @@ function deletePermission(p) {
|
||||
// Count roles that have this permission
|
||||
let roleCount = 0
|
||||
for (const org of orgs.value) {
|
||||
for (const role of org.roles) {
|
||||
if (role.permissions.includes(p.uuid)) {
|
||||
for (const role of Object.values(org.roles)) {
|
||||
if (p.uuid in (role.permissions || {})) {
|
||||
roleCount++
|
||||
}
|
||||
}
|
||||
@@ -538,9 +559,10 @@ function openUser(u) {
|
||||
const selectedUser = computed(() => {
|
||||
if (!currentUserId.value) return null
|
||||
for (const o of orgs.value) {
|
||||
for (const r of o.roles) {
|
||||
const u = r.users.find(x => x.uuid === currentUserId.value)
|
||||
if (u) return { ...u, org: o.uuid, role_display_name: r.display_name }
|
||||
const u = o.users[currentUserId.value]
|
||||
if (u) {
|
||||
const role = o.roles[u.role]
|
||||
return { ...u, uuid: currentUserId.value, org: o.uuid, role_display_name: role?.display_name }
|
||||
}
|
||||
}
|
||||
return null
|
||||
@@ -554,7 +576,7 @@ const breadcrumbEntries = computed(() => {
|
||||
// For org admins, combine Admin and their org
|
||||
if (isOrgAdmin.value && !isMasterAdmin.value && orgs.value.length > 0) {
|
||||
const org = orgs.value[0]
|
||||
entries.push({ label: `Admin: ${org.display_name}`, href: `#org/${org.uuid}` })
|
||||
entries.push({ label: `Admin: ${org.org.display_name}`, href: `#org/${org.uuid}` })
|
||||
} else {
|
||||
entries.push({ label: 'Admin', href: adminUiPath() })
|
||||
}
|
||||
@@ -567,7 +589,7 @@ const breadcrumbEntries = computed(() => {
|
||||
// Add org breadcrumb only if it's not already included in the Admin entry
|
||||
const adminOrg = (isOrgAdmin.value && !isMasterAdmin.value && orgs.value.length > 0) ? orgs.value[0] : null
|
||||
if (orgToShow && (!adminOrg || orgToShow.uuid !== adminOrg.uuid)) {
|
||||
entries.push({ label: orgToShow.display_name, href: `#org/${orgToShow.uuid}` })
|
||||
entries.push({ label: orgToShow.org.display_name, href: `#org/${orgToShow.uuid}` })
|
||||
}
|
||||
if (currentOidcId.value) {
|
||||
const label = editingOidcClient.value?.isNew ? 'New Client' : (editingOidcClient.value?.name || 'OIDC Client')
|
||||
@@ -594,14 +616,23 @@ function generateUserRegistrationLink(u) {
|
||||
}
|
||||
|
||||
async function toggleOrgPermission(org, permId, checked) {
|
||||
// Build next permission list
|
||||
const has = org.permissions.includes(permId)
|
||||
// org.permissions is dict[UUID, Permission]
|
||||
const has = permId in org.permissions
|
||||
if (checked && has) return
|
||||
if (!checked && !has) return
|
||||
const next = checked ? [...org.permissions, permId] : org.permissions.filter(p => p !== permId)
|
||||
// Optimistic update
|
||||
const prev = [...org.permissions]
|
||||
org.permissions = next
|
||||
const prev = { ...org.permissions }
|
||||
if (checked) {
|
||||
// Need to fetch the permission object to add it
|
||||
const perm = permissions.value.find(p => p.uuid === permId)
|
||||
if (perm) {
|
||||
org.permissions = { ...org.permissions, [permId]: perm }
|
||||
}
|
||||
} else {
|
||||
const next = { ...org.permissions }
|
||||
delete next[permId]
|
||||
org.permissions = next
|
||||
}
|
||||
try {
|
||||
const params = new URLSearchParams({ permission_uuid: permId })
|
||||
await apiJson(`/auth/api/admin/orgs/${org.uuid}/permission?${params.toString()}`, { method: checked ? 'POST' : 'DELETE' })
|
||||
|
||||
Reference in New Issue
Block a user