xref: /openbmc/webui-vue/src/views/SecurityAndAccess/UserManagement/UserManagement.vue (revision de23ea23d88451a2fa2774ec72053772603c23ae)
1<template>
2  <b-container fluid="xl">
3    <page-title />
4    <b-row>
5      <b-col xl="9" class="text-right">
6        <b-button variant="link" @click="initModalSettings">
7          <icon-settings />
8          {{ $t('pageUserManagement.accountPolicySettings') }}
9        </b-button>
10        <b-button
11          variant="primary"
12          data-test-id="userManagement-button-addUser"
13          @click="initModalUser(null)"
14        >
15          <icon-add />
16          {{ $t('pageUserManagement.addUser') }}
17        </b-button>
18      </b-col>
19    </b-row>
20    <b-row>
21      <b-col xl="9">
22        <table-toolbar
23          ref="toolbar"
24          :selected-items-count="selectedRows.length"
25          :actions="tableToolbarActions"
26          @clear-selected="clearSelectedRows($refs.table)"
27          @batch-action="onBatchAction"
28        />
29        <b-table
30          ref="table"
31          responsive="md"
32          selectable
33          show-empty
34          no-select-on-click
35          hover
36          :busy="isBusy"
37          :fields="fields"
38          :items="tableItems"
39          :empty-text="$t('global.table.emptyMessage')"
40          @row-selected="onRowSelected($event, tableItems.length)"
41        >
42          <!-- Checkbox column -->
43          <template #head(checkbox)>
44            <b-form-checkbox
45              v-model="tableHeaderCheckboxModel"
46              data-test-id="userManagement-checkbox-tableHeaderCheckbox"
47              :indeterminate="tableHeaderCheckboxIndeterminate"
48              @change="onChangeHeaderCheckbox($refs.table)"
49            >
50              <span class="sr-only">{{ $t('global.table.selectAll') }}</span>
51            </b-form-checkbox>
52          </template>
53          <template #cell(checkbox)="row">
54            <b-form-checkbox
55              v-model="row.rowSelected"
56              data-test-id="userManagement-checkbox-toggleSelectRow"
57              @change="toggleSelectRow($refs.table, row.index)"
58            >
59              <span class="sr-only">{{ $t('global.table.selectItem') }}</span>
60            </b-form-checkbox>
61          </template>
62
63          <!-- table actions column -->
64          <template #cell(actions)="{ item }">
65            <table-row-action
66              v-for="(action, index) in item.actions"
67              :key="index"
68              :value="action.value"
69              :enabled="action.enabled"
70              :title="action.title"
71              @click-table-action="onTableRowAction($event, item)"
72            >
73              <template #icon>
74                <icon-edit
75                  v-if="action.value === 'edit'"
76                  :data-test-id="`userManagement-tableRowAction-edit-${index}`"
77                />
78                <icon-trashcan
79                  v-if="action.value === 'delete'"
80                  :data-test-id="`userManagement-tableRowAction-delete-${index}`"
81                />
82              </template>
83            </table-row-action>
84          </template>
85        </b-table>
86      </b-col>
87    </b-row>
88    <b-row>
89      <b-col xl="8">
90        <b-button
91          v-b-toggle.collapse-role-table
92          data-test-id="userManagement-button-viewPrivilegeRoleDescriptions"
93          variant="link"
94          class="mt-3"
95        >
96          <icon-chevron />
97          {{ $t('pageUserManagement.viewPrivilegeRoleDescriptions') }}
98        </b-button>
99        <b-collapse id="collapse-role-table" class="mt-3">
100          <table-roles />
101        </b-collapse>
102      </b-col>
103    </b-row>
104    <!-- Modals -->
105    <modal-settings :settings="setting" @ok="saveAccountSettings" />
106    <modal-user
107      :user="activeUser"
108      :password-requirements="passwordRequirements"
109      @ok="saveUser"
110      @hidden="activeUser = null"
111    />
112  </b-container>
113</template>
114
115<script>
116import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
117import IconEdit from '@carbon/icons-vue/es/edit/20';
118import IconAdd from '@carbon/icons-vue/es/add--alt/20';
119import IconSettings from '@carbon/icons-vue/es/settings/20';
120import IconChevron from '@carbon/icons-vue/es/chevron--up/20';
121
122import ModalUser from './ModalUser';
123import ModalSettings from './ModalSettings';
124import PageTitle from '@/components/Global/PageTitle';
125import TableRoles from './TableRoles';
126import TableToolbar from '@/components/Global/TableToolbar';
127import TableRowAction from '@/components/Global/TableRowAction';
128
129import BVTableSelectableMixin, {
130  selectedRows,
131  tableHeaderCheckboxModel,
132  tableHeaderCheckboxIndeterminate,
133} from '@/components/Mixins/BVTableSelectableMixin';
134import BVToastMixin from '@/components/Mixins/BVToastMixin';
135import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
136import { useI18n } from 'vue-i18n';
137import i18n from '@/i18n';
138
139export default {
140  name: 'UserManagement',
141  components: {
142    IconAdd,
143    IconChevron,
144    IconEdit,
145    IconSettings,
146    IconTrashcan,
147    ModalSettings,
148    ModalUser,
149    PageTitle,
150    TableRoles,
151    TableRowAction,
152    TableToolbar,
153  },
154  mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
155  beforeRouteLeave(to, from, next) {
156    this.hideLoader();
157    next();
158  },
159  data() {
160    return {
161      $t: useI18n().t,
162      isBusy: true,
163      activeUser: null,
164      setting: {},
165      fields: [
166        {
167          key: 'checkbox',
168        },
169        {
170          key: 'username',
171          label: i18n.global.t('pageUserManagement.table.username'),
172        },
173        {
174          key: 'privilege',
175          label: i18n.global.t('pageUserManagement.table.privilege'),
176        },
177        {
178          key: 'status',
179          label: i18n.global.t('pageUserManagement.table.status'),
180        },
181        {
182          key: 'actions',
183          label: '',
184          tdClass: 'text-right text-nowrap',
185        },
186      ],
187      tableToolbarActions: [
188        {
189          value: 'delete',
190          label: i18n.global.t('global.action.delete'),
191        },
192        {
193          value: 'enable',
194          label: i18n.global.t('global.action.enable'),
195        },
196        {
197          value: 'disable',
198          label: i18n.global.t('global.action.disable'),
199        },
200      ],
201      selectedRows: selectedRows,
202      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
203      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
204    };
205  },
206  computed: {
207    allUsers() {
208      return this.$store.getters['userManagement/allUsers'];
209    },
210    tableItems() {
211      // transform user data to table data
212      return this.allUsers.map((user) => {
213        return {
214          username: user.UserName,
215          privilege: user.RoleId,
216          status: user.Locked
217            ? 'Locked'
218            : user.Enabled
219              ? 'Enabled'
220              : 'Disabled',
221          actions: [
222            {
223              value: 'edit',
224              enabled: this.editEnable(user),
225              title: i18n.global.t('pageUserManagement.editUser'),
226            },
227            {
228              value: 'delete',
229              enabled:
230                user.UserName === this.$store.getters['global/username']
231                  ? false
232                  : true && user.UserName === 'root'
233                    ? false
234                    : true,
235              title: i18n.global.t('pageUserManagement.deleteUser'),
236            },
237          ],
238          ...user,
239        };
240      });
241    },
242    settings() {
243      return this.$store.getters['userManagement/accountSettings'];
244    },
245    passwordRequirements() {
246      return this.$store.getters['userManagement/accountPasswordRequirements'];
247    },
248  },
249  created() {
250    this.startLoader();
251    this.$store.dispatch('userManagement/getUsers').finally(() => {
252      this.endLoader();
253      this.isBusy = false;
254    });
255    this.$store.dispatch('userManagement/getAccountSettings');
256    this.$store.dispatch('userManagement/getAccountRoles');
257  },
258  methods: {
259    editEnable(user) {
260      if ('root' === this.$store.getters['global/username']) {
261        return true;
262      } else {
263        return user.UserName === 'root' ? false : true;
264      }
265    },
266    initModalUser(user) {
267      this.activeUser = user;
268      this.$bvModal.show('modal-user');
269    },
270    initModalDelete(user) {
271      this.$bvModal
272        .msgBoxConfirm(
273          i18n.global.t('pageUserManagement.modal.deleteConfirmMessage', {
274            user: user.username,
275          }),
276          {
277            title: i18n.global.t('pageUserManagement.deleteUser'),
278            okTitle: i18n.global.t('pageUserManagement.deleteUser'),
279            cancelTitle: i18n.global.t('global.action.cancel'),
280            autoFocusButton: 'ok',
281          },
282        )
283        .then((deleteConfirmed) => {
284          if (deleteConfirmed) {
285            this.deleteUser(user);
286          }
287        });
288    },
289    initModalSettings() {
290      this.setting = this.settings;
291      this.$bvModal.show('modal-settings');
292    },
293    saveUser({ isNewUser, userData }) {
294      this.startLoader();
295      if (isNewUser) {
296        this.$store
297          .dispatch('userManagement/createUser', userData)
298          .then((success) => this.successToast(success))
299          .catch(({ message }) => this.errorToast(message))
300          .finally(() => this.endLoader());
301      } else {
302        this.$store
303          .dispatch('userManagement/updateUser', userData)
304          .then((success) => this.successToast(success))
305          .catch(({ message }) => this.errorToast(message))
306          .finally(() => this.endLoader());
307      }
308    },
309    deleteUser({ username }) {
310      this.startLoader();
311      this.$store
312        .dispatch('userManagement/deleteUser', username)
313        .then((success) => this.successToast(success))
314        .catch(({ message }) => this.errorToast(message))
315        .finally(() => this.endLoader());
316    },
317    onBatchAction(action) {
318      switch (action) {
319        case 'delete':
320          this.$bvModal
321            .msgBoxConfirm(
322              i18n.global.t(
323                'pageUserManagement.modal.batchDeleteConfirmMessage',
324                this.selectedRows.length,
325              ),
326              {
327                title: i18n.global.t(
328                  'pageUserManagement.deleteUser',
329                  this.selectedRows.length,
330                ),
331                okTitle: i18n.global.t(
332                  'pageUserManagement.deleteUser',
333                  this.selectedRows.length,
334                ),
335                cancelTitle: i18n.global.t('global.action.cancel'),
336                autoFocusButton: 'ok',
337              },
338            )
339            .then((deleteConfirmed) => {
340              if (deleteConfirmed) {
341                this.startLoader();
342                this.$store
343                  .dispatch('userManagement/deleteUsers', this.selectedRows)
344                  .then((messages) => {
345                    messages.forEach(({ type, message }) => {
346                      if (type === 'success') this.successToast(message);
347                      if (type === 'error') this.errorToast(message);
348                    });
349                  })
350                  .finally(() => this.endLoader());
351              }
352            });
353          break;
354        case 'enable':
355          this.startLoader();
356          this.$store
357            .dispatch('userManagement/enableUsers', this.selectedRows)
358            .then((messages) => {
359              messages.forEach(({ type, message }) => {
360                if (type === 'success') this.successToast(message);
361                if (type === 'error') this.errorToast(message);
362              });
363            })
364            .finally(() => this.endLoader());
365          break;
366        case 'disable':
367          this.$bvModal
368            .msgBoxConfirm(
369              this.$tc(
370                'pageUserManagement.modal.batchDisableConfirmMessage',
371                this.selectedRows.length,
372              ),
373              {
374                title: this.$tc(
375                  'pageUserManagement.disableUser',
376                  this.selectedRows.length,
377                ),
378                okTitle: this.$tc(
379                  'pageUserManagement.disableUser',
380                  this.selectedRows.length,
381                ),
382                cancelTitle: this.$t('global.action.cancel'),
383                autoFocusButton: 'ok',
384              },
385            )
386            .then((disableConfirmed) => {
387              if (disableConfirmed) {
388                this.startLoader();
389                this.$store
390                  .dispatch('userManagement/disableUsers', this.selectedRows)
391                  .then((messages) => {
392                    messages.forEach(({ type, message }) => {
393                      if (type === 'success') this.successToast(message);
394                      if (type === 'error') this.errorToast(message);
395                    });
396                  })
397                  .finally(() => this.endLoader());
398              }
399            });
400          break;
401      }
402    },
403    onTableRowAction(action, row) {
404      switch (action) {
405        case 'edit':
406          this.initModalUser(row);
407          break;
408        case 'delete':
409          this.initModalDelete(row);
410          break;
411        default:
412          break;
413      }
414    },
415    saveAccountSettings(settings) {
416      this.startLoader();
417      this.$store
418        .dispatch('userManagement/saveAccountSettings', settings)
419        .then((message) => this.successToast(message))
420        .catch(({ message }) => this.errorToast(message))
421        .finally(() => this.endLoader());
422    },
423  },
424};
425</script>
426
427<style lang="scss" scoped>
428@import '@/assets/styles/bmc/helpers/_index.scss';
429@import '@/assets/styles/bootstrap/_helpers.scss';
430
431.btn.collapsed {
432  svg {
433    transform: rotate(180deg);
434  }
435}
436</style>
437