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                    {{ caCertificateExpiration | formatDate }}
56                  </dd>
57                  <dd v-else>--</dd>
58                  <dt>{{ $t('pageLdap.form.ldapCertificateValidUntil') }}</dt>
59                  <dd v-if="ldapCertificateExpiration">
60                    {{ ldapCertificateExpiration | formatDate }}
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                        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                        Active Directory
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/lib/validators';
236
237import BVToastMixin from '@/components/Mixins/BVToastMixin';
238import VuelidateMixin from '@/components/Mixins/VuelidateMixin';
239import LoadingBarMixin, { loading } from '@/components/Mixins/LoadingBarMixin';
240import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
241import PageTitle from '@/components/Global/PageTitle';
242import PageSection from '@/components/Global/PageSection';
243import InfoTooltip from '@/components/Global/InfoTooltip';
244import TableRoleGroups from './TableRoleGroups';
245
246export default {
247  name: 'Ldap',
248  components: {
249    InfoTooltip,
250    InputPasswordToggle,
251    PageTitle,
252    PageSection,
253    TableRoleGroups,
254  },
255  mixins: [BVToastMixin, VuelidateMixin, LoadingBarMixin],
256  beforeRouteLeave(to, from, next) {
257    this.hideLoader();
258    next();
259  },
260  data() {
261    return {
262      form: {
263        ldapAuthenticationEnabled: this.$store.getters['ldap/isServiceEnabled'],
264        secureLdapEnabled: false,
265        activeDirectoryEnabled: this.$store.getters[
266          'ldap/isActiveDirectoryEnabled'
267        ],
268        serverUri: '',
269        bindDn: '',
270        bindPassword: '',
271        baseDn: '',
272        userIdAttribute: '',
273        groupIdAttribute: '',
274        loading,
275      },
276    };
277  },
278  computed: {
279    ...mapGetters('ldap', [
280      'isServiceEnabled',
281      'isActiveDirectoryEnabled',
282      'ldap',
283      'activeDirectory',
284    ]),
285    sslCertificates() {
286      return this.$store.getters['certificates/allCertificates'];
287    },
288    caCertificateExpiration() {
289      const caCertificate = find(this.sslCertificates, {
290        type: 'TrustStore Certificate',
291      });
292      if (caCertificate === undefined) return null;
293      return caCertificate.validUntil;
294    },
295    ldapCertificateExpiration() {
296      const ldapCertificate = find(this.sslCertificates, {
297        type: 'LDAP Certificate',
298      });
299      if (ldapCertificate === undefined) return null;
300      return ldapCertificate.validUntil;
301    },
302    ldapProtocol() {
303      return this.form.secureLdapEnabled ? 'ldaps://' : 'ldap://';
304    },
305  },
306  watch: {
307    isServiceEnabled: function (value) {
308      this.form.ldapAuthenticationEnabled = value;
309    },
310    isActiveDirectoryEnabled: function (value) {
311      this.form.activeDirectoryEnabled = value;
312      this.setFormValues();
313    },
314  },
315  validations: {
316    form: {
317      ldapAuthenticationEnabled: {},
318      secureLdapEnabled: {},
319      activeDirectoryEnabled: {
320        required: requiredIf(function () {
321          return this.form.ldapAuthenticationEnabled;
322        }),
323      },
324      serverUri: {
325        required: requiredIf(function () {
326          return this.form.ldapAuthenticationEnabled;
327        }),
328      },
329      bindDn: {
330        required: requiredIf(function () {
331          return this.form.ldapAuthenticationEnabled;
332        }),
333      },
334      bindPassword: {
335        required: requiredIf(function () {
336          return this.form.ldapAuthenticationEnabled;
337        }),
338      },
339      baseDn: {
340        required: requiredIf(function () {
341          return this.form.ldapAuthenticationEnabled;
342        }),
343      },
344      userIdAttribute: {},
345      groupIdAttribute: {},
346    },
347  },
348  created() {
349    this.startLoader();
350    this.$store
351      .dispatch('ldap/getAccountSettings')
352      .finally(() => this.endLoader());
353    this.$store
354      .dispatch('certificates/getCertificates')
355      .finally(() => this.endLoader());
356    this.setFormValues();
357  },
358  methods: {
359    setFormValues(serviceType) {
360      if (!serviceType) {
361        serviceType = this.isActiveDirectoryEnabled
362          ? this.activeDirectory
363          : this.ldap;
364      }
365      const {
366        serviceAddress = '',
367        bindDn = '',
368        baseDn = '',
369        userAttribute = '',
370        groupsAttribute = '',
371      } = serviceType;
372      const secureLdap =
373        serviceAddress && serviceAddress.includes('ldaps://') ? true : false;
374      const serverUri = serviceAddress
375        ? serviceAddress.replace(/ldaps?:\/\//, '')
376        : '';
377      this.form.secureLdapEnabled = secureLdap;
378      this.form.serverUri = serverUri;
379      this.form.bindDn = bindDn;
380      this.form.bindPassword = '';
381      this.form.baseDn = baseDn;
382      this.form.userIdAttribute = userAttribute;
383      this.form.groupIdAttribute = groupsAttribute;
384    },
385    handleSubmit() {
386      this.$v.$touch();
387      if (this.$v.$invalid) return;
388      const data = {
389        serviceEnabled: this.form.ldapAuthenticationEnabled,
390        activeDirectoryEnabled: this.form.activeDirectoryEnabled,
391        serviceAddress: `${this.ldapProtocol}${this.form.serverUri}`,
392        bindDn: this.form.bindDn,
393        bindPassword: this.form.bindPassword,
394        baseDn: this.form.baseDn,
395        userIdAttribute: this.form.userIdAttribute,
396        groupIdAttribute: this.form.groupIdAttribute,
397      };
398      this.startLoader();
399      this.$store
400        .dispatch('ldap/saveAccountSettings', data)
401        .then((success) => {
402          this.successToast(success);
403        })
404        .catch(({ message }) => {
405          this.errorToast(message);
406        })
407        .finally(() => {
408          this.form.bindPassword = '';
409          this.$v.form.$reset();
410          this.endLoader();
411        });
412    },
413    onChangeServiceType(isActiveDirectoryEnabled) {
414      this.$v.form.activeDirectoryEnabled.$touch();
415      const serviceType = isActiveDirectoryEnabled
416        ? this.activeDirectory
417        : this.ldap;
418      // Set form values according to user selected
419      // service type
420      this.setFormValues(serviceType);
421    },
422    onChangeldapAuthenticationEnabled(isServiceEnabled) {
423      this.$v.form.ldapAuthenticationEnabled.$touch();
424      if (!isServiceEnabled) {
425        // Request will fail if sent with empty values.
426        // The frontend only checks for required fields
427        // when the service is enabled. This is to prevent
428        // an error if a user clears any properties then
429        // disables the service.
430        this.setFormValues();
431      }
432    },
433  },
434};
435</script>
436