Add LDAP page

Adds ability to enable LDAP service and modify LDAP and
ActiveDirectory properties.

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I59d65bba7f6fe321af395227ce2f7188d9c006b7
diff --git a/src/assets/styles/_form-components.scss b/src/assets/styles/_form-components.scss
index d1fe785..8d3ed9e 100644
--- a/src/assets/styles/_form-components.scss
+++ b/src/assets/styles/_form-components.scss
@@ -49,4 +49,8 @@
     color: $primary;
     fill: currentColor;
   }
+}
+
+.form-background {
+  background-color: $container-bgd;
 }
\ No newline at end of file
diff --git a/src/components/AppNavigation/AppNavigation.vue b/src/components/AppNavigation/AppNavigation.vue
index 94076de..2f56c28 100644
--- a/src/components/AppNavigation/AppNavigation.vue
+++ b/src/components/AppNavigation/AppNavigation.vue
@@ -75,7 +75,7 @@
               <icon-expand class="icon-expand" />
             </b-button>
             <b-collapse id="access-control-menu" tag="ul" class="nav-item__nav">
-              <b-nav-item href="javascript:void(0)">
+              <b-nav-item to="/access-control/ldap">
                 {{ $t('appNavigation.ldap') }}
               </b-nav-item>
               <b-nav-item to="/access-control/local-user-management">
diff --git a/src/locales/en-US.json b/src/locales/en-US.json
index 022eefb..d44cc07 100644
--- a/src/locales/en-US.json
+++ b/src/locales/en-US.json
@@ -13,6 +13,7 @@
       "filter": "Filter",
       "replace": "Replace",
       "save": "Save",
+      "saveSettings": "Save settings",
       "selected": "Selected"
     },
     "ariaLabel": {
@@ -89,6 +90,36 @@
     "sslCertificates": "SSL Certificates",
     "unauthorized": "Unauthorized"
   },
