user manager integration
This commit is contained in:
@@ -4,9 +4,11 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"build": "vite build --mode production --config vite.config.ts",
|
||||
"dev": "vite build --mode development --config vite.config.ts",
|
||||
"watch": "vite build --mode development --watch --config vite.config.ts",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
|
||||
148
src/components/AdminSecurityPanel.vue
Normal file
148
src/components/AdminSecurityPanel.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface User {
|
||||
uid: string;
|
||||
identity: string;
|
||||
label: string;
|
||||
provider?: string;
|
||||
provider_subject?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
user: User;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
update: [];
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
const success = ref<string | null>(null);
|
||||
const unlinkDialog = ref(false);
|
||||
|
||||
// OIDC unlink is handled via the user_manager service since it modifies user record
|
||||
const unlinkProvider = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const response = await fetch('/m/user_manager/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
version: 1,
|
||||
transaction: crypto.randomUUID(),
|
||||
operation: 'user.provider.unlink',
|
||||
data: {
|
||||
uid: props.user.uid,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
success.value = 'Provider unlinked successfully';
|
||||
unlinkDialog.value = false;
|
||||
emit('update');
|
||||
} else {
|
||||
const data = await response.json();
|
||||
error.value = data.error || 'Failed to unlink provider';
|
||||
}
|
||||
} catch (err: any) {
|
||||
error.value = err.message || 'Failed to unlink provider';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCard v-if="user.provider" variant="outlined">
|
||||
<VCardTitle class="d-flex align-center">
|
||||
<VIcon icon="mdi-shield-lock" class="mr-2" />
|
||||
External Identity Provider
|
||||
</VCardTitle>
|
||||
<VCardText>
|
||||
<VAlert
|
||||
v-if="error"
|
||||
type="error"
|
||||
closable
|
||||
class="mb-4"
|
||||
@click:close="error = null"
|
||||
>
|
||||
{{ error }}
|
||||
</VAlert>
|
||||
|
||||
<VAlert
|
||||
v-if="success"
|
||||
type="success"
|
||||
closable
|
||||
class="mb-4"
|
||||
@click:close="success = null"
|
||||
>
|
||||
{{ success }}
|
||||
</VAlert>
|
||||
|
||||
<p class="mb-2">
|
||||
<strong>Provider:</strong>
|
||||
<VChip size="small" color="primary" class="ml-2">
|
||||
{{ user.provider.toUpperCase() }}
|
||||
</VChip>
|
||||
</p>
|
||||
|
||||
<p class="mb-4">
|
||||
<strong>Subject:</strong> {{ user.provider_subject || 'N/A' }}
|
||||
</p>
|
||||
|
||||
<p class="mb-4 text-body-2">
|
||||
This user is linked to an external identity provider. Unlinking will remove the association,
|
||||
and the user will no longer be able to login via {{ user.provider.toUpperCase() }}.
|
||||
</p>
|
||||
|
||||
<VBtn
|
||||
color="error"
|
||||
prepend-icon="mdi-link-off"
|
||||
@click="unlinkDialog = true"
|
||||
>
|
||||
Unlink Provider
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
|
||||
<!-- Unlink Provider Dialog -->
|
||||
<VDialog v-model="unlinkDialog" max-width="500">
|
||||
<VCard>
|
||||
<VCardTitle class="d-flex align-center">
|
||||
<VIcon icon="mdi-alert" color="warning" class="mr-2" />
|
||||
Unlink External Provider
|
||||
</VCardTitle>
|
||||
<VCardText>
|
||||
<VAlert type="warning" class="mb-4">
|
||||
<strong>Warning:</strong> This will remove the association with the external identity provider.
|
||||
The user will no longer be able to login via {{ user.provider?.toUpperCase() }}.
|
||||
</VAlert>
|
||||
<p>
|
||||
Are you sure you want to unlink <strong>{{ user.provider?.toUpperCase() }}</strong> for <strong>{{ user.label }}</strong>?
|
||||
</p>
|
||||
<p class="mt-2 text-caption">
|
||||
Make sure the user has alternative authentication methods configured before unlinking.
|
||||
</p>
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<VSpacer />
|
||||
<VBtn @click="unlinkDialog = false">Cancel</VBtn>
|
||||
<VBtn
|
||||
color="error"
|
||||
:loading="loading"
|
||||
@click="unlinkProvider"
|
||||
>
|
||||
Unlink Provider
|
||||
</VBtn>
|
||||
</VCardActions>
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</VCard>
|
||||
</template>
|
||||
15
src/integrations.ts
Normal file
15
src/integrations.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { ModuleIntegrations } from "@KTXC/types/moduleTypes";
|
||||
|
||||
const integrations: ModuleIntegrations = {
|
||||
user_manager_security_panels: [
|
||||
{
|
||||
id: 'oidc-management',
|
||||
label: 'External Provider',
|
||||
icon: 'mdi-shield-lock',
|
||||
priority: 30,
|
||||
component: () => import('@/components/AdminSecurityPanel.vue'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default integrations;
|
||||
@@ -2,9 +2,11 @@
|
||||
// Exports components for admin configuration UI
|
||||
|
||||
import ConfigPanel from './components/ConfigPanel.vue'
|
||||
import integrations from './integrations'
|
||||
|
||||
export {
|
||||
ConfigPanel
|
||||
ConfigPanel,
|
||||
integrations
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
Reference in New Issue
Block a user