feat: enhance LDAP integration with new configuration options and browsing capabilities
This commit is contained in:
@@ -5,11 +5,9 @@ import FolderPicker from '@/components/FolderPicker.vue'
|
||||
|
||||
const storagePath = ref('')
|
||||
const ldapEnabled = ref(false)
|
||||
const ldapHost = ref('')
|
||||
const ldapPort = ref(389)
|
||||
const ldapBaseDN = ref('')
|
||||
const ldapBindDN = ref('')
|
||||
const ldapBindPassword = ref('')
|
||||
const ldapUrl = ref('')
|
||||
const ldapAdminUser = ref('')
|
||||
const ldapAdminPassword = ref('')
|
||||
|
||||
const oldPass = ref('')
|
||||
const newPass = ref('')
|
||||
@@ -22,17 +20,20 @@ const showGitPrompt = ref(false)
|
||||
const gitCommitMessage = ref('Initial commit')
|
||||
const gitCommitting = ref(false)
|
||||
|
||||
// LDAP Browser State
|
||||
const ldapUsers = ref<string[]>([])
|
||||
const ldapGroups = ref<string[]>([])
|
||||
const ldapBrowsing = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await gql<{ config: any }>(`{ config { storagePath ldap { host port baseDN bindDN } } }`)
|
||||
const data = await gql<{ config: any }>(`{ config { storagePath ldap { url adminUser } } }`)
|
||||
storagePath.value = data.config.storagePath || ''
|
||||
|
||||
if (data.config.ldap) {
|
||||
ldapEnabled.value = true
|
||||
ldapHost.value = data.config.ldap.host
|
||||
ldapPort.value = data.config.ldap.port
|
||||
ldapBaseDN.value = data.config.ldap.baseDN
|
||||
ldapBindDN.value = data.config.ldap.bindDN
|
||||
ldapUrl.value = data.config.ldap.url
|
||||
ldapAdminUser.value = data.config.ldap.adminUser
|
||||
}
|
||||
} catch (err: any) {
|
||||
status.value = { msg: err.message, isError: true }
|
||||
@@ -83,11 +84,9 @@ async function saveLdap() {
|
||||
let input = null
|
||||
if (ldapEnabled.value) {
|
||||
input = {
|
||||
host: ldapHost.value,
|
||||
port: ldapPort.value,
|
||||
baseDN: ldapBaseDN.value,
|
||||
bindDN: ldapBindDN.value,
|
||||
bindPassword: ldapBindPassword.value
|
||||
url: ldapUrl.value,
|
||||
adminUser: ldapAdminUser.value,
|
||||
adminPass: ldapAdminPassword.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +97,52 @@ async function saveLdap() {
|
||||
}
|
||||
}
|
||||
|
||||
async function testLdap() {
|
||||
try {
|
||||
const data = await gql<{ testLdapConnection: { success: boolean, message: string } }>(
|
||||
`mutation TestLdap($url: String!, $adminUser: String, $adminPass: String) {
|
||||
testLdapConnection(url: $url, adminUser: $adminUser, adminPass: $adminPass) { success message }
|
||||
}`,
|
||||
{ url: ldapUrl.value, adminUser: ldapAdminUser.value, adminPass: ldapAdminPassword.value }
|
||||
)
|
||||
if (data.testLdapConnection.success) {
|
||||
showStatus('LDAP connection successful.', false)
|
||||
} else {
|
||||
showStatus('LDAP connection failed: ' + data.testLdapConnection.message, true)
|
||||
}
|
||||
} catch (err: any) {
|
||||
showStatus(err.message, true)
|
||||
}
|
||||
}
|
||||
|
||||
async function browseLdap() {
|
||||
try {
|
||||
ldapBrowsing.value = true
|
||||
const data = await gql<{ ldapBrowse: { users: string[], groups: string[] } }>(
|
||||
`query LdapBrowse($url: String, $adminUser: String, $adminPass: String) {
|
||||
ldapBrowse(url: $url, adminUser: $adminUser, adminPass: $adminPass) { users groups }
|
||||
}`,
|
||||
{ url: ldapUrl.value || undefined, adminUser: ldapAdminUser.value || undefined, adminPass: ldapAdminPassword.value || undefined }
|
||||
)
|
||||
ldapUsers.value = data.ldapBrowse.users
|
||||
ldapGroups.value = data.ldapBrowse.groups
|
||||
showStatus('LDAP structure loaded.')
|
||||
} catch (err: any) {
|
||||
showStatus(err.message, true)
|
||||
} finally {
|
||||
ldapBrowsing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function importSubject(type: 'user' | 'group', name: string) {
|
||||
try {
|
||||
await gql(`mutation ImportSubj($type: String!, $name: String!) { importLdapSubject(type: $type, name: $name) }`, { type, name })
|
||||
showStatus(`Imported ${type} ${name} successfully.`)
|
||||
} catch (err: any) {
|
||||
showStatus(err.message, true)
|
||||
}
|
||||
}
|
||||
|
||||
async function savePassword() {
|
||||
if (newPass.value.length < 8) {
|
||||
showStatus('New password must be at least 8 characters.', true)
|
||||
@@ -217,33 +262,58 @@ async function savePassword() {
|
||||
</div>
|
||||
|
||||
<div v-if="ldapEnabled" class="grid gap-4 sm:grid-cols-2 mb-4">
|
||||
<div class="sm:col-span-2 md:col-span-1">
|
||||
<label class="label">Host</label>
|
||||
<input v-model="ldapHost" class="input" placeholder="ldap.example.com" />
|
||||
</div>
|
||||
<div class="sm:col-span-2 md:col-span-1">
|
||||
<label class="label">Port</label>
|
||||
<input v-model.number="ldapPort" type="number" class="input" placeholder="389" />
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="label">Base DN <span class="text-slate-400 text-xs font-normal">(e.g. dc=example,dc=com)</span></label>
|
||||
<input v-model="ldapBaseDN" class="input" placeholder="dc=example,dc=com" />
|
||||
<label class="label">LDAP URL</label>
|
||||
<input v-model="ldapUrl" class="input" placeholder="ldap://ldap.example.com:389" />
|
||||
</div>
|
||||
<div class="sm:col-span-2 md:col-span-1">
|
||||
<label class="label">Bind DN (Service Account)</label>
|
||||
<input v-model="ldapBindDN" class="input" placeholder="cn=reader,dc=example,dc=com" />
|
||||
<label class="label">Admin User (Optional Service Account)</label>
|
||||
<input v-model="ldapAdminUser" class="input" placeholder="cn=reader,dc=example,dc=com" />
|
||||
</div>
|
||||
<div class="sm:col-span-2 md:col-span-1">
|
||||
<label class="label">Bind Password</label>
|
||||
<input v-model="ldapBindPassword" type="password" class="input" />
|
||||
<label class="label">Admin Password</label>
|
||||
<input v-model="ldapAdminPassword" type="password" class="input" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<div class="flex justify-end gap-2 mb-6">
|
||||
<button class="btn-secondary" v-if="ldapEnabled" @click="testLdap">Test Connection</button>
|
||||
<button class="btn-primary" @click="saveLdap">
|
||||
{{ ldapEnabled ? 'Save LDAP Config' : 'Save & Disable LDAP' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- LDAP Browser -->
|
||||
<div v-if="ldapEnabled" class="border-t border-slate-200 dark:border-slate-700 pt-6 mt-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-md font-semibold">LDAP Browser</h3>
|
||||
<button class="btn-secondary text-sm py-1 px-3" @click="browseLdap" :disabled="ldapBrowsing">
|
||||
{{ ldapBrowsing ? 'Loading...' : 'Browse Users & Groups' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="ldapUsers.length || ldapGroups.length" class="grid sm:grid-cols-2 gap-6">
|
||||
<div class="card p-4">
|
||||
<h4 class="font-medium text-slate-800 dark:text-slate-200 mb-2 border-b pb-1">Users</h4>
|
||||
<ul class="max-h-48 overflow-y-auto space-y-1">
|
||||
<li v-for="u in ldapUsers" :key="u" class="flex justify-between items-center text-sm p-1 hover:bg-slate-50 dark:hover:bg-slate-800 rounded">
|
||||
<span class="truncate">{{ u }}</span>
|
||||
<button class="text-accent-600 hover:text-accent-700 text-xs" @click="importSubject('user', u)">Import</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<h4 class="font-medium text-slate-800 dark:text-slate-200 mb-2 border-b pb-1">Groups</h4>
|
||||
<ul class="max-h-48 overflow-y-auto space-y-1">
|
||||
<li v-for="g in ldapGroups" :key="g" class="flex justify-between items-center text-sm p-1 hover:bg-slate-50 dark:hover:bg-slate-800 rounded">
|
||||
<span class="truncate">{{ g }}</span>
|
||||
<button class="text-accent-600 hover:text-accent-700 text-xs" @click="importSubject('group', g)">Import</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else-if="!ldapBrowsing" class="text-sm text-slate-500 italic">Click browse to load LDAP hierarchy.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user