+  "pageLdap": {
+    "pageDescription": "Configure LDAP settings and manage role groups",
+    "settings": "Settings",
+    "ariaLabel": {
+      "ldapSettings": "LDAP settings"
+    },
+    "form": {
+      "baseDn": "Base DN",
+      "bindDn": "Bind DN",
+      "bindPassword": "Bind password",
+      "caCertificateValidUntil": "CA Certificate valid until",
+      "groupIdAttribute": "Group ID attribute",
+      "ldapAuthentication": "LDAP authentication",
+      "ldapAuthenticationHelper": "Must be enabled to modify role groups",
+      "ldapCertificateValidUntil": "LDAP Certificate valid until",
+      "manageSslCertificates": "Manage SSL certificates",
+      "secureLdapHelper": "A CA certificate and an LDAP certificate are required to enable secure LDAP",
+      "secureLdapUsingSsl": "Secure LDAP using SSL",
+      "serverUri": "Server URI",
+      "serverUriTooltip": "Enabling Secure LDAP changes URI scheme to ldaps",
+      "serviceType": "Service type",
+      "userIdAttribute": "User ID attribute"
+    },
+    "toast": {
+      "errorSaveActiveDirectorySettings": "Error saving Active Directory settings.",
+      "errorSaveLdapSettings": "Error saving Open LDAP settings.",
+      "successSaveActiveDirectorySettings": "Successfully saved Active Directory settings.",
+      "successSaveLdapSettings": "Successfully saved Open LDAP settings."
+    }
+  },
   "pageLocalUserManagement": {
     "accountPolicySettings": "Account policy settings",
     "addUser": "Add user",
diff --git a/src/main.js b/src/main.js
index 5adc5ef..b59afd9 100644
--- a/src/main.js
+++ b/src/main.js
@@ -17,6 +17,7 @@
   FormRadioPlugin,
   FormSelectPlugin,
   FormTagsPlugin,
+  InputGroupPlugin,
   LayoutPlugin,
   LinkPlugin,
   ListGroupPlugin,
@@ -82,6 +83,7 @@
 Vue.use(FormRadioPlugin);
 Vue.use(FormSelectPlugin);
 Vue.use(FormTagsPlugin);
+Vue.use(InputGroupPlugin);
 Vue.use(LayoutPlugin);
 Vue.use(LayoutPlugin);
 Vue.use(LinkPlugin);
diff --git a/src/router/index.js b/src/router/index.js
index 2af53ea..6e95834 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -31,6 +31,14 @@
         }
       },
       {
+        path: '/access-control/ldap',
+        name: 'ldap',
+        component: () => import('@/views/AccessControl/Ldap'),
+        meta: {
+          title: 'appPageTitle.ldap'
+        }
+      },
+      {
         path: '/access-control/local-user-management',
         name: 'local-users',
         component: () => import('@/views/AccessControl/LocalUserManagement'),
diff --git a/src/store/index.js b/src/store/index.js
index 0180213..364e16c 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -3,6 +3,7 @@
 
 import GlobalStore from './modules/GlobalStore';
 import AuthenticationStore from './modules/Authentication/AuthenticanStore';
+import LdapStore from './modules/AccessControl/LdapStore';
 import LocalUserManagementStore from './modules/AccessControl/LocalUserMangementStore';
 import SslCertificatesStore from './modules/AccessControl/SslCertificatesStore';
 import OverviewStore from './modules/Overview/OverviewStore';
@@ -25,6 +26,7 @@
   modules: {
     global: GlobalStore,
     authentication: AuthenticationStore,
+    ldap: LdapStore,
     localUsers: LocalUserManagementStore,
     overview: OverviewStore,
     firmware: FirmwareStore,
diff --git a/src/store/modules/AccessControl/LdapStore.js b/src/store/modules/AccessControl/LdapStore.js
new file mode 100644
index 0000000..54fbbcc
--- /dev/null
+++ b/src/store/modules/AccessControl/LdapStore.js
@@ -0,0 +1,156 @@
+import api from '@/store/api';
+import i18n from '@/i18n';
+
+const LdapStore = {
+  namespaced: true,
+  state: {
+    isServiceEnabled: null,
+    ldap: {
+      serviceEnabled: null,
+      serviceAddress: null,
+      bindDn: null,
+      baseDn: null,
+      userAttribute: null,
+      groupsAttribute: null
+    },
+    activeDirectory: {
+      serviceEnabled: null,
+      serviceAddress: null,
+      bindDn: null,
+      baseDn: null,
+      userAttribute: null,
+      groupsAttribute: null
+    }
+  },
+  getters: {
+    isServiceEnabled: state => state.isServiceEnabled,
+    ldap: state => state.ldap,
+    activeDirectory: state => state.activeDirectory
+  },
+  mutations: {
+    setServiceEnabled: (state, serviceEnabled) =>
+      (state.isServiceEnabled = serviceEnabled),
+    setLdapProperties: (
+      state,
+      {
+        ServiceEnabled,
+        ServiceAddresses,
+        Authentication = {},
+        LDAPService: { SearchSettings = {} } = {}
+      }
+    ) => {
+      state.ldap.serviceAddress = ServiceAddresses[0];
+      state.ldap.serviceEnabled = ServiceEnabled;
+      state.ldap.baseDn = SearchSettings.BaseDistinguishedNames[0];
+      state.ldap.bindDn = Authentication.Username;
+      state.ldap.userAttribute = SearchSettings.UsernameAttribute;
+      state.ldap.groupsAttribute = SearchSettings.GroupsAttribute;
+    },
+    setActiveDirectoryProperties: (
+      state,
+      {
+        ServiceEnabled,
+        ServiceAddresses,
+        Authentication = {},
+        LDAPService: { SearchSettings = {} } = {}
+      }
+    ) => {
+      state.activeDirectory.serviceEnabled = ServiceEnabled;
+      state.activeDirectory.serviceAddress = ServiceAddresses[0];
+      state.activeDirectory.bindDn = Authentication.Username;
+      state.activeDirectory.baseDn = SearchSettings.BaseDistinguishedNames[0];
+      state.activeDirectory.userAttribute = SearchSettings.UsernameAttribute;
+      state.activeDirectory.groupsAttribute = SearchSettings.GroupsAttribute;
+    }
+  },
+  actions: {
+    getAccountSettings({ commit }) {
+      api
+        .get('/redfish/v1/AccountService')
+        .then(({ data: { LDAP = {}, ActiveDirectory = {} } }) => {
+          const ldapEnabled = LDAP.ServiceEnabled;
+          const activeDirectoryEnabled = ActiveDirectory.ServiceEnabled;
+
+          commit('setServiceEnabled', ldapEnabled || activeDirectoryEnabled);
+          commit('setLdapProperties', LDAP);
+          commit('setActiveDirectoryProperties', ActiveDirectory);
+        })
+        .catch(error => console.log(error));
+    },
+    async saveLdapSettings({ state, dispatch }, properties) {
+      const data = { LDAP: properties };
+      if (state.activeDirectory.serviceEnabled) {
+        // Disable Active Directory service if enabled
+        await api.patch('/redfish/v1/AccountService', {
+          ActiveDirectory: { ServiceEnabled: false }
+        });
+      }
+      return await api
+        .patch('/redfish/v1/AccountService', data)
+        .then(() => dispatch('getAccountSettings'))
+        .then(() => i18n.t('pageLdap.toast.successSaveLdapSettings'))
+        .catch(error => {
+          console.log(error);
+          throw new Error(i18n.t('pageLdap.toast.errorSaveLdapSettings'));
+        });
+    },
+    async saveActiveDirectorySettings({ state, dispatch }, properties) {
+      const data = { ActiveDirectory: properties };
+      if (state.ldap.serviceEnabled) {
+        // Disable LDAP service if enabled
+        await api.patch('/redfish/v1/AccountService', {
+          LDAP: { ServiceEnabled: false }
+        });
+      }
+      return await api
+        .patch('/redfish/v1/AccountService', data)
+        .then(() => dispatch('getAccountSettings'))
+        .then(() => i18n.t('pageLdap.toast.successSaveActiveDirectorySettings'))
+        .catch(error => {
+          console.log(error);
+          throw new Error(
+            i18n.t('pageLdap.toast.errorSaveActiveDirectorySettings')
+          );
+        });
+    },
+    async saveAccountSettings(
+      { dispatch },
+      {
+        serviceEnabled,
+        serviceAddress,
+        activeDirectoryEnabled,
+        bindDn,
+        bindPassword,
+        baseDn,
+        userIdAttribute,
+        groupIdAttribute
+      }
+    ) {
+      const data = {
+        ServiceEnabled: serviceEnabled,
+        ServiceAddresses: [serviceAddress],
+        Authentication: {
+          Username: bindDn,
+          Password: bindPassword
+        },
+        LDAPService: {
+          SearchSettings: {
+            BaseDistinguishedNames: [baseDn]
+          }
+        }
+      };
+      if (groupIdAttribute)
+        data.LDAPService.SearchSettings.GroupsAttribute = groupIdAttribute;
+      if (userIdAttribute)
+        data.LDAPService.SearchSettings.UsernameAttribute = userIdAttribute;
+
+      if (activeDirectoryEnabled) {
+        return await dispatch('saveActiveDirectorySettings', data);
+      } else {
+        return await dispatch('saveLdapSettings', data);
+      }
+    }
+  }
+};
+
+export default LdapStore;
diff --git a/src/views/AccessControl/Ldap/Ldap.vue b/src/views/AccessControl/Ldap/Ldap.vue
new file mode 100644
index 0000000..c2d0e34
--- /dev/null
+++ b/src/views/AccessControl/Ldap/Ldap.vue
@@ -0,0 +1,399 @@
+<template>
+  <b-container fluid="xl">
+    <page-title :description="$t('pageLdap.pageDescription')" />
+    <page-section :section-title="$t('pageLdap.settings')">
+      <b-form novalidate @submit.prevent="handleSubmit">
+        <b-row>
+          <b-col>
+            <b-form-group
+              class="mb-3"
+              :label="$t('pageLdap.form.ldapAuthentication')"
+            >
+              <b-form-text id="enable-ldap-auth-help-block">
+                {{ $t('pageLdap.form.ldapAuthenticationHelper') }}
+              </b-form-text>
+              <b-form-checkbox
+                id="enable-ldap-auth"
+                v-model="form.ldapAuthenticationEnabled"
+                aria-describedby="enable-ldap-auth-help-block"
+                @change="onChangeldapAuthenticationEnabled"
+              >
+                {{ $t('global.action.enable') }}
+              </b-form-checkbox>
+            </b-form-group>
+          </b-col>
+        </b-row>
+        <div class="form-background p-3">
+          <b-form-group
+            class="m-0"
+            :label="$t('pageLdap.ariaLabel.ldapSettings')"
+            label-class="sr-only"
+            :disabled="!form.ldapAuthenticationEnabled"
+          >
+            <b-row>
+              <b-col md="3" lg="4" xl="3">
+                <b-form-group
+                  class="mb-4"
+                  :label="$t('pageLdap.form.secureLdapUsingSsl')"
+                >
+                  <b-form-text id="enable-secure-help-block">
+                    {{ $t('pageLdap.form.secureLdapHelper') }}
+                  </b-form-text>
+                  <b-form-checkbox
+                    id="enable-secure-ldap"
+                    v-model="form.secureLdapEnabled"
+                    aria-describedby="enable-secure-help-block"
+                    :disabled="
+                      !caCertificateExpiration || !ldapCertificateExpiration
+                    "
+                    @change="$v.form.secureLdapEnabled.$touch()"
+                  >
+                    {{ $t('global.action.enable') }}
+                  </b-form-checkbox>
+                </b-form-group>
+                <dl>
+                  <dt>{{ $t('pageLdap.form.caCertificateValidUntil') }}</dt>
+                  <dd v-if="caCertificateExpiration">
+                    {{ caCertificateExpiration | formatDate }}
+                  </dd>
+                  <dd v-else>--</dd>
+                  <dt>{{ $t('pageLdap.form.ldapCertificateValidUntil') }}</dt>
+                  <dd v-if="ldapCertificateExpiration">
+                    {{ ldapCertificateExpiration | formatDate }}
+                  </dd>
+                  <dd v-else>--</dd>
+                </dl>
+                <b-link
+                  class="d-inline-block mb-4 m-md-0"
+                  to="/access-control/ssl-certificates"
+                >
+                  {{ $t('pageLdap.form.manageSslCertificates') }}
+                </b-link>
+              </b-col>
+              <b-col md="9" lg="8" xl="9">
+                <b-row>
+                  <b-col>
+                    <b-form-group :label="$t('pageLdap.form.serviceType')">
+                      <b-form-radio
+                        v-model="form.activeDirectoryEnabled"
+                        :value="false"
+                        @change="onChangeServiceType"
+                      >
+                        OpenLDAP
+                      </b-form-radio>
+                      <b-form-radio
+                        v-model="form.activeDirectoryEnabled"
+                        :value="true"
+                        @change="onChangeServiceType"
+                      >
+                        Active Directory
+                      </b-form-radio>
+                    </b-form-group>
+                  </b-col>
+                </b-row>
+                <b-row>
+                  <b-col sm="6" xl="4">
+                    <b-form-group label-for="server-uri">
+                      <template v-slot:label>
+                        {{ $t('pageLdap.form.serverUri') }}
+                        <info-tooltip
+                          :title="$t('pageLdap.form.serverUriTooltip')"
+                        />
+                      </template>
+                      <b-input-group :prepend="ldapProtocol">
+                        <b-form-input
+                          id="server-uri"
+                          v-model="form.serverUri"
+                          :state="getValidationState($v.form.serverUri)"
+                          @change="$v.form.serverUri.$touch()"
+                        />
+                        <b-form-invalid-feedback role="alert">
+                          {{ $t('global.form.fieldRequired') }}
+                        </b-form-invalid-feedback>
+                      </b-input-group>
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group
+                      :label="$t('pageLdap.form.bindDn')"
+                      label-for="bind-dn"
+                    >
+                      <b-form-input
+                        id="bind-dn"
+                        v-model="form.bindDn"
+                        :state="getValidationState($v.form.bindDn)"
+                        @change="$v.form.bindDn.$touch()"
+                      />
+                      <b-form-invalid-feedback role="alert">
+                        {{ $t('global.form.fieldRequired') }}
+                      </b-form-invalid-feedback>
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group
+                      :label="$t('pageLdap.form.bindPassword')"
+                      label-for="bind-password"
+                    >
+                      <input-password-toggle>
+                        <b-form-input
+                          id="bind-password"
+                          v-model="form.bindPassword"
+                          type="password"
+                          :state="getValidationState($v.form.bindPassword)"
+                          @change="$v.form.bindPassword.$touch()"
+                        />
+                        <b-form-invalid-feedback role="alert">
+                          {{ $t('global.form.fieldRequired') }}
+                        </b-form-invalid-feedback>
+                      </input-password-toggle>
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group
+                      :label="$t('pageLdap.form.baseDn')"
+                      label-for="base-dn"
+                    >
+                      <b-form-input
+                        id="base-dn"
+                        v-model="form.baseDn"
+                        :state="getValidationState($v.form.baseDn)"
+                        @change="$v.form.baseDn.$touch()"
+                      />
+                      <b-form-invalid-feedback role="alert">
+                        {{ $t('global.form.fieldRequired') }}
+                      </b-form-invalid-feedback>
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group
+                      :label="$t('pageLdap.form.userIdAttribute')"
+                      label-for="user-id-attribute"
+                    >
+                      <b-form-input
+                        id="user-id-attribute"
+                        v-model="form.userIdAttribute"
+                        @change="$v.form.userIdAttribute.$touch()"
+                      />
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group
+                      :label="$t('pageLdap.form.groupIdAttribute')"
+                      label-for="group-id-attribute"
+                    >
+                      <b-form-input
+                        id="group-id-attribute"
+                        v-model="form.groupIdAttribute"
+                        @change="$v.form.groupIdAttribute.$touch()"
+                      />
+                    </b-form-group>
+                  </b-col>
+                </b-row>
+              </b-col>
+            </b-row>
+          </b-form-group>
+        </div>
+        <b-row class="mt-4">
+          <b-col>
+            <b-btn
+              variant="primary"
+              type="submit"
+              :disabled="!$v.form.$anyDirty"
+            >
+              {{ $t('global.action.saveSettings') }}
+            </b-btn>
+          </b-col>
+        </b-row>
+      </b-form>
+    </page-section>
+  </b-container>
+</template>
+
+<script>
+import { mapGetters } from 'vuex';
+import { find } from 'lodash';
+import { requiredIf } from 'vuelidate/lib/validators';
+
+import BVToastMixin from '@/components/Mixins/BVToastMixin.js';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+import PageTitle from '@/components/Global/PageTitle';
+import PageSection from '@/components/Global/PageSection';
+import InfoTooltip from '@/components/Global/InfoTooltip';
+import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
+
+export default {
+  name: 'Ldap',
+  components: { InfoTooltip, InputPasswordToggle, PageTitle, PageSection },
+  mixins: [BVToastMixin, VuelidateMixin],
+  data() {
+    return {
+      form: {
+        ldapAuthenticationEnabled: false,
+        secureLdapEnabled: false,
+        activeDirectoryEnabled: false,
+        serverUri: '',
+        bindDn: '',
+        bindPassword: '',
+        baseDn: '',
+        userIdAttribute: '',
+        groupIdAttribute: ''
+      }
+    };
+  },
+  computed: {
+    ...mapGetters('ldap', ['isServiceEnabled', 'ldap', 'activeDirectory']),
+    sslCertificates() {
+      return this.$store.getters['sslCertificates/allCertificates'];
+    },
+    caCertificateExpiration() {
+      const caCertificate = find(this.sslCertificates, {
+        type: 'TrustStore Certificate'
+      });
+      if (caCertificate === undefined) return null;
+      return caCertificate.validUntil;
+    },
+    ldapCertificateExpiration() {
+      const ldapCertificate = find(this.sslCertificates, {
+        type: 'LDAP Certificate'
+      });
+      if (ldapCertificate === undefined) return null;
+      return ldapCertificate.validUntil;
+    },
+    ldapProtocol() {
+      return this.form.secureLdapEnabled ? 'ldaps://' : 'ldap://';
+    }
+  },
+  watch: {
+    isServiceEnabled: function(value) {
+      this.form.ldapAuthenticationEnabled = value;
+    },
+    ldap: {
+      handler: function(value) {
+        if (value.serviceEnabled || !this.form.activeDirectoryEnabled) {
+          this.setFormValues(value);
+        }
+      },
+      deep: true
+    },
+    activeDirectory: {
+      handler: function(value) {
+        if (value.serviceEnabled) {
+          this.form.activeDirectoryEnabled = true;
+          this.setFormValues(value);
+        }
+      },
+      deep: true
+    }
+  },
+  validations: {
+    form: {
+      ldapAuthenticationEnabled: {},
+      secureLdapEnabled: {},
+      activeDirectoryEnabled: {
+        required: requiredIf(function() {
+          return this.form.ldapAuthenticationEnabled;
+        })
+      },
+      serverUri: {
+        required: requiredIf(function() {
+          return this.form.ldapAuthenticationEnabled;
+        })
+      },
+      bindDn: {
+        required: requiredIf(function() {
+          return this.form.ldapAuthenticationEnabled;
+        })
+      },
+      bindPassword: {
+        required: requiredIf(function() {
+          return this.form.ldapAuthenticationEnabled;
+        })
+      },
+      baseDn: {
+        required: requiredIf(function() {
+          return this.form.ldapAuthenticationEnabled;
+        })
+      },
+      userIdAttribute: {},
+      groupIdAttribute: {}
+    }
+  },
+  created() {
+    this.$store.dispatch('ldap/getAccountSettings');
+    this.$store.dispatch('sslCertificates/getCertificates');
+    if (this.form.activeDirectoryEnabled) {
+      this.setFormValues(this.activeDirectory);
+    } else {
+      this.setFormValues(this.ldap);
+    }
+  },
+  methods: {
+    setFormValues({
+      serviceAddress = '',
+      bindDn = '',
+      baseDn = '',
+      userAttribute = '',
+      groupsAttribute = ''
+    }) {
+      const secureLdap =
+        serviceAddress && serviceAddress.includes('ldaps://') ? true : false;
+      const serverUri = serviceAddress
+        ? serviceAddress.replace(/ldaps?:\/\//, '')
+        : '';
+      this.form.secureLdapEnabled = secureLdap;
+      this.form.serverUri = serverUri;
+      this.form.bindDn = bindDn;
+      this.form.bindPassword = '';
+      this.form.baseDn = baseDn;
+      this.form.userIdAttribute = userAttribute;
+      this.form.groupIdAttribute = groupsAttribute;
+    },
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      const data = {
+        serviceEnabled: this.form.ldapAuthenticationEnabled,
+        activeDirectoryEnabled: this.form.activeDirectoryEnabled,
+        serviceAddress: `${this.ldapProtocol}${this.form.serverUri}`,
+        bindDn: this.form.bindDn,
+        bindPassword: this.form.bindPassword,
+        baseDn: this.form.baseDn,
+        userIdAttribute: this.form.userIdAttribute,
+        groupIdAttribute: this.form.groupIdAttribute
+      };
+      this.$store
+        .dispatch('ldap/saveAccountSettings', data)
+        .then(success => {
+          this.successToast(success);
+          this.$v.form.$reset();
+        })
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => {
+          this.form.bindPassword = '';
+        });
+    },
+    onChangeServiceType(isActiveDirectoryEnabled) {
+      this.$v.form.activeDirectoryEnabled.$touch();
+      const serviceType = isActiveDirectoryEnabled
+        ? this.activeDirectory
+        : this.ldap;
+      this.setFormValues(serviceType);
+    },
+    onChangeldapAuthenticationEnabled(isServiceEnabled) {
+      this.$v.form.ldapAuthenticationEnabled.$touch();
+      if (!isServiceEnabled) {
+        // Request will fail if sent with empty values.
+        // The frontend only checks for required fields
+        // when the service is enabled. This is to prevent
+        // an error if a user clears any properties then
+        // disables the service.
+        if (this.form.activeDirectoryEnabled) {
+          this.setFormValues(this.activeDirectory);
+        } else {
+          this.setFormValues(this.ldap);
+        }
+      }
+    }
+  }
+};
+</script>
diff --git a/src/views/AccessControl/Ldap/index.js b/src/views/AccessControl/Ldap/index.js
new file mode 100644
index 0000000..6ae3abf
--- /dev/null
+++ b/src/views/AccessControl/Ldap/index.js
@@ -0,0 +1,2 @@
+import Ldap from './Ldap.vue';
+export default Ldap;