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