xref: /openbmc/webui-vue/src/views/SecurityAndAccess/UserManagement/UserManagement.vue (revision 20983592cffa5213a37f30724b101a61a7caa550)
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';
136
137export default {
138  name: 'UserManagement',
139  components: {
140    IconAdd,
141    IconChevron,
142    IconEdit,
143    IconSettings,
144    IconTrashcan,
145    ModalSettings,
146    ModalUser,
147    PageTitle,
148    TableRoles,
149    TableRowAction,
150    TableToolbar,
151  },
152  mixins: [BVTableSelectableMixin, BVToastMixin, LoadingBarMixin],
153  beforeRouteLeave(to, from, next) {
154    this.hideLoader();
155    next();
156  },
157  data() {
158    return {
159      isBusy: true,
160      activeUser: null,
161      setting: {},
162      fields: [
163        {
164          key: 'checkbox',
165        },
166        {
167          key: 'username',
168          label: this.$t('pageUserManagement.table.username'),
169        },
170        {
171          key: 'privilege',
172          label: this.$t('pageUserManagement.table.privilege'),
173        },
174        {
175          key: 'status',
176          label: this.$t('pageUserManagement.table.status'),
177        },
178        {
179          key: 'actions',
180          label: '',
181          tdClass: 'text-right text-nowrap',
182        },
183      ],
184      tableToolbarActions: [
185        {
186          value: 'delete',
187          label: this.$t('global.action.delete'),
188        },
189        {
190          value: 'enable',
191          label: this.$t('global.action.enable'),
192        },
193        {
194          value: 'disable',
195          label: this.$t('global.action.disable'),
196        },
197      ],
198      selectedRows: selectedRows,
199      tableHeaderCheckboxModel: tableHeaderCheckboxModel,
200      tableHeaderCheckboxIndeterminate: tableHeaderCheckboxIndeterminate,
201    };
202  },
203  computed: {
204    allUsers() {
205      return this.$store.getters['userManagement/allUsers'];
206    },
207    tableItems() {
208      // transform user data to table data
209      return this.allUsers.map((user) => {
210        return {
211          username: user.UserName,
212          privilege: user.RoleId,
213          status: user.Locked
214            ? 'Locked'
215            : user.Enabled
216            ? 'Enabled'
217            : 'Disabled',
218          actions: [
219            {
220              value: 'edit',
221              enabled: this.editEnable(user),
222              title: this.$t('pageUserManagement.editUser'),
223            },
224            {
225              value: 'delete',
226              enabled:
227                user.UserName === this.$store.getters['global/username']
228                  ? false
229                  : true && user.UserName === 'root'
230                  ? false
231                  : true,
232              title: this.$tc('pageUserManagement.deleteUser'),
233            },
234          ],
235          ...user,
236        };
237      });
238    },
239    settings() {
240      return this.$store.getters['userManagement/accountSettings'];
241    },
242    passwordRequirements() {
243      return this.$store.getters['userManagement/accountPasswordRequirements'];
244    },
245  },
246  created() {
247    this.startLoader();
248    this.$store.dispatch('userManagement/getUsers').finally(() => {
249      this.endLoader();
250      this.isBusy = false;
251    });
252    this.$store.dispatch('userManagement/getAccountSettings');
253    this.$store.dispatch('userManagement/getAccountRoles');
254  },
255  methods: {
256    editEnable(user) {
257      if ('root' === this.$store.getters['global/username']) {
258        return true;
259      } else {
260        return user.UserName === 'root' ? false : true;
261      }
262    },
263    initModalUser(user) {
264      this.activeUser = user;
265      this.$bvModal.show('modal-user');
266    },
267    initModalDelete(user) {
268      this.$bvModal
269        .msgBoxConfirm(
270          this.$t('pageUserManagement.modal.deleteConfirmMessage', {
271            user: user.username,
272          }),
273          {
274            title: this.$tc('pageUserManagement.deleteUser'),
275            okTitle: this.$tc('pageUserManagement.deleteUser'),
276            cancelTitle: this.$t('global.action.cancel'),
277          }
278        )
279        .then((deleteConfirmed) => {
280          if (deleteConfirmed) {
281            this.deleteUser(user);
282          }
283        });
284    },
285    initModalSettings() {
286      this.setting = this.settings;
287      this.$bvModal.show('modal-settings');
288    },
289    saveUser({ isNewUser, userData }) {
290      this.startLoader();
291      if (isNewUser) {
292        this.$store
293          .dispatch('userManagement/createUser', userData)
294          .then((success) => this.successToast(success))
295          .catch(({ message }) => this.errorToast(message))
296          .finally(() => this.endLoader());
297      } else {
298        this.$store
299          .dispatch('userManagement/updateUser', userData)
300          .then((success) => this.successToast(success))
301          .catch(({ message }) => this.errorToast(message))
302          .finally(() => this.endLoader());
303      }
304    },
305    deleteUser({ username }) {
306      this.startLoader();
307      this.$store
308        .dispatch('userManagement/deleteUser', username)
309        .then((success) => this.successToast(success))
310        .catch(({ message }) => this.errorToast(message))
311        .finally(() => this.endLoader());
312    },
313    onBatchAction(action) {
314      switch (action) {
315        case 'delete':
316          this.$bvModal
317            .msgBoxConfirm(
318              this.$tc(
319                'pageUserManagement.modal.batchDeleteConfirmMessage',
320                this.selectedRows.length
321              ),
322              {
323                title: this.$tc(
324                  'pageUserManagement.deleteUser',
325                  this.selectedRows.length
326                ),
327                okTitle: this.$tc(
328                  'pageUserManagement.deleteUser',
329                  this.selectedRows.length
330                ),
331                cancelTitle: this.$t('global.action.cancel'),
332              }
333            )
334            .then((deleteConfirmed) => {
335              if (deleteConfirmed) {
336                this.startLoader();
337                this.$store
338                  .dispatch('userManagement/deleteUsers', this.selectedRows)
339                  .then((messages) => {
340                    messages.forEach(({ type, message }) => {
341                      if (type === 'success') this.successToast(message);
342                      if (type === 'error') this.errorToast(message);
343                    });
344                  })
345                  .finally(() => this.endLoader());
346              }
347            });
348          break;
349        case 'enable':
350          this.startLoader();
351          this.$store
352            .dispatch('userManagement/enableUsers', this.selectedRows)
353            .then((messages) => {
354              messages.forEach(({ type, message }) => {
355                if (type === 'success') this.successToast(message);
356                if (type === 'error') this.errorToast(message);
357              });
358            })
359            .finally(() => this.endLoader());
360          break;
361        case 'disable':
362          this.startLoader();
363          this.$store
364            .dispatch('userManagement/disableUsers', this.selectedRows)
365            .then((messages) => {
366              messages.forEach(({ type, message }) => {
367                if (type === 'success') this.successToast(message);
368                if (type === 'error') this.errorToast(message);
369              });
370            })
371            .finally(() => this.endLoader());
372          break;
373      }
374    },
375    onTableRowAction(action, row) {
376      switch (action) {
377        case 'edit':
378          this.initModalUser(row);
379          break;
380        case 'delete':
381          this.initModalDelete(row);
382          break;
383        default:
384          break;
385      }
386    },
387    saveAccountSettings(settings) {
388      this.startLoader();
389      this.$store
390        .dispatch('userManagement/saveAccountSettings', settings)
391        .then((message) => this.successToast(message))
392        .catch(({ message }) => this.errorToast(message))
393        .finally(() => this.endLoader());
394    },
395  },
396};
397</script>
398
399<style lang="scss" scoped>
400.btn.collapsed {
401  svg {
402    transform: rotate(180deg);
403  }
404}
405</style>
406