use OCA\User_LDAP\ConnectionFactory;
use OCA\User_LDAP\Helper;
use OCA\User_LDAP\Settings\Admin;
-use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
-->
<template>
<fieldset class="ldap-wizard__advanced">
- <summary class="ldap-wizard__advanced__section">
- <h3>{{ t('user_ldap', 'Connection Settings') }}</h3>
+ <details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
+ <summary><h3>{{ t('user_ldap', 'Connection Settings') }}</h3></summary>
<NcCheckboxRadioSwitch :checked="ldapConfig.ldapConfigurationActive === '1'"
:aria-label="t('user_ldap', 'When unchecked, this configuration will be skipped.')"
:label="t('user_ldap', 'Cache Time-To-Live')"
:value="ldapConfig.ldapCacheTTL"
:helper-text="t('user_ldap', 'in seconds. A change empties the cache.')" />
- </summary>
+ </details>
- <summary class="ldap-wizard__advanced__section">
- <h3>{{ t('user_ldap', 'Directory Settings') }}</h3>
+ <details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
+ <summary><h3>{{ t('user_ldap', 'Directory Settings') }}</h3></summary>
<NcTextField autocomplete="off"
:value.sync="ldapConfig.ldapUserDisplayName"
:label="t('user_ldap', 'Default password policy DN')"
:value.sync="ldapConfig.ldapDefaultPPolicyDN"
:helper-text="t('user_ldap', 'The DN of a default password policy that will be used for password expiry handling. Works only when LDAP password changes per user are enabled and is only supported by OpenLDAP. Leave empty to disable password expiry handling.')" />
- </summary>
+ </details>
- <summary class="ldap-wizard__advanced__section">
- <h3>{{ t('user_ldap', 'Special Attributes') }}</h3>
+ <details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
+ <summary><h3>{{ t('user_ldap', 'Special Attributes') }}</h3></summary>
<NcTextField autocomplete="off"
:value.sync="ldapConfig.ldapQuotaAttribute"
:label="t('user_ldap', '`$home` Placeholder Field')"
:value.sync="ldapConfig.ldapExtStorageHomeAttribute"
:helper-text="t('user_ldap', '$home in an external storage configuration will be replaced with the value of the specified attribute')" />
- </summary>
+ </details>
- <summary class="ldap-wizard__advanced__section">
- <h3>{{ t('user_ldap', 'User Profile Attributes') }}</h3>
+ <details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
+ <summary><h3>{{ t('user_ldap', 'User Profile Attributes') }}</h3></summary>
<NcTextField autocomplete="off"
:label="t('user_ldap', 'Phone Field')"
:label="t('user_ldap', 'Birthdate Field')"
:value.sync="ldapConfig.ldapAttributeBirthDate"
:helper-text="t('user_ldap', 'User profile Date of birth will be set from the specified attribute')" />
- </summary>
+ </details>
</fieldset>
</template>
display: flex;
flex-direction: column;
gap: 8px;
+
+ summary {
+
+ h3 {
+ margin: 0;
+ display: inline;
+ cursor: pointer;
+ color: var(--color-text-lighter);
+ font-size: 16px;
+
+ }
+ }
+
+ &:hover, &[open] {
+ h3 {
+ color: var(--color-text-light);
+ }
+ }
}
}
</style>
<div class="ldap-wizard__expert__line">
<strong>{{ t('user_ldap', 'Username-LDAP User Mapping') }}</strong>
{{ t('user_ldap', 'Usernames are used to store and assign metadata. In order to precisely identify and recognize users, each LDAP user will have an internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage.') }}
- <NcButton id="ldap_action_clear_user_mappings" type="button" name="ldap_action_clear_user_mappings">
+ <NcButton @click="console.log('TODO')">
{{ t('user_ldap', 'Clear Username-LDAP User Mapping') }}
</NcButton>
- <NcButton id="ldap_action_clear_group_mappings" type="button" name="ldap_action_clear_group_mappings">
+ <NcButton @click="console.log('TODO')">
{{ t('user_ldap', 'Clear Groupname-LDAP Group Mapping') }}
</NcButton>
</div>
{{ t('user_ldap', 'When logging in, {instanceName} will find the user based on the following attributes:', { instanceName }) }}
<div class="ldap-wizard__login__line ldap-wizard__login__login-attributes">
- <NcCheckboxRadioSwitch :checked="ldapConfig.ldapAgentName === '1'"
+ <NcCheckboxRadioSwitch :checked="ldapConfig.ldapLoginFilterUsername === '1'"
:aria-label="t('user_ldap', 'Allows login against the LDAP/AD username, which is either `uid` or `sAMAccountName` and will be detected.')"
- @update:checked="ldapConfig.ldapAgentName = $event ? '1' : '0'">
+ @update:checked="ldapConfig.ldapLoginFilterUsername = $event ? '1' : '0'">
{{ t('user_ldap', 'LDAP/AD Username') }}
</NcCheckboxRadioSwitch>
:placeholder="t('user_ldap', 'Port')"
type="number"
autocomplete="off" />
- <NcButton @click="detectPort">
+ <NcButton :disabled="currentWizardActions.includes('guessPortAndTLS')" @click="guessPortAndTLS">
{{ t('user_ldap', 'Detect Port') }}
</NcButton>
</div>
:placeholder="t('user_ldap', 'One Base DN per line')"
:helper-text="t('user_ldap', 'You can specify Base DN for users and groups in the Advanced tab')" />
- <NcButton @click="detectBaseDN">
+ <NcButton :disabled="currentWizardActions.includes('guessBaseDN')" @click="guessBaseDN">
{{ t('user_ldap', 'Detect Base DN') }}
</NcButton>
- <NcButton @click="testBaseDN">
+ <NcButton :disabled="currentWizardActions.includes('countInBaseDN')" @click="countInBaseDN">
{{ t('user_ldap', 'Test Base DN') }}
</NcButton>
</div>
<div class="ldap-wizard__server__line">
- <NcCheckboxRadioSwitch :checked.sync="advancedAdmin"
- :aria-label="t('user_ldap', 'Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.')">
+ <NcCheckboxRadioSwitch :checked="ldapConfig.ldapExperiencedAdmin === '1'"
+ :aria-label="t('user_ldap', 'Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.')"
+ @update:checked="ldapConfig.ldapExperiencedAdmin = $event ? '1' : '0'">
{{ t('user_ldap', 'Manually enter LDAP filters (recommended for large directories)') }}
</NcCheckboxRadioSwitch>
</div>
import { NcButton, NcTextField, NcTextArea, NcCheckboxRadioSwitch } from '@nextcloud/vue'
import { useLDAPConfigStore } from '../../store/config'
-
-const ldapConfigStore = useLDAPConfigStore()
+import { callWizard } from '../../services/ldapConfigService'
const { ldapConfigId } = defineProps({
ldapConfigId: {
},
})
-const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+const ldapConfigStore = useLDAPConfigStore()
-// TODO: use this
-const advancedAdmin = ref(false)
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+const usersCount = ref<number|undefined>(undefined)
+const currentWizardActions = ref<string[]>([])
/**
*
*/
-async function detectPort() {
- // TODO
+async function guessPortAndTLS() {
+ currentWizardActions.value.push('guessPortAndTLS')
+ const { changes: { ldap_port: ldapPort } } = await callWizard('guessPortAndTLS', ldapConfigId)
+ ldapConfig.value.ldapPort = ldapPort
+ currentWizardActions.value.splice(currentWizardActions.value.indexOf('guessPortAndTLS'), 1)
}
/**
*
*/
-async function detectBaseDN() {
- // TODO
+async function guessBaseDN() {
+ currentWizardActions.value.push('guessBaseDN')
+ const { changes: { ldap_base: ldapBase } } = await callWizard('guessBaseDN', ldapConfigId)
+ ldapConfig.value.ldapBase = ldapBase
+ currentWizardActions.value.splice(currentWizardActions.value.indexOf('guessPortAndTLS'), 1)
}
/**
*
*/
-async function testBaseDN() {
- // TODO
+async function countInBaseDN() {
+ currentWizardActions.value.push('countInBaseDN')
+ const { changes: { ldap_test_base: ldapTestBase } } = await callWizard('countInBaseDN', ldapConfigId)
+ usersCount.value = ldapTestBase
+ currentWizardActions.value.splice(currentWizardActions.value.indexOf('guessPortAndTLS'), 1)
}
+
</script>
<style lang="scss" scoped>
.ldap-wizard__server {
import path from 'path'
import axios from '@nextcloud/axios'
-import { getAppRootUrl } from '@nextcloud/router'
+import { getAppRootUrl, generateOcsUrl } from '@nextcloud/router'
import type { LDAPConfig } from '../models'
-const API_ENDPOINT = path.join(getAppRootUrl('user_ldap'), '/api/v1/config')
+const APP_URL = getAppRootUrl('user_ldap')
+const AJAX_ENDPOINT = path.join(APP_URL, '/ajax')
+
+const OCS_APP_URL = generateOcsUrl('apps/user_ldap')
+const CONFIG_API_ENDPOINT = path.join(OCS_APP_URL, '/api/v1/config')
+
+console.log(OCS_APP_URL)
+console.log(CONFIG_API_ENDPOINT)
+
+type WizardAction =
+ 'guessPortAndTLS' |
+ 'guessBaseDN' |
+ 'detectEmailAttribute' |
+ 'detectUserDisplayNameAttribute' |
+ 'determineGroupMemberAssoc' |
+ 'determineUserObjectClasses' |
+ 'determineGroupObjectClasses' |
+ 'determineGroupsForUsers' |
+ 'determineGroupsForGroups' |
+ 'determineAttributes' |
+ 'getUserListFilter' |
+ 'getUserLoginFilter' |
+ 'getGroupFilter' |
+ 'countUsers' |
+ 'countGroups' |
+ 'countInBaseDN' |
+ 'testLoginName'
/**
*
* @param config
*/
-export async function createConfig(config: LDAPConfig): Promise<{configId: string, config: LDAPConfig}> {
- const response = await axios.post(API_ENDPOINT, config)
- return { configId: response.data.configId as string, config: response.data.config as LDAPConfig }
+export async function createConfig(
+ config: LDAPConfig,
+): Promise<{ configId: string; config: LDAPConfig }> {
+ const response = await axios.post(CONFIG_API_ENDPOINT, config)
+ return {
+ configId: response.data.configId as string,
+ config: response.data.config as LDAPConfig,
+ }
}
/**
* @param configId
* @param config
*/
-export async function updateConfig(configId: string, config: LDAPConfig): Promise<LDAPConfig> {
- const response = await axios.put(path.join(API_ENDPOINT, configId), config)
+export async function updateConfig(
+ configId: string,
+ config: LDAPConfig,
+): Promise<LDAPConfig> {
+ const response = await axios.put(
+ path.join(CONFIG_API_ENDPOINT, configId),
+ config,
+ )
return response.data as LDAPConfig
}
* @param configId
*/
export async function deleteConfig(configId: string): Promise<boolean> {
- await axios.delete(path.join(API_ENDPOINT, configId))
+ await axios.delete(path.join(CONFIG_API_ENDPOINT, configId))
return true
}
+
+/**
+ * Starts a configuration test.
+ * @param configId
+ */
+export async function testConfiguration(configId: string) {
+ const response = await axios.post(
+ path.join(AJAX_ENDPOINT, 'testConfiguration.php'),
+ undefined,
+ {
+ params: {
+ ldap_serverconfig_chooser: configId,
+ },
+ },
+ )
+
+ return response.data // TODO: check response content
+}
+
+/**
+ *
+ * @param subject
+ */
+export async function clearMapping(subject: 'user' | 'group') {
+ const response = await axios.post(
+ path.join(AJAX_ENDPOINT, 'clearMappings.php'),
+ undefined,
+ {
+ data: {
+ ldap_clear_mapping: subject,
+ },
+ },
+ )
+
+ return response.data // TODO: check response content
+}
+
+/**
+ * Calls the wizard endpoint.
+ * @param action
+ * @param configId
+ */
+export async function callWizard(action: WizardAction, configId: string) {
+ const params = new FormData()
+ params.set('action', action)
+ params.set('ldap_serverconfig_chooser', configId)
+ const response = await axios.post(
+ path.join(AJAX_ENDPOINT, 'wizard.php'),
+ params,
+ )
+
+ return response.data
+}
+++ /dev/null
- /**
- * starts a configuration test on the Nextcloud server
- */
- requestConfigurationTest: function() {
- var url = OC.generateUrl('apps/user_ldap/ajax/testConfiguration.php');
- var params = OC.buildQueryString({ldap_serverconfig_chooser: this.configID});
- var model = this;
- $.post(url, params, function(result) { model._processTestResult(model, result) });
- //TODO: make sure only one test is running at a time
- },
\ No newline at end of file
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { defineStore } from 'pinia'
-import Vue from 'vue'
+import Vue, { ref } from 'vue'
import { loadState } from '@nextcloud/initial-state'
import { createConfig, deleteConfig, updateConfig } from '../services/ldapConfigService'
import type { LDAPConfig } from '../models'
-/**
- *
- * @param {...any} args
- */
-export function useLDAPConfigStore(...args) {
- const store = defineStore('ldapconfig', {
- state: () => ({
- ldapConfigs: loadState('user_ldap', 'ldapConfigs') as Record<string, LDAPConfig>,
- defaultLdapConfig: loadState('user_ldap', 'ldapDefaultConfig') as LDAPConfig,
- }),
-
- actions: {
- async create() {
- const { configId, config } = await createConfig({ ...this.defaultLdapConfig })
- Vue.set(this.ldapConfigs, configId, config)
- },
-
- async copy(fromConfigId: string) {
- const { configId, config } = await createConfig({ ...this.ldapConfigs[fromConfigId] })
- Vue.set(this.ldapConfigs, configId, config)
- },
-
- async remove(configId: string) {
- const result = await deleteConfig(configId)
- if (result === true) {
- Vue.delete(this.ldapConfigs, configId)
- }
- },
-
- async update(configId: string, config: LDAPConfig) {
- config = await updateConfig(configId, config)
- Vue.set(this.ldapConfigs, configId, config)
- },
-
- async detectPort() {
- // TODO
- },
-
- async detectBaseDN() {
- // TODO
- },
-
- async testBaseDN() {
- // TODO
- },
- },
- })
-
- return store(...args)
-}
+export const useLDAPConfigStore = defineStore('ldapconfig', () => {
+ const ldapConfigs = ref(loadState('user_ldap', 'ldapConfigs') as Record<string, LDAPConfig>)
+ const defaultLdapConfig = ref(loadState('user_ldap', 'ldapDefaultConfig') as LDAPConfig)
+
+ /**
+ *
+ */
+ async function create() {
+ const { configId, config } = await createConfig({ ...defaultLdapConfig.value })
+ Vue.set(ldapConfigs, configId, config)
+ }
+
+ /**
+ *
+ * @param fromConfigId
+ */
+ async function copy(fromConfigId: string) {
+ const { configId, config } = await createConfig({ ...ldapConfigs[fromConfigId] })
+ Vue.set(ldapConfigs, configId, config)
+ }
+
+ /**
+ *
+ * @param configId
+ */
+ async function remove(configId: string) {
+ const result = await deleteConfig(configId)
+ if (result === true) {
+ Vue.delete(ldapConfigs, configId)
+ }
+ }
+
+ /**
+ *
+ * @param configId
+ * @param config
+ */
+ async function update(configId: string) {
+ const config = await updateConfig(configId, ldapConfigs[configId])
+ Vue.set(ldapConfigs, configId, config)
+ }
+
+ return {
+ ldapConfigs,
+ create,
+ copy,
+ remove,
+ update,
+ }
+})
</template>
<script lang="ts" setup>
-import { ref } from 'vue'
+import { computed, ref } from 'vue'
import Plus from 'vue-material-design-icons/Plus.vue'
label: `${configId}: ${config.ldapHost}`,
}))
+ldapConfigStore.$subscribe((mutation, state) => {
+ ldapConfigStore.update(selectedConfigId.value)
+ console.log('mutation', mutation, state)
+})
+
</script>
<style lang="scss" scoped>
.ldap-wizard {