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