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