IA update: Update access and control section

This is the fifth commit of the information architecture changes and
has the following changes:

- The icon for access and control has been updated
- Access and control section has been updated to security and
access section
- Security settings page has been updated to policies page and moved to
security and access section
- Client sessions page has been updated to sessions page
- Local user management page has been updated to user management page
- SSL certificates page has been updated to certificates page

Signed-off-by: Sandeepa Singh <sandeepa.singh@ibm.com>
Change-Id: Ie93cee9002742ecf7d33615636f4f159f4395fc4
diff --git a/src/views/SecurityAndAccess/Ldap/Ldap.vue b/src/views/SecurityAndAccess/Ldap/Ldap.vue
new file mode 100644
index 0000000..1f2108d
--- /dev/null
+++ b/src/views/SecurityAndAccess/Ldap/Ldap.vue
@@ -0,0 +1,435 @@
+<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')"
+              :disabled="loading"
+            >
+              <b-form-checkbox
+                v-model="form.ldapAuthenticationEnabled"
+                data-test-id="ldap-checkbox-ldapAuthenticationEnabled"
+                @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 || loading"
+          >
+            <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"
+                    data-test-id="ldap-checkbox-secureLdapEnabled"
+                    :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="/security-and-access/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"
+                        data-test-id="ldap-radio-activeDirectoryEnabled"
+                        :value="false"
+                        @change="onChangeServiceType"
+                      >
+                        OpenLDAP
+                      </b-form-radio>
+                      <b-form-radio
+                        v-model="form.activeDirectoryEnabled"
+                        data-test-id="ldap-radio-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 #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"
+                          data-test-id="ldap-input-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"
+                        data-test-id="ldap-input-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
+                        data-test-id="ldap-input-togglePassword"
+                      >
+                        <b-form-input
+                          id="bind-password"
+                          v-model="form.bindPassword"
+                          type="password"
+                          :state="getValidationState($v.form.bindPassword)"
+                          class="form-control-with-button"
+                          @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"
+                        data-test-id="ldap-input-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-for="user-id-attribute">
+                      <template #label>
+                        {{ $t('pageLdap.form.userIdAttribute') }} -
+                        <span class="form-text d-inline">
+                          {{ $t('global.form.optional') }}
+                        </span>
+                      </template>
+                      <b-form-input
+                        id="user-id-attribute"
+                        v-model="form.userIdAttribute"
+                        data-test-id="ldap-input-userIdAttribute"
+                        @change="$v.form.userIdAttribute.$touch()"
+                      />
+                    </b-form-group>
+                  </b-col>
+                  <b-col sm="6" xl="4">
+                    <b-form-group label-for="group-id-attribute">
+                      <template #label>
+                        {{ $t('pageLdap.form.groupIdAttribute') }} -
+                        <span class="form-text d-inline">
+                          {{ $t('global.form.optional') }}
+                        </span>
+                      </template>
+                      <b-form-input
+                        id="group-id-attribute"
+                        v-model="form.groupIdAttribute"
+                        data-test-id="ldap-input-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 mb-5">
+          <b-col>
+            <b-btn
+              variant="primary"
+              type="submit"
+              data-test-id="ldap-button-saveSettings"
+              :disabled="loading"
+            >
+              {{ $t('global.action.saveSettings') }}
+            </b-btn>
+          </b-col>
+        </b-row>
+      </b-form>
+    </page-section>
+
+    <!-- Role groups -->
+    <page-section :section-title="$t('pageLdap.roleGroups')">
+      <table-role-groups />
+    </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';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
+import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
+import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
+import PageTitle from '@/components/Global/PageTitle';
+import PageSection from '@/components/Global/PageSection';
+import InfoTooltip from '@/components/Global/InfoTooltip';
+import TableRoleGroups from './TableRoleGroups';
+
+export default {
+  name: 'Ldap',
+  components: {
+    InfoTooltip,
+    InputPasswordToggle,
+    PageTitle,
+    PageSection,
+    TableRoleGroups,
+  },
+  mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
+  beforeRouteLeave(to, from, next) {
+    this.hideLoader();
+    next();
+  },
+  data() {
+    return {
+      form: {
+        ldapAuthenticationEnabled: this.$store.getters['ldap/isServiceEnabled'],
+        secureLdapEnabled: false,
+        activeDirectoryEnabled: this.$store.getters[
+          'ldap/isActiveDirectoryEnabled'
+        ],
+        serverUri: '',
+        bindDn: '',
+        bindPassword: '',
+        baseDn: '',
+        userIdAttribute: '',
+        groupIdAttribute: '',
+        loading,
+      },
+    };
+  },
+  computed: {
+    ...mapGetters('ldap', [
+      'isServiceEnabled',
+      'isActiveDirectoryEnabled',
+      'ldap',
+      'activeDirectory',
+    ]),
+    sslCertificates() {
+      return this.$store.getters['certificates/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;
+    },
+    isActiveDirectoryEnabled: function (value) {
+      this.form.activeDirectoryEnabled = value;
+      this.setFormValues();
+    },
+  },
+  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.startLoader();
+    this.$store
+      .dispatch('ldap/getAccountSettings')
+      .finally(() => this.endLoader());
+    this.$store
+      .dispatch('certificates/getCertificates')
+      .finally(() => this.endLoader());
+    this.setFormValues();
+  },
+  methods: {
+    setFormValues(serviceType) {
+      if (!serviceType) {
+        serviceType = this.isActiveDirectoryEnabled
+          ? this.activeDirectory
+          : this.ldap;
+      }
+      const {
+        serviceAddress = '',
+        bindDn = '',
+        baseDn = '',
+        userAttribute = '',
+        groupsAttribute = '',
+      } = serviceType;
+      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.startLoader();
+      this.$store
+        .dispatch('ldap/saveAccountSettings', data)
+        .then((success) => {
+          this.successToast(success);
+        })
+        .catch(({ message }) => {
+          this.errorToast(message);
+        })
+        .finally(() => {
+          this.form.bindPassword = '';
+          this.$v.form.$reset();
+          this.endLoader();
+        });
+    },
+    onChangeServiceType(isActiveDirectoryEnabled) {
+      this.$v.form.activeDirectoryEnabled.$touch();
+      const serviceType = isActiveDirectoryEnabled
+        ? this.activeDirectory
+        : this.ldap;
+      // Set form values according to user selected
+      // service type
+      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.
+        this.setFormValues();
+      }
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue b/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue
new file mode 100644
index 0000000..6ea2561
--- /dev/null
+++ b/src/views/SecurityAndAccess/Ldap/ModalAddRoleGroup.vue
@@ -0,0 +1,164 @@
+<template>
+  <b-modal id="modal-role-group" ref="modal" @ok="onOk" @hidden="resetForm">
+    <template #modal-title>
+      <template v-if="roleGroup">
+        {{ $t('pageLdap.modal.editRoleGroup') }}
+      </template>
+      <template v-else>
+        {{ $t('pageLdap.modal.addNewRoleGroup') }}
+      </template>
+    </template>
+    <b-container>
+      <b-row>
+        <b-col sm="8">
+          <b-form id="role-group" @submit.prevent="handleSubmit">
+            <!-- Edit role group -->
+            <template v-if="roleGroup !== null">
+              <dl class="mb-4">
+                <dt>{{ $t('pageLdap.modal.groupName') }}</dt>
+                <dd>{{ form.groupName }}</dd>
+              </dl>
+            </template>
+
+            <!-- Add new role group -->
+            <template v-else>
+              <b-form-group
+                :label="$t('pageLdap.modal.groupName')"
+                label-for="role-group-name"
+              >
+                <b-form-input
+                  id="role-group-name"
+                  v-model="form.groupName"
+                  :state="getValidationState($v.form.groupName)"
+                  @input="$v.form.groupName.$touch()"
+                />
+                <b-form-invalid-feedback role="alert">
+                  {{ $t('global.form.fieldRequired') }}
+                </b-form-invalid-feedback>
+              </b-form-group>
+            </template>
+
+            <b-form-group
+              :label="$t('pageLdap.modal.groupPrivilege')"
+              label-for="privilege"
+            >
+              <b-form-select
+                id="privilege"
+                v-model="form.groupPrivilege"
+                :options="accountRoles"
+                :state="getValidationState($v.form.groupPrivilege)"
+                @input="$v.form.groupPrivilege.$touch()"
+              >
+                <template v-if="!roleGroup" #first>
+                  <b-form-select-option :value="null" disabled>
+                    {{ $t('global.form.selectAnOption') }}
+                  </b-form-select-option>
+                </template>
+              </b-form-select>
+              <b-form-invalid-feedback role="alert">
+                {{ $t('global.form.fieldRequired') }}
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-form>
+        </b-col>
+      </b-row>
+    </b-container>
+    <template #modal-footer="{ cancel }">
+      <b-button variant="secondary" @click="cancel()">
+        {{ $t('global.action.cancel') }}
+      </b-button>
+      <b-button form="role-group" type="submit" variant="primary" @click="onOk">
+        <template v-if="roleGroup">
+          {{ $t('global.action.save') }}
+        </template>
+        <template v-else>
+          {{ $t('global.action.add') }}
+        </template>
+      </b-button>
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import { required, requiredIf } from 'vuelidate/lib/validators';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+export default {
+  mixins: [VuelidateMixin],
+  props: {
+    roleGroup: {
+      type: Object,
+      default: null,
+      validator: (prop) => {
+        if (prop === null) return true;
+        return (
+          Object.prototype.hasOwnProperty.call(prop, 'groupName') &&
+          Object.prototype.hasOwnProperty.call(prop, 'groupPrivilege')
+        );
+      },
+    },
+  },
+  data() {
+    return {
+      form: {
+        groupName: null,
+        groupPrivilege: null,
+      },
+    };
+  },
+  computed: {
+    accountRoles() {
+      return this.$store.getters['userManagement/accountRoles'];
+    },
+  },
+  watch: {
+    roleGroup: function (value) {
+      if (value === null) return;
+      this.form.groupName = value.groupName;
+      this.form.groupPrivilege = value.groupPrivilege;
+    },
+  },
+  validations() {
+    return {
+      form: {
+        groupName: {
+          required: requiredIf(function () {
+            return !this.roleGroup;
+          }),
+        },
+        groupPrivilege: {
+          required,
+        },
+      },
+    };
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$emit('ok', {
+        addNew: !this.roleGroup,
+        groupName: this.form.groupName,
+        groupPrivilege: this.form.groupPrivilege,
+      });
+      this.closeModal();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      this.form.groupName = null;
+      this.form.groupPrivilege = null;
+      this.$v.$reset();
+      this.$emit('hidden');
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue b/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue
new file mode 100644
index 0000000..6e23111
--- /dev/null
+++ b/src/views/SecurityAndAccess/Ldap/TableRoleGroups.vue
@@ -0,0 +1,265 @@
+<template>
+  <div>
+    <b-row>
+      <b-col md="9">
+        <alert :show="isServiceEnabled === false" variant="info">
+          {{ $t('pageLdap.tableRoleGroups.alertContent') }}
+        </alert>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col class="text-right" md="9">
+        <b-btn
+          variant="primary"
+          :disabled="!isServiceEnabled"
+          @click="initRoleGroupModal(null)"
+        >
+          <icon-add />
+          {{ $t('pageLdap.addRoleGroup') }}
+        </b-btn>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col md="9">
+        <table-toolbar
+          ref="toolbar"
+          :selected-items-count="selectedRows.length"
+          :actions="batchActions"
+          @clear-selected="clearSelectedRows($refs.table)"
+          @batch-action="onBatchAction"
+        />
+        <b-table
+          ref="table"
+          responsive
+          selectable
+          show-empty
+          no-select-on-click
+          hover
+          no-sort-reset
+          sort-icon-left
+          :items="tableItems"
+          :fields="fields"
+          :empty-text="$t('global.table.emptyMessage')"
+          @row-selected="onRowSelected($event, tableItems.length)"
+        >
+          <!-- Checkbox column -->
+          <template #head(checkbox)>
+            <b-form-checkbox
+              v-model="tableHeaderCheckboxModel"
+              :indeterminate="tableHeaderCheckboxIndeterminate"
+              :disabled="!isServiceEnabled"
+              @change="onChangeHeaderCheckbox($refs.table)"
+            >
+              <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
+            </b-form-checkbox>
+          </template>
+          <template #cell(checkbox)="row">
+            <b-form-checkbox
+              v-model="row.rowSelected"
+              :disabled="!isServiceEnabled"
+              @change="toggleSelectRow($refs.table, row.index)"
+            >
+              <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+            </b-form-checkbox>
+          </template>
+
+          <!-- table actions column -->
+          <template #cell(actions)="{ item }">
+            <table-row-action
+              v-for="(action, index) in item.actions"
+              :key="index"
+              :value="action.value"
+              :enabled="action.enabled"
+              :title="action.title"
+              @click-table-action="onTableRowAction($event, item)"
+            >
+              <template #icon>
+                <icon-edit v-if="action.value === 'edit'" />
+                <icon-trashcan v-if="action.value === 'delete'" />
+              </template>
+            </table-row-action>
+          </template>
+        </b-table>
+      </b-col>
+    </b-row>
+    <modal-add-role-group
+      :role-group="activeRoleGroup"
+      @ok="saveRoleGroup"
+      @hidden="activeRoleGroup = null"
+    />
+  </div>
+</template>
+
+<script>
+import IconEdit from '@carbon/icons-vue/es/edit/20';
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import { mapGetters } from 'vuex';
+
+import Alert from '@/components/Global/Alert';
+import TableToolbar from '@/components/Global/TableToolbar';
+import TableRowAction from '@/components/Global/TableRowAction';
+import BVTableSelectableMixin, {
+  selectedRows,
+  tableHeaderCheckboxModel,
+  tableHeaderCheckboxIndeterminate,
+} from '@/components/Mixins/BVTableSelectableMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import ModalAddRoleGroup from './ModalAddRoleGroup';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+
+export default {
+  components: {
+    Alert,
+    IconAdd,
+    IconEdit,
+    IconTrashcan,
+    ModalAddRoleGroup,
+    TableRowAction,
+    TableToolbar,
+  },
+  mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
+  data() {
+    return {
+      activeRoleGroup: null,
+      fields: [
+        {
+          key: 'checkbox',
+          sortable: false,
+        },
+        {
+          key: 'groupName',
+          sortable: true,
+          label: this.$t('pageLdap.tableRoleGroups.groupName'),
+        },
+        {
+          key: 'groupPrivilege',
+          sortable: true,
+          label: this.$t('pageLdap.tableRoleGroups.groupPrivilege'),
+        },
+        {
+          key: 'actions',
+          sortable: false,
+          label: '',
+          tdClass: 'text-right',
+        },
+      ],
+      batchActions: [
+        {
+          value: 'delete',
+          label: this.$t('global.action.delete'),
+        },
+      ],
+      selectedRows: selectedRows,
+      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
+      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
+    };
+  },
+  computed: {
+    ...mapGetters('ldap', ['isServiceEnabled', 'enabledRoleGroups']),
+    tableItems() {
+      return this.enabledRoleGroups.map(({ LocalRole, RemoteGroup }) => {
+        return {
+          groupName: RemoteGroup,
+          groupPrivilege: LocalRole,
+          actions: [
+            {
+              value: 'edit',
+              title: this.$t('global.action.edit'),
+              enabled: this.isServiceEnabled,
+            },
+            {
+              value: 'delete',
+              title: this.$t('global.action.delete'),
+              enabled: this.isServiceEnabled,
+            },
+          ],
+        };
+      });
+    },
+  },
+  created() {
+    this.$store.dispatch('userManagement/getAccountRoles');
+  },
+  methods: {
+    onBatchAction() {
+      this.$bvModal
+        .msgBoxConfirm(
+          this.$tc(
+            'pageLdap.modal.deleteRoleGroupBatchConfirmMessage',
+            this.selectedRows.length
+          ),
+          {
+            title: this.$t('pageLdap.modal.deleteRoleGroup'),
+            okTitle: this.$t('global.action.delete'),
+            cancelTitle: this.$t('global.action.cancel'),
+          }
+        )
+        .then((deleteConfirmed) => {
+          if (deleteConfirmed) {
+            this.startLoader();
+            this.$store
+              .dispatch('ldap/deleteRoleGroup', {
+                roleGroups: this.selectedRows,
+              })
+              .then((success) => this.successToast(success))
+              .catch(({ message }) => this.errorToast(message))
+              .finally(() => this.endLoader());
+          }
+        });
+    },
+    onTableRowAction(action, row) {
+      switch (action) {
+        case 'edit':
+          this.initRoleGroupModal(row);
+          break;
+        case 'delete':
+          this.$bvModal
+            .msgBoxConfirm(
+              this.$t('pageLdap.modal.deleteRoleGroupConfirmMessage', {
+                groupName: row.groupName,
+              }),
+              {
+                title: this.$t('pageLdap.modal.deleteRoleGroup'),
+                okTitle: this.$t('global.action.delete'),
+                cancelTitle: this.$t('global.action.cancel'),
+              }
+            )
+            .then((deleteConfirmed) => {
+              if (deleteConfirmed) {
+                this.startLoader();
+                this.$store
+                  .dispatch('ldap/deleteRoleGroup', { roleGroups: [row] })
+                  .then((success) => this.successToast(success))
+                  .catch(({ message }) => this.errorToast(message))
+                  .finally(() => this.endLoader());
+              }
+            });
+          break;
+      }
+    },
+    initRoleGroupModal(roleGroup) {
+      this.activeRoleGroup = roleGroup;
+      this.$bvModal.show('modal-role-group');
+    },
+    saveRoleGroup({ addNew, groupName, groupPrivilege }) {
+      this.activeRoleGroup = null;
+      const data = { groupName, groupPrivilege };
+      this.startLoader();
+      if (addNew) {
+        this.$store
+          .dispatch('ldap/addNewRoleGroup', data)
+          .then((success) => this.successToast(success))
+          .catch(({ message }) => this.errorToast(message))
+          .finally(() => this.endLoader());
+      } else {
+        this.$store
+          .dispatch('ldap/saveRoleGroup', data)
+          .then((success) => this.successToast(success))
+          .catch(({ message }) => this.errorToast(message))
+          .finally(() => this.endLoader());
+      }
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Ldap/index.js b/src/views/SecurityAndAccess/Ldap/index.js
new file mode 100644
index 0000000..6ae3abf
--- /dev/null
+++ b/src/views/SecurityAndAccess/Ldap/index.js
@@ -0,0 +1,2 @@
+import Ldap from './Ldap.vue';
+export default Ldap;