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/Certificates/Certificates.vue b/src/views/SecurityAndAccess/Certificates/Certificates.vue
new file mode 100644
index 0000000..9d20889
--- /dev/null
+++ b/src/views/SecurityAndAccess/Certificates/Certificates.vue
@@ -0,0 +1,319 @@
+<template>
+  <b-container fluid="xl">
+    <page-title />
+    <b-row>
+      <b-col xl="11">
+        <!-- Expired certificates banner -->
+        <alert :show="expiredCertificateTypes.length > 0" variant="danger">
+          <template v-if="expiredCertificateTypes.length > 1">
+            {{ $t('pageCertificates.alert.certificatesExpiredMessage') }}
+          </template>
+          <template v-else>
+            {{
+              $t('pageCertificates.alert.certificateExpiredMessage', {
+                certificate: expiredCertificateTypes[0],
+              })
+            }}
+          </template>
+        </alert>
+        <!-- Expiring certificates banner -->
+        <alert :show="expiringCertificateTypes.length > 0" variant="warning">
+          <template v-if="expiringCertificateTypes.length > 1">
+            {{ $t('pageCertificates.alert.certificatesExpiringMessage') }}
+          </template>
+          <template v-else>
+            {{
+              $t('pageCertificates.alert.certificateExpiringMessage', {
+                certificate: expiringCertificateTypes[0],
+              })
+            }}
+          </template>
+        </alert>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col xl="11" class="text-right">
+        <b-button
+          v-b-modal.generate-csr
+          data-test-id="certificates-button-generateCsr"
+          variant="link"
+        >
+          <icon-add />
+          {{ $t('pageCertificates.generateCsr') }}
+        </b-button>
+        <b-button
+          variant="primary"
+          :disabled="certificatesForUpload.length === 0"
+          @click="initModalUploadCertificate(null)"
+        >
+          <icon-add />
+          {{ $t('pageCertificates.addNewCertificate') }}
+        </b-button>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col xl="11">
+        <b-table
+          responsive="md"
+          show-empty
+          hover
+          :fields="fields"
+          :items="tableItems"
+          :empty-text="$t('global.table.emptyMessage')"
+        >
+          <template #cell(validFrom)="{ value }">
+            {{ value | formatDate }}
+          </template>
+
+          <template #cell(validUntil)="{ value }">
+            <status-icon
+              v-if="getDaysUntilExpired(value) < 31"
+              :status="getIconStatus(value)"
+            />
+            {{ value | formatDate }}
+          </template>
+
+          <template #cell(actions)="{ value, item }">
+            <table-row-action
+              v-for="(action, index) in value"
+              :key="index"
+              :value="action.value"
+              :title="action.title"
+              :enabled="action.enabled"
+              @click-table-action="onTableRowAction($event, item)"
+            >
+              <template #icon>
+                <icon-replace v-if="action.value === 'replace'" />
+                <icon-trashcan v-if="action.value === 'delete'" />
+              </template>
+            </table-row-action>
+          </template>
+        </b-table>
+      </b-col>
+    </b-row>
+
+    <!-- Modals -->
+    <modal-upload-certificate :certificate="modalCertificate" @ok="onModalOk" />
+    <modal-generate-csr />
+  </b-container>
+</template>
+
+<script>
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconReplace from '@carbon/icons-vue/es/renew/20';
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+
+import ModalGenerateCsr from './ModalGenerateCsr';
+import ModalUploadCertificate from './ModalUploadCertificate';
+import PageTitle from '@/components/Global/PageTitle';
+import TableRowAction from '@/components/Global/TableRowAction';
+import StatusIcon from '@/components/Global/StatusIcon';
+import Alert from '@/components/Global/Alert';
+
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+
+export default {
+  name: 'Certificates',
+  components: {
+    Alert,
+    IconAdd,
+    IconReplace,
+    IconTrashcan,
+    ModalGenerateCsr,
+    ModalUploadCertificate,
+    PageTitle,
+    StatusIcon,
+    TableRowAction,
+  },
+  mixins: [BVToastMixin, LoadingBarMixin],
+  beforeRouteLeave(to, from, next) {
+    this.hideLoader();
+    next();
+  },
+  data() {
+    return {
+      modalCertificate: null,
+      fields: [
+        {
+          key: 'certificate',
+          label: this.$t('pageCertificates.table.certificate'),
+        },
+        {
+          key: 'issuedBy',
+          label: this.$t('pageCertificates.table.issuedBy'),
+        },
+        {
+          key: 'issuedTo',
+          label: this.$t('pageCertificates.table.issuedTo'),
+        },
+        {
+          key: 'validFrom',
+          label: this.$t('pageCertificates.table.validFrom'),
+        },
+        {
+          key: 'validUntil',
+          label: this.$t('pageCertificates.table.validUntil'),
+        },
+        {
+          key: 'actions',
+          label: '',
+          tdClass: 'text-right text-nowrap',
+        },
+      ],
+    };
+  },
+  computed: {
+    certificates() {
+      return this.$store.getters['certificates/allCertificates'];
+    },
+    tableItems() {
+      return this.certificates.map((certificate) => {
+        return {
+          ...certificate,
+          actions: [
+            {
+              value: 'replace',
+              title: this.$t('pageCertificates.replaceCertificate'),
+            },
+            {
+              value: 'delete',
+              title: this.$t('pageCertificates.deleteCertificate'),
+              enabled:
+                certificate.type === 'TrustStore Certificate' ? true : false,
+            },
+          ],
+        };
+      });
+    },
+    certificatesForUpload() {
+      return this.$store.getters['certificates/availableUploadTypes'];
+    },
+    bmcTime() {
+      return this.$store.getters['global/bmcTime'];
+    },
+    expiredCertificateTypes() {
+      return this.certificates.reduce((acc, val) => {
+        const daysUntilExpired = this.getDaysUntilExpired(val.validUntil);
+        if (daysUntilExpired < 1) {
+          acc.push(val.certificate);
+        }
+        return acc;
+      }, []);
+    },
+    expiringCertificateTypes() {
+      return this.certificates.reduce((acc, val) => {
+        const daysUntilExpired = this.getDaysUntilExpired(val.validUntil);
+        if (daysUntilExpired < 31 && daysUntilExpired > 0) {
+          acc.push(val.certificate);
+        }
+        return acc;
+      }, []);
+    },
+  },
+  async created() {
+    this.startLoader();
+    await this.$store.dispatch('global/getBmcTime');
+    this.$store
+      .dispatch('certificates/getCertificates')
+      .finally(() => this.endLoader());
+  },
+  methods: {
+    onTableRowAction(event, rowItem) {
+      switch (event) {
+        case 'replace':
+          this.initModalUploadCertificate(rowItem);
+          break;
+        case 'delete':
+          this.initModalDeleteCertificate(rowItem);
+          break;
+        default:
+          break;
+      }
+    },
+    initModalUploadCertificate(certificate = null) {
+      this.modalCertificate = certificate;
+      this.$bvModal.show('upload-certificate');
+    },
+    initModalDeleteCertificate(certificate) {
+      this.$bvModal
+        .msgBoxConfirm(
+          this.$t('pageCertificates.modal.deleteConfirmMessage', {
+            issuedBy: certificate.issuedBy,
+            certificate: certificate.certificate,
+          }),
+          {
+            title: this.$t('pageCertificates.deleteCertificate'),
+            okTitle: this.$t('global.action.delete'),
+            cancelTitle: this.$t('global.action.cancel'),
+          }
+        )
+        .then((deleteConfirmed) => {
+          if (deleteConfirmed) this.deleteCertificate(certificate);
+        });
+    },
+    onModalOk({ addNew, file, type, location }) {
+      if (addNew) {
+        // Upload a new certificate
+        this.addNewCertificate(file, type);
+      } else {
+        // Replace an existing certificate
+        this.replaceCertificate(file, type, location);
+      }
+    },
+    addNewCertificate(file, type) {
+      this.startLoader();
+      this.$store
+        .dispatch('certificates/addNewCertificate', { file, type })
+        .then((success) => this.successToast(success))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+    replaceCertificate(file, type, location) {
+      this.startLoader();
+      const reader = new FileReader();
+      reader.readAsBinaryString(file);
+      reader.onloadend = (event) => {
+        const certificateString = event.target.result;
+        this.$store
+          .dispatch('certificates/replaceCertificate', {
+            certificateString,
+            type,
+            location,
+          })
+          .then((success) => this.successToast(success))
+          .catch(({ message }) => this.errorToast(message))
+          .finally(() => this.endLoader());
+      };
+    },
+    deleteCertificate({ type, location }) {
+      this.startLoader();
+      this.$store
+        .dispatch('certificates/deleteCertificate', {
+          type,
+          location,
+        })
+        .then((success) => this.successToast(success))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+    getDaysUntilExpired(date) {
+      if (this.bmcTime) {
+        const validUntilMs = date.getTime();
+        const currentBmcTimeMs = this.bmcTime.getTime();
+        const oneDayInMs = 24 * 60 * 60 * 1000;
+        return Math.round((validUntilMs - currentBmcTimeMs) / oneDayInMs);
+      }
+      return new Date();
+    },
+    getIconStatus(date) {
+      const daysUntilExpired = this.getDaysUntilExpired(date);
+      if (daysUntilExpired < 1) {
+        return 'danger';
+      } else if (daysUntilExpired < 31) {
+        return 'warning';
+      }
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Certificates/CsrCountryCodes.js b/src/views/SecurityAndAccess/Certificates/CsrCountryCodes.js
new file mode 100644
index 0000000..a2d7000
--- /dev/null
+++ b/src/views/SecurityAndAccess/Certificates/CsrCountryCodes.js
@@ -0,0 +1,345 @@
+import i18n from '@/i18n';
+
+export const COUNTRY_LIST = [
+  { name: 'Afghanistan', code: 'AF', label: i18n.t('countries.AF') },
+  { name: 'Albania', code: 'AL', label: i18n.t('countries.AL') },
+  { name: 'Algeria', code: 'DZ', label: i18n.t('countries.DZ') },
+  { name: 'American Samoa', code: 'AS', label: i18n.t('countries.AS') },
+  { name: 'Andorra', code: 'AD', label: i18n.t('countries.AD') },
+  { name: 'Angola', code: 'AO', label: i18n.t('countries.AO') },
+  { name: 'Anguilla', code: 'AI', label: i18n.t('countries.AI') },
+  { name: 'Antarctica', code: 'AQ', label: i18n.t('countries.AQ') },
+  { name: 'Antigua and Barbuda', code: 'AG', label: i18n.t('countries.AG') },
+  { name: 'Argentina', code: 'AR', label: i18n.t('countries.AR') },
+  { name: 'Armenia', code: 'AM', label: i18n.t('countries.AM') },
+  { name: 'Aruba', code: 'AW', label: i18n.t('countries.AW') },
+  { name: 'Australia', code: 'AU', label: i18n.t('countries.AU') },
+  { name: 'Austria', code: 'AT', label: i18n.t('countries.AT') },
+  { name: 'Azerbaijan', code: 'AZ', label: i18n.t('countries.AZ') },
+  { name: 'Bahamas, The', code: 'BS', label: i18n.t('countries.BS') },
+  { name: 'Bahrain', code: 'BH', label: i18n.t('countries.BH') },
+  { name: 'Bangladesh', code: 'BD', label: i18n.t('countries.BD') },
+  { name: 'Barbados', code: 'BB', label: i18n.t('countries.BB') },
+  { name: 'Belarus', code: 'BY', label: i18n.t('countries.BY') },
+  { name: 'Belgium', code: 'BE', label: i18n.t('countries.BE') },
+  { name: 'Belize', code: 'BZ', label: i18n.t('countries.BZ') },
+  { name: 'Benin', code: 'BJ', label: i18n.t('countries.BJ') },
+  { name: 'Bermuda', code: 'BM', label: i18n.t('countries.BM') },
+  { name: 'Bhutan', code: 'BT', label: i18n.t('countries.BT') },
+  { name: 'Bolivia', code: 'BO', label: i18n.t('countries.BO') },
+  {
+    name: 'Bonaire, Sint Eustatius and Saba',
+    code: 'BQ',
+    label: i18n.t('countries.BQ'),
+  },
+  {
+    name: 'Bosnia and Herzegovina ',
+    code: 'BA',
+    label: i18n.t('countries.BA'),
+  },
+  { name: 'Bostwana', code: 'BW', label: i18n.t('countries.BW') },
+  { name: 'Bouvet Island', code: 'BV', label: i18n.t('countries.BV') },
+  { name: 'Brazil', code: 'BR', label: i18n.t('countries.BR') },
+  {
+    name: 'British Indian Ocean Territory',
+    code: 'IO',
+    label: i18n.t('countries.IO'),
+  },
+  { name: 'Brunei Darussalam ', code: 'BN', label: i18n.t('countries.BN') },
+  { name: 'Bulgaria', code: 'BG', label: i18n.t('countries.BG') },
+  { name: 'Burkina Faso', code: 'BF', label: i18n.t('countries.BF') },
+  { name: 'Burundi', code: 'BI', label: i18n.t('countries.BI') },
+  { name: 'Cabo Verde', code: 'CV', label: i18n.t('countries.CV') },
+  { name: 'Cambodia', code: 'KH', label: i18n.t('countries.KH') },
+  { name: 'Cameroon', code: 'CM', label: i18n.t('countries.CM') },
+  { name: 'Canada', code: 'CA', label: i18n.t('countries.CA') },
+  { name: 'Cayman Islands', code: 'KY', label: i18n.t('countries.KY') },
+  {
+    name: 'Central African Republic',
+    code: 'CF',
+    label: i18n.t('countries.CF'),
+  },
+  { name: 'Chad', code: 'TD', label: i18n.t('countries.TD') },
+  { name: 'Chile', code: 'CL', label: i18n.t('countries.CL') },
+  { name: 'China', code: 'CN', label: i18n.t('countries.CN') },
+  { name: 'Christmas Island ', code: 'CX', label: i18n.t('countries.CX') },
+  { name: 'Cocos(Keeling) Islands', code: 'CC', label: i18n.t('countries.CC') },
+  { name: 'Columbia', code: 'CO', label: i18n.t('countries.CO') },
+  { name: 'Comoros', code: 'KM', label: i18n.t('countries.KM') },
+  {
+    name: 'Congo, The Democratic Republic of the',
+    code: 'CD',
+    label: i18n.t('countries.CD'),
+  },
+  { name: 'Congo', code: 'CG', label: i18n.t('countries.CG') },
+  { name: 'Cook Islands', code: 'CK', label: i18n.t('countries.CK') },
+  { name: 'Costa Rica', code: 'CR', label: i18n.t('countries.CR') },
+  { name: 'Croatia', code: 'HR', label: i18n.t('countries.HR') },
+  { name: 'Cuba', code: 'CU', label: i18n.t('countries.CU') },
+  { name: 'Curaçao', code: 'CW', label: i18n.t('countries.CW') },
+  { name: 'Cyprus', code: 'CY', label: i18n.t('countries.CY') },
+  { name: 'Czechia', code: 'CZ', label: i18n.t('countries.CZ') },
+  { name: "Côte d'Ivoire", code: 'CI', label: i18n.t('countries.CI') },
+  { name: 'Denmark', code: 'DK', label: i18n.t('countries.DK') },
+  { name: 'Djibouti', code: 'DJ', label: i18n.t('countries.DJ') },
+  { name: 'Dominica', code: 'DM', label: i18n.t('countries.DM') },
+  { name: 'Dominican Republic', code: 'DO', label: i18n.t('countries.DO') },
+  { name: 'Ecuador', code: 'EC', label: i18n.t('countries.EC') },
+  { name: 'Egypt', code: 'EG', label: i18n.t('countries.EG') },
+  { name: 'El Salvador', code: 'SV', label: i18n.t('countries.SV') },
+  { name: 'Equatorial Guinea ', code: 'GQ', label: i18n.t('countries.GQ') },
+  { name: 'Eritrea', code: 'ER', label: i18n.t('countries.ER') },
+  { name: 'Estonia', code: 'EE', label: i18n.t('countries.EE') },
+  { name: 'Eswatini', code: 'SZ', label: i18n.t('countries.SZ') },
+  { name: 'Ethiopia', code: 'ET', label: i18n.t('countries.ET') },
+  {
+    name: 'Falkland Islands (Malvinas)',
+    code: 'FK',
+    label: i18n.t('countries.FK'),
+  },
+  { name: 'Faroe Islands', code: 'FO', label: i18n.t('countries.FO') },
+  { name: 'Fiji', code: 'FJ', label: i18n.t('countries.FJ') },
+  { name: 'Finland', code: 'FI', label: i18n.t('countries.FI') },
+  { name: 'France', code: 'FR', label: i18n.t('countries.FR') },
+  { name: 'French Guiana', code: 'GF', label: i18n.t('countries.GF') },
+  { name: 'French Polynesia', code: 'PF', label: i18n.t('countries.PF') },
+  {
+    name: 'French Southern Territories',
+    code: 'TF',
+    label: i18n.t('countries.TF'),
+  },
+  { name: 'Gabon', code: 'GA', label: i18n.t('countries.GA') },
+  { name: 'Gambia, The', code: 'GM', label: i18n.t('countries.GM') },
+  { name: 'Georgia', code: 'GE', label: i18n.t('countries.GE') },
+  { name: 'Germany', code: 'DE', label: i18n.t('countries.DE') },
+  { name: 'Ghana', code: 'GH', label: i18n.t('countries.GH') },
+  { name: 'Gibraltar', code: 'GI', label: i18n.t('countries.GI') },
+  { name: 'Greece', code: 'GR', label: i18n.t('countries.GR') },
+  { name: 'Greenland', code: 'GL', label: i18n.t('countries.GL') },
+  { name: 'Grenada', code: 'GD', label: i18n.t('countries.GD') },
+  { name: 'Guadeloupe', code: 'GP', label: i18n.t('countries.GP') },
+  { name: 'Guam', code: 'GU', label: i18n.t('countries.GU') },
+  { name: 'Guatemala', code: 'GT', label: i18n.t('countries.GT') },
+  { name: 'Guernsey', code: 'GG', label: i18n.t('countries.GG') },
+  { name: 'Guinea', code: 'GN', label: i18n.t('countries.GN') },
+  { name: 'Guinea-Bissau', code: 'GW', label: i18n.t('countries.GW') },
+  { name: 'Guyana', code: 'GY', label: i18n.t('countries.GY') },
+  { name: 'Haiti', code: 'HT', label: i18n.t('countries.HT') },
+  {
+    name: 'Heard Island and McDonald Islands',
+    code: 'HM',
+    label: i18n.t('countries.HM'),
+  },
+  { name: 'Holy See', code: 'VA', label: i18n.t('countries.VA') },
+  { name: 'Honduras', code: 'HN', label: i18n.t('countries.HN') },
+  { name: 'Hong Kong', code: 'HK', label: i18n.t('countries.HK') },
+  { name: 'Hungary', code: 'HU', label: i18n.t('countries.HU') },
+  { name: 'Iceland', code: 'IS', label: i18n.t('countries.IS') },
+  { name: 'India', code: 'IN', label: i18n.t('countries.IN') },
+  { name: 'Indonesia', code: 'ID', label: i18n.t('countries.ID') },
+  {
+    name: 'Iran, Islamic Republic of',
+    code: 'IR',
+    label: i18n.t('countries.IR'),
+  },
+  { name: 'Iraq', code: 'IQ', label: i18n.t('countries.IQ') },
+  { name: 'Ireland', code: 'IE', label: i18n.t('countries.IE') },
+  { name: 'Isle of Man', code: 'IM', label: i18n.t('countries.IM') },
+  { name: 'Israel', code: 'IL', label: i18n.t('countries.IL') },
+  { name: 'Italy', code: 'IT', label: i18n.t('countries.IT') },
+  { name: 'Jamaica', code: 'JM', label: i18n.t('countries.JM') },
+  { name: 'Japan', code: 'JP', label: i18n.t('countries.JP') },
+  { name: 'Jersey', code: 'JE', label: i18n.t('countries.JE') },
+  { name: 'Jordan', code: 'JO', label: i18n.t('countries.JO') },
+  { name: 'Kazakhstan', code: 'KZ', label: i18n.t('countries.KZ') },
+  { name: 'Kenya', code: 'KE', label: i18n.t('countries.KE') },
+  { name: 'Kiribati', code: 'KI', label: i18n.t('countries.KI') },
+  { name: 'Korea, Republic of', code: 'KR', label: i18n.t('countries.KR') },
+  {
+    name: "Korea, Democratic People's Republic of",
+    code: 'KP',
+    label: i18n.t('countries.KP'),
+  },
+  { name: 'Kuwait', code: 'KW', label: i18n.t('countries.KW') },
+  { name: 'Kyrgyzstan', code: 'KG', label: i18n.t('countries.KG') },
+  {
+    name: "Lao People's Democratic Republic",
+    code: 'LA',
+    label: i18n.t('countries.LA'),
+  },
+  { name: 'Latvia', code: 'LV', label: i18n.t('countries.LV') },
+  { name: 'Lebanon', code: 'LB', label: i18n.t('countries.LB') },
+  { name: 'Lesotho', code: 'LS', label: i18n.t('countries.LS') },
+  { name: 'Liberia', code: 'LR', label: i18n.t('countries.LR') },
+  { name: 'Libya', code: 'LY', label: i18n.t('countries.LY') },
+  { name: 'Liechtenstein', code: 'LI', label: i18n.t('countries.LI') },
+  { name: 'Lithuania', code: 'LT', label: i18n.t('countries.LT') },
+  { name: 'Luxembourg', code: 'LU', label: i18n.t('countries.LU') },
+  { name: 'Macao', code: 'MO', label: i18n.t('countries.MO') },
+  {
+    name: 'Macedonia, The Former Yugoslav Republic of',
+    code: 'MK',
+    label: i18n.t('countries.MK'),
+  },
+  { name: 'Madagascar', code: 'MG', label: i18n.t('countries.MG') },
+  { name: 'Malawi', code: 'MW', label: i18n.t('countries.MW') },
+  { name: 'Malaysia', code: 'MY', label: i18n.t('countries.MY') },
+  { name: 'Maldives', code: 'MV', label: i18n.t('countries.MV') },
+  { name: 'Mali', code: 'ML', label: i18n.t('countries.ML') },
+  { name: 'Malta', code: 'MT', label: i18n.t('countries.MT') },
+  { name: 'Marshall Islands', code: 'MH', label: i18n.t('countries.MH') },
+  { name: 'Martinique', code: 'MQ', label: i18n.t('countries.MQ') },
+  { name: 'Mauritania', code: 'MR', label: i18n.t('countries.MR') },
+  { name: 'Mauritius', code: 'MU', label: i18n.t('countries.MU') },
+  { name: 'Mayotte', code: 'YT', label: i18n.t('countries.YT') },
+  { name: 'Mexico', code: 'MX', label: i18n.t('countries.MX') },
+  {
+    name: 'Micronesia, Federated States of',
+    code: 'FM',
+    label: i18n.t('countries.FM'),
+  },
+  { name: 'Moldova, Republic of', code: 'MD', label: i18n.t('countries.MD') },
+  { name: 'Monaco', code: 'MC', label: i18n.t('countries.MC') },
+  { name: 'Mongolia', code: 'MN', label: i18n.t('countries.MN') },
+  { name: 'Montenegro', code: 'ME', label: i18n.t('countries.ME') },
+  { name: 'Montserrat', code: 'MS', label: i18n.t('countries.MS') },
+  { name: 'Morocco', code: 'MA', label: i18n.t('countries.MA') },
+  { name: 'Mozambique', code: 'MZ', label: i18n.t('countries.MZ') },
+  { name: 'Myanmar', code: 'MM', label: i18n.t('countries.MM') },
+  { name: 'Namibia', code: 'NA', label: i18n.t('countries.NA') },
+  { name: 'Nauru', code: 'NR', label: i18n.t('countries.NR') },
+  { name: 'Nepal', code: 'NP', label: i18n.t('countries.NP') },
+  { name: 'Netherlands', code: 'NL', label: i18n.t('countries.NL') },
+  { name: 'New Caledonia', code: 'NC', label: i18n.t('countries.NC') },
+  { name: 'New Zealand', code: 'NZ', label: i18n.t('countries.NZ') },
+  { name: 'Nicaragua', code: 'NI', label: i18n.t('countries.NI') },
+  { name: 'Niger', code: 'NE', label: i18n.t('countries.NE') },
+  { name: 'Nigeria', code: 'NG', label: i18n.t('countries.NG') },
+  { name: 'Niue', code: 'NU', label: i18n.t('countries.NU') },
+  { name: 'Norfolk Island', code: 'NF', label: i18n.t('countries.NF') },
+  {
+    name: 'Northern Mariana Islands',
+    code: 'MP',
+    label: i18n.t('countries.MP'),
+  },
+  { name: 'Norway', code: 'NO', label: i18n.t('countries.NO') },
+  { name: 'Oman', code: 'OM', label: i18n.t('countries.OM') },
+  { name: 'Pakistan', code: 'PK', label: i18n.t('countries.PK') },
+  { name: 'Palau', code: 'PW', label: i18n.t('countries.PW') },
+  { name: 'Palestine', code: 'PS', label: i18n.t('countries.PS') },
+  { name: 'Panama', code: 'PA', label: i18n.t('countries.PA') },
+  { name: 'Papua New Guinea', code: 'PG', label: i18n.t('countries.PG') },
+  { name: 'Paraguay', code: 'PY', label: i18n.t('countries.PY') },
+  { name: 'Peru', code: 'PE', label: i18n.t('countries.PE') },
+  { name: 'Philippines', code: 'PH', label: i18n.t('countries.PH') },
+  { name: 'Pitcairn', code: 'PN', label: i18n.t('countries.PN') },
+  { name: 'Poland', code: 'PL', label: i18n.t('countries.PL') },
+  { name: 'Portugal', code: 'PT', label: i18n.t('countries.PT') },
+  { name: 'Puerto Rico', code: 'PR', label: i18n.t('countries.PR') },
+  { name: 'Qatar', code: 'QA', label: i18n.t('countries.QA') },
+  { name: 'Romania', code: 'RO', label: i18n.t('countries.RO') },
+  { name: 'Russian Federation', code: 'RU', label: i18n.t('countries.RU') },
+  { name: 'Rwanda', code: 'RW', label: i18n.t('countries.RW') },
+  { name: 'Réunion', code: 'RE', label: i18n.t('countries.RE') },
+  { name: 'Saint Barthélemy', code: 'BL', label: i18n.t('countries.BL') },
+  {
+    name: 'Saint Helena, Ascension and Tristan da Cunha',
+    code: 'SH',
+    label: i18n.t('countries.SH'),
+  },
+  { name: 'Saint Kitts and Nevis ', code: 'KN', label: i18n.t('countries.KN') },
+  { name: 'Saint Lucia', code: 'LC', label: i18n.t('countries.LC') },
+  { name: 'Saint Martin', code: 'MF', label: i18n.t('countries.MF') },
+  {
+    name: 'Saint Pierre and Miquelon',
+    code: 'PM',
+    label: i18n.t('countries.PM'),
+  },
+  {
+    name: 'Saint Vincent and the Grenadines',
+    code: 'VC',
+    label: i18n.t('countries.VC'),
+  },
+  { name: 'Samoa', code: 'WS', label: i18n.t('countries.WS') },
+  { name: 'San Marino ', code: 'SM', label: i18n.t('countries.SM') },
+  { name: 'Sao Tome and Principe', code: 'ST', label: i18n.t('countries.ST') },
+  { name: 'Saudi Arabia', code: 'SA', label: i18n.t('countries.SA') },
+  { name: 'Senegal', code: 'SN', label: i18n.t('countries.SN') },
+  { name: 'Serbia', code: 'RS', label: i18n.t('countries.RS') },
+  { name: 'Seychelles', code: 'SC', label: i18n.t('countries.SC') },
+  { name: 'Sierra Leone', code: 'SL', label: i18n.t('countries.SL') },
+  { name: 'Singapore', code: 'SG', label: i18n.t('countries.SG') },
+  { name: 'Sint Maarten', code: 'SX', label: i18n.t('countries.SX') },
+  { name: 'Slovakia', code: 'SK', label: i18n.t('countries.SK') },
+  { name: 'Slovenia', code: 'SI', label: i18n.t('countries.SI') },
+  { name: 'Solomon Islands', code: 'SB', label: i18n.t('countries.SB') },
+  { name: 'Somalia', code: 'SO', label: i18n.t('countries.SO') },
+  { name: 'South Africa ', code: 'ZA', label: i18n.t('countries.ZA') },
+  {
+    name: 'South Georgia and the South Sandwich Islands',
+    code: 'GS',
+    label: i18n.t('countries.GS'),
+  },
+  { name: 'South Sudan', code: 'SS', label: i18n.t('countries.SS') },
+  { name: 'Spain', code: 'ES', label: i18n.t('countries.ES') },
+  { name: 'Sri Lanka', code: 'LK', label: i18n.t('countries.LK') },
+  { name: 'Sudan', code: 'SD', label: i18n.t('countries.SD') },
+  { name: 'Suriname', code: 'SR', label: i18n.t('countries.SR') },
+  { name: 'Svalbard and Jan Mayen', code: 'SJ', label: i18n.t('countries.SJ') },
+  { name: 'Sweden', code: 'SE', label: i18n.t('countries.SE') },
+  { name: 'Switzerland', code: 'CH', label: i18n.t('countries.CH') },
+  { name: 'Syrian Arab Republic', code: 'SY', label: i18n.t('countries.SY') },
+  { name: 'Taiwan', code: 'TW', label: i18n.t('countries.TW') },
+  { name: 'Tajikistan', code: 'TJ', label: i18n.t('countries.TJ') },
+  {
+    name: 'Tanzania, United Republic of',
+    code: 'TZ',
+    label: i18n.t('countries.TZ'),
+  },
+  { name: 'Thailand', code: 'TH', label: i18n.t('countries.TH') },
+  { name: 'Timor-Leste', code: 'TL', label: i18n.t('countries.TL') },
+  { name: 'Togo', code: 'TG', label: i18n.t('countries.TG') },
+  { name: 'Tokelau', code: 'TK', label: i18n.t('countries.TK') },
+  { name: 'Tonga', code: 'TO', label: i18n.t('countries.TO') },
+  { name: 'Trinidad and Tobago', code: 'TT', label: i18n.t('countries.TT') },
+  { name: 'Tunisia', code: 'TN', label: i18n.t('countries.TN') },
+  { name: 'Turkey', code: 'TR', label: i18n.t('countries.TR') },
+  { name: 'Turkmenistan', code: 'TM', label: i18n.t('countries.TM') },
+  {
+    name: 'Turks and Caicos Islands',
+    code: 'TC',
+    label: i18n.t('countries.TC'),
+  },
+  { name: 'Tuvalu', code: 'TV', label: i18n.t('countries.TV') },
+  { name: 'Uganda', code: 'UG', label: i18n.t('countries.UG') },
+  { name: 'Ukraine', code: 'UA', label: i18n.t('countries.UA') },
+  { name: 'United Arab Emirates', code: 'AE', label: i18n.t('countries.AE') },
+  { name: 'United Kingdom', code: 'GB', label: i18n.t('countries.GB') },
+  {
+    name: 'United States Minor Outlying Islands',
+    code: 'UM',
+    label: i18n.t('countries.UM'),
+  },
+  {
+    name: 'United States of America',
+    code: 'US',
+    label: i18n.t('countries.US'),
+  },
+  { name: 'Uruguay', code: 'UY', label: i18n.t('countries.UY') },
+  { name: 'Uzbekistan', code: 'UZ', label: i18n.t('countries.UZ') },
+  { name: 'Vanuatu', code: 'VU', label: i18n.t('countries.VU') },
+  { name: 'Venezuela', code: 'VE', label: i18n.t('countries.VE') },
+  { name: 'Viet Nam', code: 'VN', label: i18n.t('countries.VN') },
+  {
+    name: 'Virgin Islands, British',
+    code: 'VG',
+    label: i18n.t('countries.VG'),
+  },
+  { name: 'Virgin Islands, U.S', code: 'VI', label: i18n.t('countries.VI') },
+  { name: 'Wallis and Futuna', code: 'WF', label: i18n.t('countries.WF') },
+  { name: 'Western Sahara', code: 'EH', label: i18n.t('countries.EH') },
+  { name: 'Yemen', code: 'YE', label: i18n.t('countries.YE') },
+  { name: 'Zambia', code: 'ZM', label: i18n.t('countries.ZM') },
+  { name: 'Zimbabwe', code: 'ZW', label: i18n.t('countries.ZW') },
+  { name: 'Åland Islands', code: 'AX', label: i18n.t('countries.AX') },
+];
diff --git a/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue b/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
new file mode 100644
index 0000000..d76f9fe
--- /dev/null
+++ b/src/views/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
@@ -0,0 +1,496 @@
+<template>
+  <div>
+    <b-modal
+      id="generate-csr"
+      ref="modal"
+      size="lg"
+      no-stacking
+      :title="$t('pageCertificates.modal.generateACertificateSigningRequest')"
+      @ok="onOkGenerateCsrModal"
+      @cancel="resetForm"
+      @hidden="$v.$reset()"
+    >
+      <b-form id="generate-csr-form" novalidate @submit.prevent="handleSubmit">
+        <b-container fluid>
+          <b-row>
+            <b-col lg="9">
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.certificateType')"
+                    label-for="certificate-type"
+                  >
+                    <b-form-select
+                      id="certificate-type"
+                      v-model="form.certificateType"
+                      data-test-id="modalGenerateCsr-select-certificateType"
+                      :options="certificateOptions"
+                      :state="getValidationState($v.form.certificateType)"
+                      @input="$v.form.certificateType.$touch()"
+                    >
+                      <template #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-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.country')"
+                    label-for="country"
+                  >
+                    <b-form-select
+                      id="country"
+                      v-model="form.country"
+                      data-test-id="modalGenerateCsr-select-country"
+                      :options="countryOptions"
+                      :state="getValidationState($v.form.country)"
+                      @input="$v.form.country.$touch()"
+                    >
+                      <template #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-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.state')"
+                    label-for="state"
+                  >
+                    <b-form-input
+                      id="state"
+                      v-model="form.state"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-state"
+                      :state="getValidationState($v.form.state)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.city')"
+                    label-for="city"
+                  >
+                    <b-form-input
+                      id="city"
+                      v-model="form.city"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-city"
+                      :state="getValidationState($v.form.city)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.companyName')"
+                    label-for="company-name"
+                  >
+                    <b-form-input
+                      id="company-name"
+                      v-model="form.companyName"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-companyName"
+                      :state="getValidationState($v.form.companyName)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.companyUnit')"
+                    label-for="company-unit"
+                  >
+                    <b-form-input
+                      id="company-unit"
+                      v-model="form.companyUnit"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-companyUnit"
+                      :state="getValidationState($v.form.companyUnit)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.commonName')"
+                    label-for="common-name"
+                  >
+                    <b-form-input
+                      id="common-name"
+                      v-model="form.commonName"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-commonName"
+                      :state="getValidationState($v.form.commonName)"
+                    />
+                    <b-form-invalid-feedback role="alert">
+                      {{ $t('global.form.fieldRequired') }}
+                    </b-form-invalid-feedback>
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group label-for="challenge-password">
+                    <template #label>
+                      {{ $t('pageCertificates.modal.challengePassword') }} -
+                      <span class="form-text d-inline">
+                        {{ $t('global.form.optional') }}
+                      </span>
+                    </template>
+                    <b-form-input
+                      id="challenge-password"
+                      v-model="form.challengePassword"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-challengePassword"
+                    />
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="6">
+                  <b-form-group label-for="contact-person">
+                    <template #label>
+                      {{ $t('pageCertificates.modal.contactPerson') }} -
+                      <span class="form-text d-inline">
+                        {{ $t('global.form.optional') }}
+                      </span>
+                    </template>
+                    <b-form-input
+                      id="contact-person"
+                      v-model="form.contactPerson"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-contactPerson"
+                    />
+                  </b-form-group>
+                </b-col>
+                <b-col lg="6">
+                  <b-form-group label-for="email-address">
+                    <template #label>
+                      {{ $t('pageCertificates.modal.emailAddress') }} -
+                      <span class="form-text d-inline">
+                        {{ $t('global.form.optional') }}
+                      </span>
+                    </template>
+                    <b-form-input
+                      id="email-address"
+                      v-model="form.emailAddress"
+                      type="text"
+                      data-test-id="modalGenerateCsr-input-emailAddress"
+                    />
+                  </b-form-group>
+                </b-col>
+              </b-row>
+              <b-row>
+                <b-col lg="12">
+                  <b-form-group label-for="alternate-name">
+                    <template #label>
+                      {{ $t('pageCertificates.modal.alternateName') }} -
+                      <span class="form-text d-inline">
+                        {{ $t('global.form.optional') }}
+                      </span>
+                    </template>
+                    <b-form-text id="alternate-name-help-block">
+                      {{ $t('pageCertificates.modal.alternateNameHelperText') }}
+                    </b-form-text>
+                    <b-form-tags
+                      v-model="form.alternateName"
+                      :remove-on-delete="true"
+                      :tag-pills="true"
+                      input-id="alternate-name"
+                      size="lg"
+                      separator=" "
+                      :input-attrs="{
+                        'aria-describedby': 'alternate-name-help-block',
+                      }"
+                      :duplicate-tag-text="
+                        $t('pageCertificates.modal.duplicateAlternateName')
+                      "
+                      placeholder=""
+                      data-test-id="modalGenerateCsr-input-alternateName"
+                    >
+                      <template #add-button-text>
+                        <icon-add /> {{ $t('global.action.add') }}
+                      </template>
+                    </b-form-tags>
+                  </b-form-group>
+                </b-col>
+              </b-row>
+            </b-col>
+            <b-col lg="3">
+              <b-row>
+                <b-col lg="12">
+                  <p class="col-form-label">
+                    {{ $t('pageCertificates.modal.privateKey') }}
+                  </p>
+                  <b-form-group
+                    :label="$t('pageCertificates.modal.keyPairAlgorithm')"
+                    label-for="key-pair-algorithm"
+                  >
+                    <b-form-select
+                      id="key-pair-algorithm"
+                      v-model="form.keyPairAlgorithm"
+                      data-test-id="modalGenerateCsr-select-keyPairAlgorithm"
+                      :options="keyPairAlgorithmOptions"
+                      :state="getValidationState($v.form.keyPairAlgorithm)"
+                      @input="$v.form.keyPairAlgorithm.$touch()"
+                    >
+                      <template #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-col>
+              </b-row>
+              <b-row>
+                <b-col lg="12">
+                  <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'">
+                    <b-form-group
+                      :label="$t('pageCertificates.modal.keyCurveId')"
+                      label-for="key-curve-id"
+                    >
+                      <b-form-select
+                        id="key-curve-id"
+                        v-model="form.keyCurveId"
+                        data-test-id="modalGenerateCsr-select-keyCurveId"
+                        :options="keyCurveIdOptions"
+                        :state="getValidationState($v.form.keyCurveId)"
+                        @input="$v.form.keyCurveId.$touch()"
+                      >
+                        <template #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>
+                  </template>
+                  <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'">
+                    <b-form-group
+                      :label="$t('pageCertificates.modal.keyBitLength')"
+                      label-for="key-bit-length"
+                    >
+                      <b-form-select
+                        id="key-bit-length"
+                        v-model="form.keyBitLength"
+                        data-test-id="modalGenerateCsr-select-keyBitLength"
+                        :options="keyBitLengthOptions"
+                        :state="getValidationState($v.form.keyBitLength)"
+                        @input="$v.form.keyBitLength.$touch()"
+                      >
+                        <template #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>
+                  </template>
+                </b-col>
+              </b-row>
+            </b-col>
+          </b-row>
+        </b-container>
+      </b-form>
+      <template #modal-footer="{ ok, cancel }">
+        <b-button variant="secondary" @click="cancel()">
+          {{ $t('global.action.cancel') }}
+        </b-button>
+        <b-button
+          form="generate-csr-form"
+          type="submit"
+          variant="primary"
+          data-test-id="modalGenerateCsr-button-ok"
+          @click="ok()"
+        >
+          {{ $t('pageCertificates.generateCsr') }}
+        </b-button>
+      </template>
+    </b-modal>
+    <b-modal
+      id="csr-string"
+      no-stacking
+      size="lg"
+      :title="$t('pageCertificates.modal.certificateSigningRequest')"
+      @hidden="onHiddenCsrStringModal"
+    >
+      {{ csrString }}
+      <template #modal-footer>
+        <b-btn variant="secondary" @click="copyCsrString">
+          <template v-if="csrStringCopied">
+            <icon-checkmark />
+            {{ $t('global.status.copied') }}
+          </template>
+          <template v-else>
+            {{ $t('global.action.copy') }}
+          </template>
+        </b-btn>
+        <a
+          :href="`data:text/json;charset=utf-8,${csrString}`"
+          download="certificate.txt"
+          class="btn btn-primary"
+        >
+          {{ $t('global.action.download') }}
+        </a>
+      </template>
+    </b-modal>
+  </div>
+</template>
+
+<script>
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconCheckmark from '@carbon/icons-vue/es/checkmark/20';
+
+import { required, requiredIf } from 'vuelidate/lib/validators';
+
+import { COUNTRY_LIST } from './CsrCountryCodes';
+import { CERTIFICATE_TYPES } from '@/store/modules/SecurityAndAccess/CertificatesStore';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+export default {
+  name: 'ModalGenerateCsr',
+  components: { IconAdd, IconCheckmark },
+  mixins: [BVToastMixin, VuelidateMixin],
+  data() {
+    return {
+      form: {
+        certificateType: null,
+        country: null,
+        state: null,
+        city: null,
+        companyName: null,
+        companyUnit: null,
+        commonName: null,
+        challengePassword: null,
+        contactPerson: null,
+        emailAddress: null,
+        alternateName: [],
+        keyPairAlgorithm: null,
+        keyCurveId: null,
+        keyBitLength: null,
+      },
+      certificateOptions: CERTIFICATE_TYPES.reduce((arr, cert) => {
+        if (cert.type === 'TrustStore Certificate') return arr;
+        arr.push({
+          text: cert.label,
+          value: cert.type,
+        });
+        return arr;
+      }, []),
+      countryOptions: COUNTRY_LIST.map((country) => ({
+        text: country.label,
+        value: country.code,
+      })),
+      keyPairAlgorithmOptions: ['EC', 'RSA'],
+      keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'],
+      keyBitLengthOptions: [2048],
+      csrString: '',
+      csrStringCopied: false,
+    };
+  },
+  validations: {
+    form: {
+      certificateType: { required },
+      country: { required },
+      state: { required },
+      city: { required },
+      companyName: { required },
+      companyUnit: { required },
+      commonName: { required },
+      challengePassword: {},
+      contactPerson: {},
+      emailAddress: {},
+      alternateName: {},
+      keyPairAlgorithm: { required },
+      keyCurveId: {
+        reuired: requiredIf(function (form) {
+          return form.keyPairAlgorithm === 'EC';
+        }),
+      },
+      keyBitLength: {
+        reuired: requiredIf(function (form) {
+          return form.keyPairAlgorithm === 'RSA';
+        }),
+      },
+    },
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$store
+        .dispatch('certificates/generateCsr', this.form)
+        .then(({ data: { CSRString } }) => {
+          this.csrString = CSRString;
+          this.$bvModal.show('csr-string');
+          this.$v.$reset();
+        });
+    },
+    resetForm() {
+      for (let key of Object.keys(this.form)) {
+        if (key === 'alternateName') {
+          this.form[key] = [];
+        } else {
+          this.form[key] = null;
+        }
+      }
+    },
+    onOkGenerateCsrModal(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+    onHiddenCsrStringModal() {
+      this.csrString = '';
+      this.resetForm();
+    },
+    copyCsrString(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      navigator.clipboard.writeText(this.csrString).then(() => {
+        // Show copied text for 5 seconds
+        this.csrStringCopied = true;
+        setTimeout(() => {
+          this.csrStringCopied = false;
+        }, 5000 /*5 seconds*/);
+      });
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue b/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
new file mode 100644
index 0000000..f4db7a2
--- /dev/null
+++ b/src/views/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
@@ -0,0 +1,168 @@
+<template>
+  <b-modal id="upload-certificate" ref="modal" @ok="onOk" @hidden="resetForm">
+    <template #modal-title>
+      <template v-if="certificate">
+        {{ $t('pageCertificates.replaceCertificate') }}
+      </template>
+      <template v-else>
+        {{ $t('pageCertificates.addNewCertificate') }}
+      </template>
+    </template>
+    <b-form>
+      <!-- Replace Certificate type -->
+      <template v-if="certificate !== null">
+        <dl class="mb-4">
+          <dt>{{ $t('pageCertificates.modal.certificateType') }}</dt>
+          <dd>{{ certificate.certificate }}</dd>
+        </dl>
+      </template>
+
+      <!-- Add new Certificate type -->
+      <template v-else>
+        <b-form-group
+          :label="$t('pageCertificates.modal.certificateType')"
+          label-for="certificate-type"
+        >
+          <b-form-select
+            id="certificate-type"
+            v-model="form.certificateType"
+            :options="certificateOptions"
+            :state="getValidationState($v.form.certificateType)"
+            @input="$v.form.certificateType.$touch()"
+          >
+          </b-form-select>
+          <b-form-invalid-feedback role="alert">
+            <template v-if="!$v.form.certificateType.required">
+              {{ $t('global.form.fieldRequired') }}
+            </template>
+          </b-form-invalid-feedback>
+        </b-form-group>
+      </template>
+
+      <b-form-group :label="$t('pageCertificates.modal.certificateFile')">
+        <form-file
+          id="certificate-file"
+          v-model="form.file"
+          accept=".pem"
+          :state="getValidationState($v.form.file)"
+        >
+          <template #invalid>
+            <b-form-invalid-feedback role="alert">
+              {{ $t('global.form.required') }}
+            </b-form-invalid-feedback>
+          </template>
+        </form-file>
+      </b-form-group>
+    </b-form>
+    <template #modal-ok>
+      <template v-if="certificate">
+        {{ $t('global.action.replace') }}
+      </template>
+      <template v-else>
+        {{ $t('global.action.add') }}
+      </template>
+    </template>
+    <template #modal-cancel>
+      {{ $t('global.action.cancel') }}
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import { required, requiredIf } from 'vuelidate/lib/validators';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+import FormFile from '@/components/Global/FormFile';
+
+export default {
+  components: { FormFile },
+  mixins: [VuelidateMixin],
+  props: {
+    certificate: {
+      type: Object,
+      default: null,
+      validator: (prop) => {
+        if (prop === null) return true;
+        return (
+          Object.prototype.hasOwnProperty.call(prop, 'type') &&
+          Object.prototype.hasOwnProperty.call(prop, 'certificate')
+        );
+      },
+    },
+  },
+  data() {
+    return {
+      form: {
+        certificateType: null,
+        file: null,
+      },
+    };
+  },
+  computed: {
+    certificateTypes() {
+      return this.$store.getters['certificates/availableUploadTypes'];
+    },
+    certificateOptions() {
+      return this.certificateTypes.map(({ type, label }) => {
+        return {
+          text: label,
+          value: type,
+        };
+      });
+    },
+  },
+  watch: {
+    certificateOptions: function (options) {
+      if (options.length) {
+        this.form.certificateType = options[0].value;
+      }
+    },
+  },
+  validations() {
+    return {
+      form: {
+        certificateType: {
+          required: requiredIf(function () {
+            return !this.certificate;
+          }),
+        },
+        file: {
+          required,
+        },
+      },
+    };
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+      this.$emit('ok', {
+        addNew: !this.certificate,
+        file: this.form.file,
+        location: this.certificate ? this.certificate.location : null,
+        type: this.certificate
+          ? this.certificate.type
+          : this.form.certificateType,
+      });
+      this.closeModal();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      this.form.certificateType = this.certificateOptions.length
+        ? this.certificateOptions[0].value
+        : null;
+      this.form.file = null;
+      this.$v.$reset();
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/Certificates/index.js b/src/views/SecurityAndAccess/Certificates/index.js
new file mode 100644
index 0000000..aff57b5
--- /dev/null
+++ b/src/views/SecurityAndAccess/Certificates/index.js
@@ -0,0 +1,2 @@
+import Certificates from './Certificates.vue';
+export default Certificates;
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;
diff --git a/src/views/SecurityAndAccess/Policies/Policies.vue b/src/views/SecurityAndAccess/Policies/Policies.vue
new file mode 100644
index 0000000..41b9536
--- /dev/null
+++ b/src/views/SecurityAndAccess/Policies/Policies.vue
@@ -0,0 +1,123 @@
+<template>
+  <b-container fluid="xl">
+    <page-title />
+    <b-row>
+      <b-col md="8">
+        <page-section :section-title="$t('pagePolicies.networkServices')">
+          <b-row class="setting-section">
+            <b-col class="d-flex align-items-center justify-content-between">
+              <dl class="mr-3 w-75">
+                <dt>{{ $t('pagePolicies.ssh') }}</dt>
+                <dd>
+                  {{ $t('pagePolicies.sshDescription') }}
+                </dd>
+              </dl>
+              <b-form-checkbox
+                id="sshSwitch"
+                v-model="sshProtocolState"
+                data-test-id="policies-toggle-bmcShell"
+                switch
+                @change="changeSshProtocolState"
+              >
+                <span class="sr-only">
+                  {{ $t('pagePolicies.ssh') }}
+                </span>
+                <span v-if="sshProtocolState">
+                  {{ $t('global.status.enabled') }}
+                </span>
+                <span v-else>{{ $t('global.status.disabled') }}</span>
+              </b-form-checkbox>
+            </b-col>
+          </b-row>
+          <b-row class="setting-section">
+            <b-col class="d-flex align-items-center justify-content-between">
+              <dl class="mt-3 mr-3 w-75">
+                <dt>{{ $t('pagePolicies.ipmi') }}</dt>
+                <dd>
+                  {{ $t('pagePolicies.ipmiDescription') }}
+                </dd>
+              </dl>
+              <b-form-checkbox
+                id="ipmiSwitch"
+                v-model="ipmiProtocolState"
+                data-test-id="polices-toggle-networkIpmi"
+                switch
+                @change="changeIpmiProtocolState"
+              >
+                <span class="sr-only">
+                  {{ $t('pagePolicies.ipmi') }}
+                </span>
+                <span v-if="ipmiProtocolState">
+                  {{ $t('global.status.enabled') }}
+                </span>
+                <span v-else>{{ $t('global.status.disabled') }}</span>
+              </b-form-checkbox>
+            </b-col>
+          </b-row>
+        </page-section>
+      </b-col>
+    </b-row>
+  </b-container>
+</template>
+
+<script>
+import PageSection from '@/components/Global/PageSection';
+import PageTitle from '@/components/Global/PageTitle';
+
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+
+export default {
+  name: 'Policies',
+  components: { PageTitle, PageSection },
+  mixins: [LoadingBarMixin, BVToastMixin],
+  beforeRouteLeave(to, from, next) {
+    this.hideLoader();
+    next();
+  },
+  computed: {
+    sshProtocolState: {
+      get() {
+        return this.$store.getters['policies/sshProtocolEnabled'];
+      },
+      set(newValue) {
+        return newValue;
+      },
+    },
+    ipmiProtocolState: {
+      get() {
+        return this.$store.getters['policies/ipmiProtocolEnabled'];
+      },
+      set(newValue) {
+        return newValue;
+      },
+    },
+  },
+  created() {
+    this.startLoader();
+    this.$store
+      .dispatch('policies/getNetworkProtocolStatus')
+      .finally(() => this.endLoader());
+  },
+  methods: {
+    changeIpmiProtocolState(state) {
+      this.$store
+        .dispatch('policies/saveIpmiProtocolState', state)
+        .then((message) => this.successToast(message))
+        .catch(({ message }) => this.errorToast(message));
+    },
+    changeSshProtocolState(state) {
+      this.$store
+        .dispatch('policies/saveSshProtocolState', state)
+        .then((message) => this.successToast(message))
+        .catch(({ message }) => this.errorToast(message));
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.setting-section {
+  border-bottom: 1px solid gray('300');
+}
+</style>
diff --git a/src/views/SecurityAndAccess/Policies/index.js b/src/views/SecurityAndAccess/Policies/index.js
new file mode 100644
index 0000000..7702390
--- /dev/null
+++ b/src/views/SecurityAndAccess/Policies/index.js
@@ -0,0 +1,2 @@
+import Policies from './Policies.vue';
+export default Policies;
diff --git a/src/views/SecurityAndAccess/Sessions/Sessions.vue b/src/views/SecurityAndAccess/Sessions/Sessions.vue
new file mode 100644
index 0000000..bd4c06a
--- /dev/null
+++ b/src/views/SecurityAndAccess/Sessions/Sessions.vue
@@ -0,0 +1,291 @@
+<template>
+  <b-container fluid="xl">
+    <page-title />
+    <b-row class="align-items-end">
+      <b-col sm="6" md="5" xl="4">
+        <search
+          :placeholder="$t('pageSessions.table.searchSessions')"
+          data-test-id="sessions-input-searchSessions"
+          @change-search="onChangeSearchInput"
+          @clear-search="onClearSearchInput"
+        />
+      </b-col>
+      <b-col sm="3" md="3" xl="2">
+        <table-cell-count
+          :filtered-items-count="filteredRows"
+          :total-number-of-cells="allConnections.length"
+        ></table-cell-count>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col>
+        <table-toolbar
+          ref="toolbar"
+          :selected-items-count="selectedRows.length"
+          :actions="batchActions"
+          @clear-selected="clearSelectedRows($refs.table)"
+          @batch-action="onBatchAction"
+        >
+        </table-toolbar>
+        <b-table
+          id="table-session-logs"
+          ref="table"
+          responsive="md"
+          selectable
+          no-select-on-click
+          hover
+          show-empty
+          sort-by="clientID"
+          :fields="fields"
+          :items="allConnections"
+          :filter="searchFilter"
+          :empty-text="$t('global.table.emptyMessage')"
+          :per-page="perPage"
+          :current-page="currentPage"
+          @filtered="onFiltered"
+          @row-selected="onRowSelected($event, allConnections.length)"
+        >
+          <!-- Checkbox column -->
+          <template #head(checkbox)>
+            <b-form-checkbox
+              v-model="tableHeaderCheckboxModel"
+              data-test-id="sessions-checkbox-selectAll"
+              :indeterminate="tableHeaderCheckboxIndeterminate"
+              @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"
+              :data-test-id="`sessions-checkbox-selectRow-${row.index}`"
+              @change="toggleSelectRow($refs.table, row.index)"
+            >
+              <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
+            </b-form-checkbox>
+          </template>
+
+          <!-- Actions column -->
+          <template #cell(actions)="row" class="ml-3">
+            <table-row-action
+              v-for="(action, index) in row.item.actions"
+              :key="index"
+              :value="action.value"
+              :title="action.title"
+              :row-data="row.item"
+              :btn-icon-only="false"
+              :data-test-id="`sessions-button-disconnect-${row.index}`"
+              @click-table-action="onTableRowAction($event, row.item)"
+            ></table-row-action>
+          </template>
+        </b-table>
+      </b-col>
+    </b-row>
+
+    <!-- Table pagination -->
+    <b-row>
+      <b-col sm="6">
+        <b-form-group
+          class="table-pagination-select"
+          :label="$t('global.table.itemsPerPage')"
+          label-for="pagination-items-per-page"
+        >
+          <b-form-select
+            id="pagination-items-per-page"
+            v-model="perPage"
+            :options="itemsPerPageOptions"
+          />
+        </b-form-group>
+      </b-col>
+      <b-col sm="6">
+        <b-pagination
+          v-model="currentPage"
+          first-number
+          last-number
+          :per-page="perPage"
+          :total-rows="getTotalRowCount(allConnections.length)"
+          aria-controls="table-session-logs"
+        />
+      </b-col>
+    </b-row>
+  </b-container>
+</template>
+
+<script>
+import PageTitle from '@/components/Global/PageTitle';
+import Search from '@/components/Global/Search';
+import TableCellCount from '@/components/Global/TableCellCount';
+import TableRowAction from '@/components/Global/TableRowAction';
+import TableToolbar from '@/components/Global/TableToolbar';
+
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+import BVPaginationMixin, {
+  currentPage,
+  perPage,
+  itemsPerPageOptions,
+} from '@/components/Mixins/BVPaginationMixin';
+import BVTableSelectableMixin, {
+  selectedRows,
+  tableHeaderCheckboxModel,
+  tableHeaderCheckboxIndeterminate,
+} from '@/components/Mixins/BVTableSelectableMixin';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import SearchFilterMixin, {
+  searchFilter,
+} from '@/components/Mixins/SearchFilterMixin';
+
+export default {
+  components: {
+    PageTitle,
+    Search,
+    TableCellCount,
+    TableRowAction,
+    TableToolbar,
+  },
+  mixins: [
+    BVPaginationMixin,
+    BVTableSelectableMixin,
+    BVToastMixin,
+    LoadingBarMixin,
+    SearchFilterMixin,
+  ],
+  beforeRouteLeave(to, from, next) {
+    // Hide loader if the user navigates to another page
+    // before request is fulfilled.
+    this.hideLoader();
+    next();
+  },
+  data() {
+    return {
+      fields: [
+        {
+          key: 'checkbox',
+        },
+        {
+          key: 'clientID',
+          label: this.$t('pageSessions.table.clientID'),
+        },
+        {
+          key: 'username',
+          label: this.$t('pageSessions.table.username'),
+        },
+        {
+          key: 'ipAddress',
+          label: this.$t('pageSessions.table.ipAddress'),
+        },
+        {
+          key: 'actions',
+          label: '',
+        },
+      ],
+      batchActions: [
+        {
+          value: 'disconnect',
+          label: this.$t('pageSessions.action.disconnect'),
+        },
+      ],
+      currentPage: currentPage,
+      itemsPerPageOptions: itemsPerPageOptions,
+      perPage: perPage,
+      selectedRows: selectedRows,
+      searchTotalFilteredRows: 0,
+      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
+      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
+      searchFilter: searchFilter,
+    };
+  },
+  computed: {
+    filteredRows() {
+      return this.searchFilter
+        ? this.searchTotalFilteredRows
+        : this.allConnections.length;
+    },
+    allConnections() {
+      return this.$store.getters['sessions/allConnections'].map((session) => {
+        return {
+          ...session,
+          actions: [
+            {
+              value: 'disconnect',
+              title: this.$t('pageSessions.action.disconnect'),
+            },
+          ],
+        };
+      });
+    },
+  },
+  created() {
+    this.startLoader();
+    this.$store
+      .dispatch('sessions/getSessionsData')
+      .finally(() => this.endLoader());
+  },
+  methods: {
+    onFiltered(filteredItems) {
+      this.searchTotalFilteredRows = filteredItems.length;
+    },
+    onChangeSearchInput(event) {
+      this.searchFilter = event;
+    },
+    disconnectSessions(uris) {
+      this.$store
+        .dispatch('sessions/disconnectSessions', uris)
+        .then((messages) => {
+          messages.forEach(({ type, message }) => {
+            if (type === 'success') {
+              this.successToast(message);
+            } else if (type === 'error') {
+              this.errorToast(message);
+            }
+          });
+        });
+    },
+    onTableRowAction(action, { uri }) {
+      if (action === 'disconnect') {
+        this.$bvModal
+          .msgBoxConfirm(this.$tc('pageSessions.modal.disconnectMessage'), {
+            title: this.$tc('pageSessions.modal.disconnectTitle'),
+            okTitle: this.$t('pageSessions.action.disconnect'),
+            cancelTitle: this.$t('global.action.cancel'),
+          })
+          .then((deleteConfirmed) => {
+            if (deleteConfirmed) this.disconnectSessions([uri]);
+          });
+      }
+    },
+    onBatchAction(action) {
+      if (action === 'disconnect') {
+        const uris = this.selectedRows.map((row) => row.uri);
+        this.$bvModal
+          .msgBoxConfirm(
+            this.$tc(
+              'pageSessions.modal.disconnectMessage',
+              this.selectedRows.length
+            ),
+            {
+              title: this.$tc(
+                'pageSessions.modal.disconnectTitle',
+                this.selectedRows.length
+              ),
+              okTitle: this.$t('pageSessions.action.disconnect'),
+              cancelTitle: this.$t('global.action.cancel'),
+            }
+          )
+          .then((deleteConfirmed) => {
+            if (deleteConfirmed) {
+              this.disconnectSessions(uris);
+            }
+          });
+      }
+    },
+  },
+};
+</script>
+<style lang="scss">
+#table-session-logs {
+  td .btn-link {
+    width: auto !important;
+  }
+}
+</style>
diff --git a/src/views/SecurityAndAccess/Sessions/index.js b/src/views/SecurityAndAccess/Sessions/index.js
new file mode 100644
index 0000000..aa113af
--- /dev/null
+++ b/src/views/SecurityAndAccess/Sessions/index.js
@@ -0,0 +1,2 @@
+import Sessions from './Sessions.vue';
+export default Sessions;
diff --git a/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue b/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue
new file mode 100644
index 0000000..0f05123
--- /dev/null
+++ b/src/views/SecurityAndAccess/UserManagement/ModalSettings.vue
@@ -0,0 +1,215 @@
+<template>
+  <b-modal
+    id="modal-settings"
+    ref="modal"
+    :title="$t('pageUserManagement.accountPolicySettings')"
+    @hidden="resetForm"
+  >
+    <b-form id="form-settings" novalidate @submit.prevent="handleSubmit">
+      <b-container>
+        <b-row>
+          <b-col>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.maxFailedLoginAttempts')"
+              label-for="lockout-threshold"
+            >
+              <b-form-text id="lockout-threshold-help-block">
+                {{
+                  $t('global.form.valueMustBeBetween', {
+                    min: 0,
+                    max: 65535,
+                  })
+                }}
+              </b-form-text>
+              <b-form-input
+                id="lockout-threshold"
+                v-model.number="form.lockoutThreshold"
+                type="number"
+                aria-describedby="lockout-threshold-help-block"
+                data-test-id="userManagement-input-lockoutThreshold"
+                :state="getValidationState($v.form.lockoutThreshold)"
+                @input="$v.form.lockoutThreshold.$touch()"
+              />
+              <b-form-invalid-feedback role="alert">
+                <template v-if="!$v.form.lockoutThreshold.required">
+                  {{ $t('global.form.fieldRequired') }}
+                </template>
+                <template
+                  v-if="
+                    !$v.form.lockoutThreshold.minLength ||
+                    !$v.form.lockoutThreshold.maxLength
+                  "
+                >
+                  {{
+                    $t('global.form.valueMustBeBetween', {
+                      min: 0,
+                      max: 65535,
+                    })
+                  }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-col>
+          <b-col>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.userUnlockMethod')"
+            >
+              <b-form-radio
+                v-model="form.unlockMethod"
+                name="unlock-method"
+                class="mb-2"
+                :value="0"
+                data-test-id="userManagement-radio-manualUnlock"
+                @input="$v.form.unlockMethod.$touch()"
+              >
+                {{ $t('pageUserManagement.modal.manual') }}
+              </b-form-radio>
+              <b-form-radio
+                v-model="form.unlockMethod"
+                name="unlock-method"
+                :value="1"
+                data-test-id="userManagement-radio-automaticUnlock"
+                @input="$v.form.unlockMethod.$touch()"
+              >
+                {{ $t('pageUserManagement.modal.automaticAfterTimeout') }}
+              </b-form-radio>
+              <div class="mt-3 ml-4">
+                <b-form-text id="lockout-duration-help-block">
+                  {{ $t('pageUserManagement.modal.timeoutDurationSeconds') }}
+                </b-form-text>
+                <b-form-input
+                  v-model.number="form.lockoutDuration"
+                  aria-describedby="lockout-duration-help-block"
+                  type="number"
+                  data-test-id="userManagement-input-lockoutDuration"
+                  :state="getValidationState($v.form.lockoutDuration)"
+                  :readonly="$v.form.unlockMethod.$model === 0"
+                  @input="$v.form.lockoutDuration.$touch()"
+                />
+                <b-form-invalid-feedback role="alert">
+                  <template v-if="!$v.form.lockoutDuration.required">
+                    {{ $t('global.form.fieldRequired') }}
+                  </template>
+                  <template v-else-if="!$v.form.lockoutDuration.minvalue">
+                    {{ $t('global.form.mustBeAtLeast', { value: 1 }) }}
+                  </template>
+                </b-form-invalid-feedback>
+              </div>
+            </b-form-group>
+          </b-col>
+        </b-row>
+      </b-container>
+    </b-form>
+    <template #modal-footer="{ cancel }">
+      <b-button
+        variant="secondary"
+        data-test-id="userManagement-button-cancel"
+        @click="cancel()"
+      >
+        {{ $t('global.action.cancel') }}
+      </b-button>
+      <b-button
+        form="form-settings"
+        type="submit"
+        variant="primary"
+        data-test-id="userManagement-button-submit"
+        @click="onOk"
+      >
+        {{ $t('global.action.save') }}
+      </b-button>
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+import {
+  required,
+  requiredIf,
+  minValue,
+  maxValue,
+} from 'vuelidate/lib/validators';
+
+export default {
+  mixins: [VuelidateMixin],
+  props: {
+    settings: {
+      type: Object,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      form: {
+        lockoutThreshold: 0,
+        unlockMethod: 0,
+        lockoutDuration: null,
+      },
+    };
+  },
+  watch: {
+    settings: function ({ lockoutThreshold, lockoutDuration }) {
+      this.form.lockoutThreshold = lockoutThreshold;
+      this.form.unlockMethod = lockoutDuration ? 1 : 0;
+      this.form.lockoutDuration = lockoutDuration ? lockoutDuration : null;
+    },
+  },
+  validations: {
+    form: {
+      lockoutThreshold: {
+        minValue: minValue(0),
+        maxValue: maxValue(65535),
+        required,
+      },
+      unlockMethod: { required },
+      lockoutDuration: {
+        minValue: function (value) {
+          return this.form.unlockMethod === 0 || value > 0;
+        },
+        required: requiredIf(function () {
+          return this.form.unlockMethod === 1;
+        }),
+      },
+    },
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+
+      let lockoutThreshold;
+      let lockoutDuration;
+      if (this.$v.form.lockoutThreshold.$dirty) {
+        lockoutThreshold = this.form.lockoutThreshold;
+      }
+      if (this.$v.form.unlockMethod.$dirty) {
+        lockoutDuration = this.form.unlockMethod
+          ? this.form.lockoutDuration
+          : 0;
+      }
+
+      this.$emit('ok', { lockoutThreshold, lockoutDuration });
+      this.closeModal();
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      // Reset form models
+      this.form.lockoutThreshold = this.settings.lockoutThreshold;
+      this.form.unlockMethod = this.settings.lockoutDuration ? 1 : 0;
+      this.form.lockoutDuration = this.settings.lockoutDuration
+        ? this.settings.lockoutDuration
+        : null;
+      this.$v.$reset(); // clear validations
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/UserManagement/ModalUser.vue b/src/views/SecurityAndAccess/UserManagement/ModalUser.vue
new file mode 100644
index 0000000..0f8757c
--- /dev/null
+++ b/src/views/SecurityAndAccess/UserManagement/ModalUser.vue
@@ -0,0 +1,386 @@
+<template>
+  <b-modal id="modal-user" ref="modal" @hidden="resetForm">
+    <template #modal-title>
+      <template v-if="newUser">
+        {{ $t('pageUserManagement.addUser') }}
+      </template>
+      <template v-else>
+        {{ $t('pageUserManagement.editUser') }}
+      </template>
+    </template>
+    <b-form id="form-user" novalidate @submit.prevent="handleSubmit">
+      <b-container>
+        <!-- Manual unlock form control -->
+        <b-row v-if="!newUser && manualUnlockPolicy && user.Locked">
+          <b-col sm="9">
+            <alert :show="true" variant="warning" small>
+              <template v-if="!$v.form.manualUnlock.$dirty">
+                {{ $t('pageUserManagement.modal.accountLocked') }}
+              </template>
+              <template v-else>
+                {{ $t('pageUserManagement.modal.clickSaveToUnlockAccount') }}
+              </template>
+            </alert>
+          </b-col>
+          <b-col sm="3">
+            <input
+              v-model="form.manualUnlock"
+              data-test-id="userManagement-input-manualUnlock"
+              type="hidden"
+              value="false"
+            />
+            <b-button
+              variant="primary"
+              :disabled="$v.form.manualUnlock.$dirty"
+              data-test-id="userManagement-button-manualUnlock"
+              @click="$v.form.manualUnlock.$touch()"
+            >
+              {{ $t('pageUserManagement.modal.unlock') }}
+            </b-button>
+          </b-col>
+        </b-row>
+        <b-row>
+          <b-col>
+            <b-form-group :label="$t('pageUserManagement.modal.accountStatus')">
+              <b-form-radio
+                v-model="form.status"
+                name="user-status"
+                :value="true"
+                data-test-id="userManagement-radioButton-statusEnabled"
+                @input="$v.form.status.$touch()"
+              >
+                {{ $t('global.status.enabled') }}
+              </b-form-radio>
+              <b-form-radio
+                v-model="form.status"
+                name="user-status"
+                data-test-id="userManagement-radioButton-statusDisabled"
+                :value="false"
+                @input="$v.form.status.$touch()"
+              >
+                {{ $t('global.status.disabled') }}
+              </b-form-radio>
+            </b-form-group>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.username')"
+              label-for="username"
+            >
+              <b-form-text id="username-help-block">
+                {{ $t('pageUserManagement.modal.cannotStartWithANumber') }}
+                <br />
+                {{
+                  $t(
+                    'pageUserManagement.modal.noSpecialCharactersExceptUnderscore'
+                  )
+                }}
+              </b-form-text>
+              <b-form-input
+                id="username"
+                v-model="form.username"
+                type="text"
+                aria-describedby="username-help-block"
+                data-test-id="userManagement-input-username"
+                :state="getValidationState($v.form.username)"
+                :disabled="!newUser && originalUsername === 'root'"
+                @input="$v.form.username.$touch()"
+              />
+              <b-form-invalid-feedback role="alert">
+                <template v-if="!$v.form.username.required">
+                  {{ $t('global.form.fieldRequired') }}
+                </template>
+                <template v-else-if="!$v.form.username.maxLength">
+                  {{
+                    $t('global.form.lengthMustBeBetween', { min: 1, max: 16 })
+                  }}
+                </template>
+                <template v-else-if="!$v.form.username.pattern">
+                  {{ $t('global.form.invalidFormat') }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.privilege')"
+              label-for="privilege"
+            >
+              <b-form-select
+                id="privilege"
+                v-model="form.privilege"
+                :options="privilegeTypes"
+                data-test-id="userManagement-select-privilege"
+                :state="getValidationState($v.form.privilege)"
+                @input="$v.form.privilege.$touch()"
+              >
+                <template #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">
+                <template v-if="!$v.form.privilege.required">
+                  {{ $t('global.form.fieldRequired') }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-col>
+          <b-col>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.userPassword')"
+              label-for="password"
+            >
+              <b-form-text id="password-help-block">
+                {{
+                  $t('pageUserManagement.modal.passwordMustBeBetween', {
+                    min: passwordRequirements.minLength,
+                    max: passwordRequirements.maxLength,
+                  })
+                }}
+              </b-form-text>
+              <input-password-toggle>
+                <b-form-input
+                  id="password"
+                  v-model="form.password"
+                  type="password"
+                  data-test-id="userManagement-input-password"
+                  aria-describedby="password-help-block"
+                  :state="getValidationState($v.form.password)"
+                  class="form-control-with-button"
+                  @input="$v.form.password.$touch()"
+                />
+                <b-form-invalid-feedback role="alert">
+                  <template v-if="!$v.form.password.required">
+                    {{ $t('global.form.fieldRequired') }}
+                  </template>
+                  <template
+                    v-if="
+                      !$v.form.password.minLength || !$v.form.password.maxLength
+                    "
+                  >
+                    {{
+                      $t('pageUserManagement.modal.passwordMustBeBetween', {
+                        min: passwordRequirements.minLength,
+                        max: passwordRequirements.maxLength,
+                      })
+                    }}
+                  </template>
+                </b-form-invalid-feedback>
+              </input-password-toggle>
+            </b-form-group>
+            <b-form-group
+              :label="$t('pageUserManagement.modal.confirmUserPassword')"
+              label-for="password-confirmation"
+            >
+              <input-password-toggle>
+                <b-form-input
+                  id="password-confirmation"
+                  v-model="form.passwordConfirmation"
+                  data-test-id="userManagement-input-passwordConfirmation"
+                  type="password"
+                  :state="getValidationState($v.form.passwordConfirmation)"
+                  class="form-control-with-button"
+                  @input="$v.form.passwordConfirmation.$touch()"
+                />
+                <b-form-invalid-feedback role="alert">
+                  <template v-if="!$v.form.passwordConfirmation.required">
+                    {{ $t('global.form.fieldRequired') }}
+                  </template>
+                  <template
+                    v-else-if="!$v.form.passwordConfirmation.sameAsPassword"
+                  >
+                    {{ $t('pageUserManagement.modal.passwordsDoNotMatch') }}
+                  </template>
+                </b-form-invalid-feedback>
+              </input-password-toggle>
+            </b-form-group>
+          </b-col>
+        </b-row>
+      </b-container>
+    </b-form>
+    <template #modal-footer="{ cancel }">
+      <b-button
+        variant="secondary"
+        data-test-id="userManagement-button-cancel"
+        @click="cancel()"
+      >
+        {{ $t('global.action.cancel') }}
+      </b-button>
+      <b-button
+        form="form-user"
+        data-test-id="userManagement-button-submit"
+        type="submit"
+        variant="primary"
+        @click="onOk"
+      >
+        <template v-if="newUser">
+          {{ $t('pageUserManagement.addUser') }}
+        </template>
+        <template v-else>
+          {{ $t('global.action.save') }}
+        </template>
+      </b-button>
+    </template>
+  </b-modal>
+</template>
+
+<script>
+import {
+  required,
+  maxLength,
+  minLength,
+  sameAs,
+  helpers,
+  requiredIf,
+} from 'vuelidate/lib/validators';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
+import Alert from '@/components/Global/Alert';
+
+export default {
+  components: { Alert, InputPasswordToggle },
+  mixins: [VuelidateMixin],
+  props: {
+    user: {
+      type: Object,
+      default: null,
+    },
+    passwordRequirements: {
+      type: Object,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      originalUsername: '',
+      form: {
+        status: true,
+        username: '',
+        privilege: null,
+        password: '',
+        passwordConfirmation: '',
+        manualUnlock: false,
+      },
+    };
+  },
+  computed: {
+    newUser() {
+      return this.user ? false : true;
+    },
+    accountSettings() {
+      return this.$store.getters['userManagement/accountSettings'];
+    },
+    manualUnlockPolicy() {
+      return !this.accountSettings.accountLockoutDuration;
+    },
+    privilegeTypes() {
+      return this.$store.getters['userManagement/accountRoles'];
+    },
+  },
+  watch: {
+    user: function (value) {
+      if (value === null) return;
+      this.originalUsername = value.username;
+      this.form.username = value.username;
+      this.form.status = value.Enabled;
+      this.form.privilege = value.privilege;
+    },
+  },
+  validations() {
+    return {
+      form: {
+        status: {
+          required,
+        },
+        username: {
+          required,
+          maxLength: maxLength(16),
+          pattern: helpers.regex('pattern', /^([a-zA-Z_][a-zA-Z0-9_]*)/),
+        },
+        privilege: {
+          required,
+        },
+        password: {
+          required: requiredIf(function () {
+            return this.requirePassword();
+          }),
+          minLength: minLength(this.passwordRequirements.minLength),
+          maxLength: maxLength(this.passwordRequirements.maxLength),
+        },
+        passwordConfirmation: {
+          required: requiredIf(function () {
+            return this.requirePassword();
+          }),
+          sameAsPassword: sameAs('password'),
+        },
+        manualUnlock: {},
+      },
+    };
+  },
+  methods: {
+    handleSubmit() {
+      let userData = {};
+
+      if (this.newUser) {
+        this.$v.$touch();
+        if (this.$v.$invalid) return;
+        userData.username = this.form.username;
+        userData.status = this.form.status;
+        userData.privilege = this.form.privilege;
+        userData.password = this.form.password;
+      } else {
+        if (this.$v.$invalid) return;
+        userData.originalUsername = this.originalUsername;
+        if (this.$v.form.status.$dirty) {
+          userData.status = this.form.status;
+        }
+        if (this.$v.form.username.$dirty) {
+          userData.username = this.form.username;
+        }
+        if (this.$v.form.privilege.$dirty) {
+          userData.privilege = this.form.privilege;
+        }
+        if (this.$v.form.password.$dirty) {
+          userData.password = this.form.password;
+        }
+        if (this.$v.form.manualUnlock.$dirty) {
+          // If form manualUnlock control $dirty then
+          // set user Locked property to false
+          userData.locked = false;
+        }
+        if (Object.entries(userData).length === 1) {
+          this.closeModal();
+          return;
+        }
+      }
+
+      this.$emit('ok', { isNewUser: this.newUser, userData });
+      this.closeModal();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      this.form.originalUsername = '';
+      this.form.status = true;
+      this.form.username = '';
+      this.form.privilege = null;
+      this.form.password = '';
+      this.form.passwordConfirmation = '';
+      this.$v.$reset();
+      this.$emit('hidden');
+    },
+    requirePassword() {
+      if (this.newUser) return true;
+      if (this.$v.form.password.$dirty) return true;
+      if (this.$v.form.passwordConfirmation.$dirty) return true;
+      return false;
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/UserManagement/TableRoles.vue b/src/views/SecurityAndAccess/UserManagement/TableRoles.vue
new file mode 100644
index 0000000..61ef1ee
--- /dev/null
+++ b/src/views/SecurityAndAccess/UserManagement/TableRoles.vue
@@ -0,0 +1,92 @@
+<template>
+  <b-table stacked="sm" hover small :items="items" :fields="fields">
+    <template #cell(administrator)="data">
+      <template v-if="data.value">
+        <checkmark20 />
+      </template>
+    </template>
+    <template #cell(operator)="data">
+      <template v-if="data.value">
+        <checkmark20 />
+      </template>
+    </template>
+    <template #cell(readonly)="data">
+      <template v-if="data.value">
+        <checkmark20 />
+      </template>
+    </template>
+    <template #cell(noaccess)="data">
+      <template v-if="data.value">
+        <checkmark20 />
+      </template>
+    </template>
+  </b-table>
+</template>
+
+<script>
+import Checkmark20 from '@carbon/icons-vue/es/checkmark/20';
+
+export default {
+  components: {
+    Checkmark20,
+  },
+  data() {
+    return {
+      items: [
+        {
+          description: this.$t(
+            'pageUserManagement.tableRoles.configureComponentsManagedByThisService'
+          ),
+          administrator: true,
+          operator: true,
+          readonly: false,
+          noaccess: false,
+        },
+        {
+          description: this.$t(
+            'pageUserManagement.tableRoles.configureManagerResources'
+          ),
+          administrator: true,
+          operator: false,
+          readonly: false,
+          noaccess: false,
+        },
+        {
+          description: this.$t(
+            'pageUserManagement.tableRoles.updatePasswordForCurrentUserAccount'
+          ),
+          administrator: true,
+          operator: true,
+          readonly: true,
+          noaccess: false,
+        },
+        {
+          description: this.$t(
+            'pageUserManagement.tableRoles.configureUsersAndTheirAccounts'
+          ),
+          administrator: true,
+          operator: false,
+          readonly: false,
+          noaccess: false,
+        },
+        {
+          description: this.$t(
+            'pageUserManagement.tableRoles.logInToTheServiceAndReadResources'
+          ),
+          administrator: true,
+          operator: true,
+          readonly: true,
+          noaccess: false,
+        },
+      ],
+      fields: [
+        { key: 'description', label: 'Privilege' },
+        { key: 'administrator', label: 'Administrator', class: 'text-center' },
+        { key: 'operator', label: 'Operator', class: 'text-center' },
+        { key: 'readonly', label: 'ReadOnly', class: 'text-center' },
+        { key: 'noaccess', label: 'NoAccess', class: 'text-center' },
+      ],
+    };
+  },
+};
+</script>
diff --git a/src/views/SecurityAndAccess/UserManagement/UserManagement.vue b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
new file mode 100644
index 0000000..015fee9
--- /dev/null
+++ b/src/views/SecurityAndAccess/UserManagement/UserManagement.vue
@@ -0,0 +1,388 @@
+<template>
+  <b-container fluid="xl">
+    <page-title />
+    <b-row>
+      <b-col xl="9" class="text-right">
+        <b-button variant="link" @click="initModalSettings">
+          <icon-settings />
+          {{ $t('pageUserManagement.accountPolicySettings') }}
+        </b-button>
+        <b-button
+          variant="primary"
+          data-test-id="userManagement-button-addUser"
+          @click="initModalUser(null)"
+        >
+          <icon-add />
+          {{ $t('pageUserManagement.addUser') }}
+        </b-button>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col xl="9">
+        <table-toolbar
+          ref="toolbar"
+          :selected-items-count="selectedRows.length"
+          :actions="tableToolbarActions"
+          @clear-selected="clearSelectedRows($refs.table)"
+          @batch-action="onBatchAction"
+        />
+        <b-table
+          ref="table"
+          responsive="md"
+          selectable
+          show-empty
+          no-select-on-click
+          hover
+          :fields="fields"
+          :items="tableItems"
+          :empty-text="$t('global.table.emptyMessage')"
+          @row-selected="onRowSelected($event, tableItems.length)"
+        >
+          <!-- Checkbox column -->
+          <template #head(checkbox)>
+            <b-form-checkbox
+              v-model="tableHeaderCheckboxModel"
+              data-test-id="userManagement-checkbox-tableHeaderCheckbox"
+              :indeterminate="tableHeaderCheckboxIndeterminate"
+              @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"
+              data-test-id="userManagement-checkbox-toggleSelectRow"
+              @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'"
+                  :data-test-id="`userManagement-tableRowAction-edit-${index}`"
+                />
+                <icon-trashcan
+                  v-if="action.value === 'delete'"
+                  :data-test-id="`userManagement-tableRowAction-delete-${index}`"
+                />
+              </template>
+            </table-row-action>
+          </template>
+        </b-table>
+      </b-col>
+    </b-row>
+    <b-row>
+      <b-col xl="8">
+        <b-button
+          v-b-toggle.collapse-role-table
+          data-test-id="userManagement-button-viewPrivilegeRoleDescriptions"
+          variant="link"
+          class="mt-3"
+        >
+          <icon-chevron />
+          {{ $t('pageUserManagement.viewPrivilegeRoleDescriptions') }}
+        </b-button>
+        <b-collapse id="collapse-role-table" class="mt-3">
+          <table-roles />
+        </b-collapse>
+      </b-col>
+    </b-row>
+    <!-- Modals -->
+    <modal-settings :settings="settings" @ok="saveAccountSettings" />
+    <modal-user
+      :user="activeUser"
+      :password-requirements="passwordRequirements"
+      @ok="saveUser"
+      @hidden="activeUser = null"
+    />
+  </b-container>
+</template>
+
+<script>
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+import IconEdit from '@carbon/icons-vue/es/edit/20';
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconSettings from '@carbon/icons-vue/es/settings/20';
+import IconChevron from '@carbon/icons-vue/es/chevron--up/20';
+
+import ModalUser from './ModalUser';
+import ModalSettings from './ModalSettings';
+import PageTitle from '@/components/Global/PageTitle';
+import TableRoles from './TableRoles';
+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 LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+
+export default {
+  name: 'UserManagement',
+  components: {
+    IconAdd,
+    IconChevron,
+    IconEdit,
+    IconSettings,
+    IconTrashcan,
+    ModalSettings,
+    ModalUser,
+    PageTitle,
+    TableRoles,
+    TableRowAction,
+    TableToolbar,
+  },
+  mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
+  beforeRouteLeave(to, from, next) {
+    this.hideLoader();
+    next();
+  },
+  data() {
+    return {
+      activeUser: null,
+      fields: [
+        {
+          key: 'checkbox',
+        },
+        {
+          key: 'username',
+          label: this.$t('pageUserManagement.table.username'),
+        },
+        {
+          key: 'privilege',
+          label: this.$t('pageUserManagement.table.privilege'),
+        },
+        {
+          key: 'status',
+          label: this.$t('pageUserManagement.table.status'),
+        },
+        {
+          key: 'actions',
+          label: '',
+          tdClass: 'text-right text-nowrap',
+        },
+      ],
+      tableToolbarActions: [
+        {
+          value: 'delete',
+          label: this.$t('global.action.delete'),
+        },
+        {
+          value: 'enable',
+          label: this.$t('global.action.enable'),
+        },
+        {
+          value: 'disable',
+          label: this.$t('global.action.disable'),
+        },
+      ],
+      selectedRows: selectedRows,
+      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
+      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
+    };
+  },
+  computed: {
+    allUsers() {
+      return this.$store.getters['userManagement/allUsers'];
+    },
+    tableItems() {
+      // transform user data to table data
+      return this.allUsers.map((user) => {
+        return {
+          username: user.UserName,
+          privilege: user.RoleId,
+          status: user.Locked
+            ? 'Locked'
+            : user.Enabled
+            ? 'Enabled'
+            : 'Disabled',
+          actions: [
+            {
+              value: 'edit',
+              enabled: true,
+              title: this.$t('pageUserManagement.editUser'),
+            },
+            {
+              value: 'delete',
+              enabled: user.UserName === 'root' ? false : true,
+              title: this.$tc('pageUserManagement.deleteUser'),
+            },
+          ],
+          ...user,
+        };
+      });
+    },
+    settings() {
+      return this.$store.getters['userManagement/accountSettings'];
+    },
+    passwordRequirements() {
+      return this.$store.getters['userManagement/accountPasswordRequirements'];
+    },
+  },
+  created() {
+    this.startLoader();
+    this.$store
+      .dispatch('userManagement/getUsers')
+      .finally(() => this.endLoader());
+    this.$store.dispatch('userManagement/getAccountSettings');
+    this.$store.dispatch('userManagement/getAccountRoles');
+  },
+  methods: {
+    initModalUser(user) {
+      this.activeUser = user;
+      this.$bvModal.show('modal-user');
+    },
+    initModalDelete(user) {
+      this.$bvModal
+        .msgBoxConfirm(
+          this.$t('pageUserManagement.modal.deleteConfirmMessage', {
+            user: user.username,
+          }),
+          {
+            title: this.$tc('pageUserManagement.deleteUser'),
+            okTitle: this.$tc('pageUserManagement.deleteUser'),
+            cancelTitle: this.$t('global.action.cancel'),
+          }
+        )
+        .then((deleteConfirmed) => {
+          if (deleteConfirmed) {
+            this.deleteUser(user);
+          }
+        });
+    },
+    initModalSettings() {
+      this.$bvModal.show('modal-settings');
+    },
+    saveUser({ isNewUser, userData }) {
+      this.startLoader();
+      if (isNewUser) {
+        this.$store
+          .dispatch('userManagement/createUser', userData)
+          .then((success) => this.successToast(success))
+          .catch(({ message }) => this.errorToast(message))
+          .finally(() => this.endLoader());
+      } else {
+        this.$store
+          .dispatch('userManagement/updateUser', userData)
+          .then((success) => this.successToast(success))
+          .catch(({ message }) => this.errorToast(message))
+          .finally(() => this.endLoader());
+      }
+    },
+    deleteUser({ username }) {
+      this.startLoader();
+      this.$store
+        .dispatch('userManagement/deleteUser', username)
+        .then((success) => this.successToast(success))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+    onBatchAction(action) {
+      switch (action) {
+        case 'delete':
+          this.$bvModal
+            .msgBoxConfirm(
+              this.$tc(
+                'pageUserManagement.modal.batchDeleteConfirmMessage',
+                this.selectedRows.length
+              ),
+              {
+                title: this.$tc(
+                  'pageUserManagement.deleteUser',
+                  this.selectedRows.length
+                ),
+                okTitle: this.$tc(
+                  'pageUserManagement.deleteUser',
+                  this.selectedRows.length
+                ),
+                cancelTitle: this.$t('global.action.cancel'),
+              }
+            )
+            .then((deleteConfirmed) => {
+              if (deleteConfirmed) {
+                this.startLoader();
+                this.$store
+                  .dispatch('userManagement/deleteUsers', this.selectedRows)
+                  .then((messages) => {
+                    messages.forEach(({ type, message }) => {
+                      if (type === 'success') this.successToast(message);
+                      if (type === 'error') this.errorToast(message);
+                    });
+                  })
+                  .finally(() => this.endLoader());
+              }
+            });
+          break;
+        case 'enable':
+          this.startLoader();
+          this.$store
+            .dispatch('userManagement/enableUsers', this.selectedRows)
+            .then((messages) => {
+              messages.forEach(({ type, message }) => {
+                if (type === 'success') this.successToast(message);
+                if (type === 'error') this.errorToast(message);
+              });
+            })
+            .finally(() => this.endLoader());
+          break;
+        case 'disable':
+          this.startLoader();
+          this.$store
+            .dispatch('userManagement/disableUsers', this.selectedRows)
+            .then((messages) => {
+              messages.forEach(({ type, message }) => {
+                if (type === 'success') this.successToast(message);
+                if (type === 'error') this.errorToast(message);
+              });
+            })
+            .finally(() => this.endLoader());
+          break;
+      }
+    },
+    onTableRowAction(action, row) {
+      switch (action) {
+        case 'edit':
+          this.initModalUser(row);
+          break;
+        case 'delete':
+          this.initModalDelete(row);
+          break;
+        default:
+          break;
+      }
+    },
+    saveAccountSettings(settings) {
+      this.startLoader();
+      this.$store
+        .dispatch('userManagement/saveAccountSettings', settings)
+        .then((message) => this.successToast(message))
+        .catch(({ message }) => this.errorToast(message))
+        .finally(() => this.endLoader());
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.btn.collapsed {
+  svg {
+    transform: rotate(180deg);
+  }
+}
+</style>
diff --git a/src/views/SecurityAndAccess/UserManagement/index.js b/src/views/SecurityAndAccess/UserManagement/index.js
new file mode 100644
index 0000000..c3aebec
--- /dev/null
+++ b/src/views/SecurityAndAccess/UserManagement/index.js
@@ -0,0 +1,2 @@
+import UserManagement from './UserManagement.vue';
+export default UserManagement;