xref: /openbmc/webui-vue/src/views/SecurityAndAccess/Ldap/Ldap.vue (revision d36ac8a8be8636ddd0e64ce005d507b21bcdeb00)
1<template>
2  <b-container fluid="xl">
3    <page-title :description="$t('pageLdap.pageDescription')" />
4    <page-section :section-title="$t('pageLdap.settings')">
5      <b-form novalidate @submit.prevent="handleSubmit">
6        <b-row>
7          <b-col>
8            <b-form-group
9              class="mb-3"
10              :label="$t('pageLdap.form.ldapAuthentication')"
11              :disabled="loading"
12            >
13              <b-form-checkbox
14                v-model="form.ldapAuthenticationEnabled"
15                data-test-id="ldap-checkbox-ldapAuthenticationEnabled"
16                @change="onChangeldapAuthenticationEnabled"
17              >
18                {{ $t('global.action.enable') }}
19              </b-form-checkbox>
20            </b-form-group>
21          </b-col>
22        </b-row>
23        <div class="form-background p-3">
24          <b-form-group
25            class="m-0"
26            :label="$t('pageLdap.ariaLabel.ldapSettings')"
27            label-class="visually-hidden-focusable"
28            :disabled="!form.ldapAuthenticationEnabled || loading"
29          >
30            <b-row>
31              <b-col md="3" lg="4" xl="3">
32                <b-form-group
33                  class="mb-4"
34                  :label="$t('pageLdap.form.secureLdapUsingSsl')"
35                >
36                  <b-form-text id="enable-secure-help-block">
37                    {{ $t('pageLdap.form.secureLdapHelper') }}
38                  </b-form-text>
39                  <b-form-checkbox
40                    id="enable-secure-ldap"
41                    v-model="form.secureLdapEnabled"
42                    aria-describedby="enable-secure-help-block"
43                    data-test-id="ldap-checkbox-secureLdapEnabled"
44                    :disabled="
45                      !caCertificateExpiration || !ldapCertificateExpiration
46                    "
47                    @change="v$.form.secureLdapEnabled.$touch()"
48                  >
49                    {{ $t('global.action.enable') }}
50                  </b-form-checkbox>
51                </b-form-group>
52                <dl>
53                  <dt>
54                    {{ $t('pageLdap.form.caCertificateValidUntil') }}
55                  </dt>
56                  <dd v-if="caCertificateExpiration">
57                    {{ $filters.formatDate(caCertificateExpiration) }}
58                  </dd>
59                  <dd v-else>--</dd>
60                  <dt>
61                    {{ $t('pageLdap.form.ldapCertificateValidUntil') }}
62                  </dt>
63                  <dd v-if="ldapCertificateExpiration">
64                    {{ $filters.formatDate(ldapCertificateExpiration) }}
65                  </dd>
66                  <dd v-else>--</dd>
67                </dl>
68                <b-link
69                  class="d-inline-block mb-4 m-md-0"
70                  to="/security-and-access/certificates"
71                >
72                  {{ $t('pageLdap.form.manageSslCertificates') }}
73                </b-link>
74              </b-col>
75              <b-col md="9" lg="8" xl="9">
76                <b-row>
77                  <b-col>
78                    <b-form-group :label="$t('pageLdap.form.serviceType')">
79                      <b-form-radio
80                        v-model="form.activeDirectoryEnabled"
81                        data-test-id="ldap-radio-activeDirectoryEnabled"
82                        :value="false"
83                        @change="onChangeServiceType"
84                      >
85                        {{ $t('pageLdap.form.openLDAP') }}
86                      </b-form-radio>
87                      <b-form-radio
88                        v-model="form.activeDirectoryEnabled"
89                        data-test-id="ldap-radio-activeDirectoryEnabled"
90                        :value="true"
91                        @change="onChangeServiceType"
92                      >
93                        {{ $t('pageLdap.form.activeDirectory') }}
94                      </b-form-radio>
95                    </b-form-group>
96                  </b-col>
97                </b-row>
98                <b-row>
99                  <b-col sm="6" xl="4">
100                    <b-form-group label-for="server-uri">
101                      <template #label>
102                        {{ $t('pageLdap.form.serverUri') }}
103                        <info-tooltip
104                          :title="$t('pageLdap.form.serverUriTooltip')"
105                        />
106                      </template>
107                      <b-input-group :prepend="ldapProtocol">
108                        <b-form-input
109                          id="server-uri"
110                          v-model="form.serverUri"
111                          data-test-id="ldap-input-serverUri"
112                          :state="getValidationState(v$.form.serverUri)"
113                          @change="v$.form.serverUri.$touch()"
114                        />
115                        <b-form-invalid-feedback role="alert">
116                          {{ $t('global.form.fieldRequired') }}
117                        </b-form-invalid-feedback>
118                      </b-input-group>
119                    </b-form-group>
120                  </b-col>
121                  <b-col sm="6" xl="4">
122                    <b-form-group
123                      :label="$t('pageLdap.form.bindDn')"
124                      label-for="bind-dn"
125                    >
126                      <b-form-input
127                        id="bind-dn"
128                        v-model="form.bindDn"
129                        autocomplete="username"
130                        data-test-id="ldap-input-bindDn"
131                        :state="getValidationState(v$.form.bindDn)"
132                        @change="v$.form.bindDn.$touch()"
133                      />
134                      <b-form-invalid-feedback role="alert">
135                        {{ $t('global.form.fieldRequired') }}
136                      </b-form-invalid-feedback>
137                    </b-form-group>
138                  </b-col>
139                  <b-col sm="6" xl="4">
140                    <b-form-group
141                      :label="$t('pageLdap.form.bindPassword')"
142                      label-for="bind-password"
143                    >
144                      <input-password-toggle
145                        data-test-id="ldap-input-togglePassword"
146                      >
147                        <b-form-input
148                          id="bind-password"
149                          v-model="form.bindPassword"
150                          type="password"
151                          autocomplete="current-password"
152                          :state="getValidationState(v$.form.bindPassword)"
153                          class="form-control-with-button"
154                          @change="v$.form.bindPassword.$touch()"
155                        />
156                        <b-form-invalid-feedback role="alert">
157                          {{ $t('global.form.fieldRequired') }}
158                        </b-form-invalid-feedback>
159                      </input-password-toggle>
160                    </b-form-group>
161                  </b-col>
162                  <b-col sm="6" xl="4">
163                    <b-form-group
164                      :label="$t('pageLdap.form.baseDn')"
165                      label-for="base-dn"
166                    >
167                      <b-form-input
168                        id="base-dn"
169                        v-model="form.baseDn"
170                        data-test-id="ldap-input-baseDn"
171                        :state="getValidationState(v$.form.baseDn)"
172                        @change="v$.form.baseDn.$touch()"
173                      />
174                      <b-form-invalid-feedback role="alert">
175                        {{ $t('global.form.fieldRequired') }}
176                      </b-form-invalid-feedback>
177                    </b-form-group>
178                  </b-col>
179                  <b-col sm="6" xl="4">
180                    <b-form-group label-for="user-id-attribute">
181                      <template #label>
182                        {{ $t('pageLdap.form.userIdAttribute') }}
183                        -
184                        <span class="form-text d-inline">
185                          {{ $t('global.form.optional') }}
186                        </span>
187                      </template>
188                      <b-form-input
189                        id="user-id-attribute"
190                        v-model="form.userIdAttribute"
191                        data-test-id="ldap-input-userIdAttribute"
192                        @change="v$.form.userIdAttribute.$touch()"
193                      />
194                    </b-form-group>
195                  </b-col>
196                  <b-col sm="6" xl="4">
197                    <b-form-group label-for="group-id-attribute">
198                      <template #label>
199                        {{ $t('pageLdap.form.groupIdAttribute') }}
200                        -
201                        <span class="form-text d-inline">
202                          {{ $t('global.form.optional') }}
203                        </span>
204                      </template>
205                      <b-form-input
206                        id="group-id-attribute"
207                        v-model="form.groupIdAttribute"
208                        data-test-id="ldap-input-groupIdAttribute"
209                        @change="v$.form.groupIdAttribute.$touch()"
210                      />
211                    </b-form-group>
212                  </b-col>
213                </b-row>
214              </b-col>
215            </b-row>
216          </b-form-group>
217        </div>
218        <b-row class="mt-4 mb-5">
219          <b-col>
220            <b-btn
221              variant="primary"
222              type="submit"
223              data-test-id="ldap-button-saveSettings"
224              :disabled="loading"
225            >
226              {{ $t('global.action.saveSettings') }}
227            </b-btn>
228          </b-col>
229        </b-row>
230      </b-form>
231    </page-section>
232
233    <!-- Role groups -->
234    <page-section :section-title="$t('pageLdap.roleGroups')">
235      <table-role-groups />
236    </page-section>
237  </b-container>
238</template>
239
240<script>
241import { mapGetters } from 'vuex';
242import { find } from 'lodash';
243import { requiredIf } from '@vuelidate/validators';
244import { useVuelidate } from '@vuelidate/core';
245
246import BVToastMixin from '@/components/Mixins/BVToastMixin';
247import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
248import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
249import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
250import PageTitle from '@/components/Global/PageTitle';
251import PageSection from '@/components/Global/PageSection';
252import InfoTooltip from '@/components/Global/InfoTooltip';
253import TableRoleGroups from './TableRoleGroups';
254import { useI18n } from 'vue-i18n';
255
256export default {
257  name: 'Ldap',
258  components: {
259    InfoTooltip,
260    InputPasswordToggle,
261    PageTitle,
262    PageSection,
263    TableRoleGroups,
264  },
265  mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
266  beforeRouteLeave(to, from, next) {
267    this.hideLoader();
268    next();
269  },
270  setup() {
271    return {
272      v$: useVuelidate(),
273    };
274  },
275  data() {
276    return {
277      $t: useI18n().t,
278      form: {
279        ldapAuthenticationEnabled: this.$store.getters['ldap/isServiceEnabled'],
280        secureLdapEnabled: false,
281        activeDirectoryEnabled:
282          this.$store.getters['ldap/isActiveDirectoryEnabled'],
283        serverUri: '',
284        bindDn: '',
285        bindPassword: '',
286        baseDn: '',
287        userIdAttribute: '',
288        groupIdAttribute: '',
289      },
290      loading,
291    };
292  },
293  computed: {
294    ...mapGetters('ldap', [
295      'isServiceEnabled',
296      'isActiveDirectoryEnabled',
297      'ldap',
298      'activeDirectory',
299    ]),
300    sslCertificates() {
301      return this.$store.getters['certificates/allCertificates'];
302    },
303    caCertificateExpiration() {
304      const caCertificate = find(this.sslCertificates, {
305        type: 'TrustStore Certificate',
306      });
307      if (caCertificate === undefined) return null;
308      return caCertificate.validUntil;
309    },
310    ldapCertificateExpiration() {
311      const ldapCertificate = find(this.sslCertificates, {
312        type: 'LDAP Certificate',
313      });
314      if (ldapCertificate === undefined) return null;
315      return ldapCertificate.validUntil;
316    },
317    ldapProtocol() {
318      return this.form.secureLdapEnabled ? 'ldaps://' : 'ldap://';
319    },
320  },
321  watch: {
322    isServiceEnabled: function (value) {
323      this.form.ldapAuthenticationEnabled = value;
324    },
325    isActiveDirectoryEnabled: function (value) {
326      this.form.activeDirectoryEnabled = value;
327      this.setFormValues();
328    },
329  },
330  validations: {
331    form: {
332      ldapAuthenticationEnabled: {},
333      secureLdapEnabled: {},
334      activeDirectoryEnabled: {
335        required: requiredIf(function () {
336          return this.form.ldapAuthenticationEnabled;
337        }),
338      },
339      serverUri: {
340        required: requiredIf(function () {
341          return this.form.ldapAuthenticationEnabled;
342        }),
343      },
344      bindDn: {
345        required: requiredIf(function () {
346          return this.form.ldapAuthenticationEnabled;
347        }),
348      },
349      bindPassword: {
350        required: requiredIf(function () {
351          return this.form.ldapAuthenticationEnabled;
352        }),
353      },
354      baseDn: {
355        required: requiredIf(function () {
356          return this.form.ldapAuthenticationEnabled;
357        }),
358      },
359      userIdAttribute: {},
360      groupIdAttribute: {},
361    },
362  },
363  created() {
364    this.startLoader();
365    this.$store
366      .dispatch('ldap/getAccountSettings')
367      .finally(() => this.endLoader());
368    this.$store
369      .dispatch('certificates/getCertificates')
370      .finally(() => this.endLoader());
371    this.setFormValues();
372  },
373  methods: {
374    setFormValues(serviceType) {
375      if (!serviceType) {
376        serviceType = this.isActiveDirectoryEnabled
377          ? this.activeDirectory
378          : this.ldap;
379      }
380      const {
381        serviceAddress = '',
382        bindDn = '',
383        baseDn = '',
384        userAttribute = '',
385        groupsAttribute = '',
386      } = serviceType;
387      const secureLdap =
388        serviceAddress && serviceAddress.includes('ldaps://') ? true : false;
389      const serverUri = serviceAddress
390        ? serviceAddress.replace(/ldaps?:\/\//, '')
391        : '';
392      this.form.secureLdapEnabled = secureLdap;
393      this.form.serverUri = serverUri;
394      this.form.bindDn = bindDn;
395      this.form.bindPassword = '';
396      this.form.baseDn = baseDn;
397      this.form.userIdAttribute = userAttribute;
398      this.form.groupIdAttribute = groupsAttribute;
399    },
400    handleSubmit() {
401      this.v$.form.$touch();
402      if (this.v$.form.$invalid) return;
403      const data = {
404        serviceEnabled: this.form.ldapAuthenticationEnabled,
405        activeDirectoryEnabled: this.form.activeDirectoryEnabled,
406        serviceAddress: `${this.ldapProtocol}${this.form.serverUri}`,
407        bindDn: this.form.bindDn,
408        bindPassword: this.form.bindPassword,
409        baseDn: this.form.baseDn,
410        userIdAttribute: this.form.userIdAttribute,
411        groupIdAttribute: this.form.groupIdAttribute,
412      };
413      this.startLoader();
414      this.$store
415        .dispatch('ldap/saveAccountSettings', data)
416        .then((success) => {
417          this.successToast(success);
418        })
419        .catch(({ message }) => {
420          this.errorToast(message);
421        })
422        .finally(() => {
423          this.form.bindPassword = '';
424          this.v$.form.$reset();
425          this.endLoader();
426        });
427    },
428    onChangeServiceType(isActiveDirectoryEnabled) {
429      this.v$.form.activeDirectoryEnabled.$touch();
430      const serviceType = isActiveDirectoryEnabled
431        ? this.activeDirectory
432        : this.ldap;
433      // Set form values according to user selected
434      // service type
435      this.setFormValues(serviceType);
436    },
437    onChangeldapAuthenticationEnabled(isServiceEnabled) {
438      this.v$.form.ldapAuthenticationEnabled.$touch();
439      if (!isServiceEnabled) {
440        // Request will fail if sent with empty values.
441        // The frontend only checks for required fields
442        // when the service is enabled. This is to prevent
443        // an error if a user clears any properties then
444        // disables the service.
445        this.setFormValues();
446      }
447    },
448  },
449};
450</script>
451