CINXE.COM
CAS authorization page
<html> <head> <title>CAS authorization page</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script type="application/x-javascript" src="./js/vue-2.5.8.min.js"></script> <script type="application/x-javascript" src="./js/vue-router-3.0.1.min.js"></script> <script type="application/x-javascript" src="./js/vue-i18n-7.3.2.min.js"></script> <script type="application/x-javascript" src="./js/vue-resource@1.3.4.min.js"></script> <script type="application/x-javascript" src="./js/v-mask.min.js"></script> <script type="application/x-javascript" src="./js/v-tooltip.min.js"></script> <script type="application/x-javascript" src="./js/vue-recaptcha-1.3.0.min.js"></script> <script type="application/x-javascript" src="./js/components.js"></script> <link type="text/css" rel="stylesheet" href="./css/bootstrap.min.css"> <link type="text/css" rel="stylesheet" href="./css/fonts-googleapis-open-sans.css"> <link type="text/css" rel="stylesheet" href="./css/fontawesome-all.min.css"> <link type="text/css" rel="stylesheet" href="./css/starter-template.css"> <link type="text/css" rel="stylesheet" href="./css/sign.css"> <link type="text/css" rel="stylesheet" href="./css/custom/custom.css"> <link rel="shortcut icon" type="image/png" href="./images/favicon.ico"/> </head> <body> <div id="app"> <router-view></router-view> <div v-if="this.captchaLoaded"> <vue-recaptcha ref="recaptcha" @verify="sendRequest" size="invisible" :sitekey="this.systemDetailConfig && this.systemDetailConfig.captchaSiteKey || ''"> </vue-recaptcha> </div> </div> </body> <script> // Vue.config.debug = true // all request's addresses should be prefixed with value of baseUrl var baseUrl = ""; var emailPattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var validatePwd = function () { self = this fetch( baseUrl + 'configuration/checkValidity', Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { code: this.$root.code, password: this.value })) }) ).then(function (response) { if (response.status === 200) { self.isCustomAsyncValidateOK = true return } return response.json() }).then(function (data) { if (!data) return throwErrorFromData(data) }).catch(function (e) { self.isCustomAsyncValidateOK = false self.errorMessage = e.message }) } function create_UUID(){ var dt = new Date().getTime(); var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = (dt + Math.random()*16)%16 | 0; dt = Math.floor(dt/16); return (c=='x' ? r :(r&0x3|0x8)).toString(16); }); return uuid; } Vue.use(VTooltip) Vue.directive('mask', VueMask.VueMaskDirective) Vue.component('main-wrapper', { template: '<transition appear name="fade"><div class="sign-page" v-bind:class="{ \'login-page\': this.$root.pages.login, \'register-page\': this.$root.pages.register }"><main role="main" class="sign-card p-4">' + ' <div class="container">' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-logo">' + ' <a v-bind:href="this.$root.landingPageUri">' + ' <img v-bind:src="this.$root.logoSrc" alt="eID">' + ' </a>' + ' </div>' + ' </div>' + ' </div>' + ' <slot></slot>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-logo">' + ' <p style="font-size: 10px;">{{ $t(\'Feedback\', { supportEmail: (this.$root.systemDetailConfig && this.$root.systemDetailConfig.supportEmail) || \'\' })}}</p>' + ' </div>' + ' </div>' + ' </div>' + ' </div>' + ' </main></div></transition>' }) FormBase = Vue.component('form-base', { data: function () { return { isIndividual: true, showGeneralError: false, errorMessage: '', showGeneralSuccess: false, successMessage: '', isPending: false, page: null, responseStatus: null } }, methods: { getFetchData: function () {}, // do provide smth useful here sendRequest: function (options) { var self = this this.isPending = true return fetch( this.submitUrl, this.getFetchData(options) ).then(function (response) { self.responseStatus = response.status return response.json() }).then(this.onDataReceived).catch(this.onSendError) }, onSubmit: function () { var self = this self.showGeneralError = false self.errorMessage = '' self.showGeneralSuccess = false self.successMessage = '' if (!isFormValid(this.$refs)) return if (this.$root.$refs.recaptcha) return this.$root.$refs.recaptcha.execute(); if (this.isPending) return this.sendRequest() }, throwErrorFromData: function (data) { throwErrorFromData.call(this, data) }, onSendError: function (err) { this.errorMessage = err.message this.showGeneralError = true this.isPending = false }, onDataReceived: function (data) { if (this.responseStatus !== 200) { this.throwErrorFromData(data) } window.location = this.$root.oauthVerifyRequest.redirect_uri + "?code=" + data.authorization_code + "&state=" + this.$root.oauthVerifyRequest.state } }, created: function () { var self = this; Object.keys(this.$root.pages).forEach(function (pageKey) { self.$root.pages[pageKey] = (pageKey === self.page || false); }) }, computed: { getPwdSetup: function () { var pwdConfig = null; fields = this.$root.getRegistrationFields; fields.some(function (f) { if (f.name !== 'password') return; pwdConfig = f; return true; }); return pwdConfig; }, getPropsUsedForLogin: function () { return this.$root.getRegistrationFields.filter(function (f) { return f.usedForLogin }) } } }) /** * opts is hash of options to override defaults * i.e. { required: false } would make the input not required * when calling getFormInput make sure to call with component as context * */ var FormInput = Vue.component('form-input', { template: '<div class="form-group input-icon input-icon-right" v-bind:class="{ \'has-error\': !isValid, \'input-icon-right-x2\': isPassword }" >' + ' <input v-if="inputMask != null" ' + ' v-bind:name="name" ' + ' v-on:blur="isTouched = true" ' + ' v-on:focus="isTouched = false" ' + ' v-on:change="$emit(\'change\')" ' + ' v-on:paste="$emit(\'paste\', $event)" ' + ' v-model="value" class="form-control form-control-lg" ' + ' v-bind:type="type" ' + ' v-bind:placeholder="$t(placeholder)" ' + ' v-mask="inputMask" />' + ' <input v-if="inputMask == null" ' + ' v-bind:name="name" ' + ' v-on:blur="isTouched = true" ' + ' v-on:focus="isTouched = false" ' + ' v-on:change="$emit(\'change\')" ' + ' v-on:paste="$emit(\'paste\', $event)" ' + ' v-model.trim="value" class="form-control form-control-lg" ' + ' v-bind:type="type" ' + ' v-bind:placeholder="$t(placeholder)" />' + ' <i v-if="tooltipMessage" v-tooltip="$t(tooltipMessage)" class="fal fa-question-circle sign-input-help"></i>' + ' <i v-if="isPassword === true" class="far password-visibility-toggle" v-bind:class="{ \'fa-eye\': !isPasswordVisible, \'fa-eye-slash\': isPasswordVisible }" v-on:click="isPasswordVisible = !isPasswordVisible"></i>' + ' <transition name="fade-slide"><div v-show="!isValid" class="error-message">{{errorMessage}}</div></transition>' + '</div>', props: [ 'required', 'isUsed', 'placeholder', 'type', 'pattern', 'patternErrorMessage', 'externalValidationErrorMessage', 'customValidationComponent', 'customValidateAsync', 'tooltipMessage', 'isPassword', 'inputMask', 'name', 'propertyId', 'initValue' ], data: function () { return { value: this.initValue || null, errorMessage: '', isUsed: true, isTouched: false, inputMask: null, name: null, isPasswordVisible: false, isCustomAsyncValidateOK: true, isInvalidatedExternally: false } }, watch: { value: function () { if (this.type === 'email') { this.value = this.value.toLocaleLowerCase(); } }, isTouched: function (oldIsTouched, newIsTouched) { if (!this.customValidateAsync) return if (newIsTouched === true) { this.isCustomAsyncValidateOK = true return } if (this.isValid || (this.isValid == null)) { this.customValidateAsync() } }, isPasswordVisible: function (oldIsPasswordVisible, newIsPasswordVisible) { this.type = newIsPasswordVisible ? "password" : "text"; } }, computed: { isValid: function () { var result; if (!this.isTouched) return true if (!this.isUsed) return true if (this.required && (this.value == null || this.value === '')) { this.errorMessage = this.$t("PropertyRequired") return false } if (!this.required && (this.value == null || this.value === '')) { return true } if (this.pattern && !this.pattern.test(this.value)) { this.errorMessage = this.$t(this.patternErrorMessage) return false; } if (this.type === 'email' && !emailPattern.test(this.value)) { this.errorMessage = this.$t("InvalidEmail") return false } if (this.isInvalidatedExternally) { if (this.externalValidationErrorMessage) { this.errorMessage = this.$t(this.externalValidationErrorMessage) } return false } if (this.customValidationComponent) { if (!this.customValidationComponent.validate(this.value, this)) { this.errorMessage = this.$t(this.customValidationComponent.getError()) return false } } if (this.customValidateAsync) { return this.isCustomAsyncValidateOK } return true } } }) Vue.component('form-input-checkbox', { extends: FormInput, template: '<div class="form-group input-icon input-icon-right" style="line-height: 3rem;margin-bottom: 0;" v-bind:class="{ \'has-error\': !isValid }" >' + ' <input ' + ' v-bind:id="\'checkbox-\' + name" ' + ' v-bind:name="name" ' + ' v-on:blur="isTouched = true" ' + ' v-on:focus="isTouched = false" ' + ' v-on:change="$emit(\'change\')" ' + ' v-model="value" ' + ' v-bind:type="type" ' + ' />' + ' <label v-bind:for="\'checkbox-\' + name">{{ $t(placeholder) }}</label>' + ' <i v-if="tooltipMessage" v-tooltip="$t(tooltipMessage)" class="fal fa-question-circle sign-input-help"></i>' + ' <transition name="fade-slide"><div v-show="!isValid" class="error-message">{{errorMessage}}</div></transition>' + '</div>' }) var STATUS_INITIAL = 0, STATUS_SAVING = 1, STATUS_SUCCESS = 2, STATUS_FAILED = 3; Vue.component('form-input-file', { extends: FormInput, template: '<div class="form-group input-icon input-icon-right" v-bind:class="{ \'has-error\': !isValid }">' + ' <div class="dropbox">' + ' <input v-bind:type="type" multiple accept="image/*|application/pdf" class="input-file" ' + ' v-on:change="filesChange($event.target.files); fileCount = $event.target.files.length; $emit(\'change\');"' + ' v-bind:name="name">' + ' <p v-if="isInitial">' + ' {{ $t(placeholder) }} ' + ' </p>' + ' <p v-if="isSaving">' + ' <img src="./images/loader.gif">' + ' </p>' + ' <i18n v-if="isSuccess" path="uploadSuccess" tag="p">' + ' <span place="fileCount">{{ uploadedFiles.length }}</span>' + ' </i18n>' + ' </div>' + ' <i v-if="tooltipMessage" v-tooltip="$t(tooltipMessage)" class="fal fa-question-circle sign-input-help"></i>' + ' <transition name="fade-slide"><div v-show="!isValid" class="error-message">{{errorMessage}}</div></transition>' + '</div>', data: function () { return { uploadedFiles: [], currentStatus: null, uploadFieldName: null } }, computed: { isInitial: function () { return this.currentStatus === STATUS_INITIAL; }, isSaving: function () { return this.currentStatus === STATUS_SAVING; }, isSuccess: function () { return this.currentStatus === STATUS_SUCCESS; }, isFailed: function () { return this.currentStatus === STATUS_FAILED; }, isValid: function () { var result; if (!this.isTouched) return true if (!this.isUsed) return true if (this.required && !this.uploadedFiles.length) { this.errorMessage = this.$t("PropertyRequired") return false } if (this.isInvalidatedExternally) { if (this.externalValidationErrorMessage) { this.errorMessage = this.$t(this.externalValidationErrorMessage) } return false } if (this.customValidateAsync) { return this.isCustomAsyncValidateOK } return true } }, methods: { reset: function () { // reset form to initial state this.currentStatus = STATUS_INITIAL; this.uploadedFiles = []; }, save: function (formData) { self = this // upload data to the server this.currentStatus = STATUS_SAVING; var promise = Promise.resolve(null); if (this.uploadedFiles.length) { promise = fetch(baseUrl + 'bdata', { method: "DELETE", headers: new Headers({ 'Content-Type': 'application/json;charset=UTF-8' }), body: JSON.stringify({ code: this.$root.code, data: self.uploadedFiles }) }).then(function (res) { if (res.status === 200) { return; } throwErrorFromData(res) }); } promise.then(function () { return fetch(baseUrl + 'bdata', { method: "POST", body: formData }).then(function (res) { if (res.status === 200) { self.isCustomAsyncValidateOK = true self.currentStatus = STATUS_SUCCESS return res.json(); } throwErrorFromData(res) }).then(function (data) { if (Array.isArray(data)) { self.uploadedFiles = data; } else { self.uploadedFiles.push(data); } }); }).catch(function (e) { self.currentStatus = STATUS_INITIAL self.isCustomAsyncValidateOK = false self.errorMessage = e.message }); }, filesChange: function (fileList) { this.isCustomAsyncValidateOK = true this.errorMessage = '' // handle file changes var formData = new FormData() if (!fileList || !fileList.length) return this.currentStatus = STATUS_INITIAL this.isTouched = true // append the files to FormData formData.append('code', this.$root.code) formData.append('property_id', this.propertyId) Array.from(Array(fileList.length).keys()).map(function (x) { formData.append('name', fileList[x].name) formData.append('file', fileList[x]) }); // save it this.save(formData) } }, mounted: function () { this.customValidateAsync = this.filesChange; this.reset(); } }); Vue.component('submit-button', { template: '<div>' + '<button v-if="!isPending" class="btn btn-submit btn-lg">{{ $t(buttonText) }}</button>' + '<img v-if="isPending" src="./images/loader.gif">' + '</div>', props: [ 'buttonText', 'isPending' ] }) var isFormValid = function (refs) { var isFormOK = true, ref Object.keys(refs).forEach(function (refKey) { ref = refs[refKey] if (Array.isArray(ref)) { ref.forEach(function (formField) { formField.isTouched = true if (!formField.isValid) isFormOK = false }) } else { refs[refKey].isTouched = true if (!refs[refKey].isValid) isFormOK = false } }) return isFormOK } var generalErrorMessage = '<transition name="fade-slide"><div v-show="showGeneralError" class="row mb-4">' + ' <div class="col">' + ' <div class="error-message" v-html="errorMessage"></div>' + ' </div>' + ' </div></transition>' var generalSuccessMessage = '<transition name="fade-slide"><div v-show="showGeneralSuccess" class="row mb-4">' + ' <div class="col">' + ' <div class="success-message">{{successMessage}}</div>' + ' </div>' + ' </div></transition>' // var individualLegalSelection = '<div class="row sign-radio mb-4" data-switch="legal">' + // ' <div v-on:click="isIndividual = true" class="col text-right sign-radio-option">' + // ' <i v-if="isIndividual" class="far fa-dot-circle"></i><i v-if="!isIndividual" class="fal fa-circle unchecked"></i> {{ $t("Individual") }}' + // ' </div>' + // ' <div v-on:click="isIndividual = false" class="col text-left sign-radio-option">' + // ' <i v-if="!isIndividual" class="far fa-dot-circle"></i><i v-if="isIndividual" class="fal fa-circle unchecked"></i> {{ $t("LegalEntity") }}' + // ' </div>' + // ' </div>' throwErrorFromData = function (data) { var noMessage = "Undefined server error", self = this // this function may not be called as method, so this may be undefined throw new Error( (data && (data.message || data.error || (data.messages && Array.isArray(data.messages) && data.messages.map(function (msg) { if (typeof msg === "string") return msg var result = msg.message, placeholder if (!self || !self.$root) return result if (!self.$root.getRegistrationFields) return result self.$root.getRegistrationFields.some(function (field) { if (field.id && field.id === msg.propId) { placeholder = field.placeholder[self.$i18n.locale] if (!placeholder) { result = msg.message return } result = field.placeholder[self.$i18n.locale] + " - " + msg.message return true } }) return result }).join(', '))) || noMessage)) } function getParameterByName(name, url) { if (!url) url = window.location.href name = name.replace(/[\[\]]/g, '\\$&') var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), results = regex.exec(url) if (!results) return null if (!results[2]) return '' return decodeURIComponent(results[2].replace(/\+/g, ' ')) } var app = new Vue({ i18n: new VueI18n({ locale: getParameterByName('lang') && getParameterByName('lang') !== "undefined" ? getParameterByName('lang') : 'en', messages: {} }), router: new VueRouter({ routes: [ { path: '/', component: { extends: FormBase, template: '<main-wrapper v-if="$root.dataLoaded || $root.isObsoleteBrowser">' + '<div v-if="$root.isObsoleteBrowser">' + '<h1>{{ $t("UnsupportedBrowserHeader") }}</h1>' + '<p>{{ $t("UnsupportedBrowser") }}</p>' + '<h2>{{ $t("Thanks") }}</h2>' + '<ul class="supported-browsers-list">' + ' <li class="supported-browsers-list-item">' + ' <img width="125" height="125" src="./images/chrome.ico" align="bottom" />' + ' <br />' + ' <span class="supported-browser-download">' + ' <a href="https://www.google.com/intl/en/chrome/browser/" target="_blank" rel="noopener">' + ' Google Chrome' + ' </a>' + ' </span>' + ' </li>' + ' <li class="supported-browsers-list-item">' + ' <img width="125" height="125" src="./images/firefox.ico" />' + ' <br />' + ' <span class="supported-browser-download">' + ' <a href="https://www.mozilla.org/firefox/all/" target="_blank" rel="noopener">' + ' Mozilla Firefox' + ' </a>' + ' </span>' + ' </li>' + ' <li class="supported-browsers-list-item">' + ' <img width="125" height="125" src="./images/safari.ico" />' + ' <br />' + ' <span class="supported-browser-download">' + ' <a href="https://www.apple.com/safari/" target="_blank" rel="noopener">' + ' Apple Safari' + ' </a>' + ' </span>' + ' </li>' + ' </ul>' + '</div>' + '<div v-if="!$root.isObsoleteBrowser">' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("LogIn") }}' + ' </div>' + ' </div>' + ' </div>' + generalErrorMessage + ' <form v-on:submit.prevent="onSubmit" novalidate="true">' + ' <transition name="fade-slide"><div v-if="!isIndividual" class="row sign-input">' + ' <div class="col">' + ' <div class="form-group input-icon input-icon-right">' + ' <input class="form-control form-control-lg" type="text" v-bind:placeholder="$t(\'CompanyNIT\')">' + ' </div>' + ' </div>' + ' </div></transition>' + '' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ref="formField1" v-bind:required="true" isUsed="true" v-bind:placeholder="$t(\'LoginPlaceholder\', { loginableProps: getPropsUsedForLogin.map(function (prop) { return prop.placeholder && prop.placeholder[$i18n.locale]; }).join(\', \') })" type="text" v-bind:tooltipMessage="$t(\'LoginPlaceholder\', { loginableProps: getPropsUsedForLogin.map(function (prop) { return prop.placeholder && prop.placeholder[$i18n.locale]; }).join(\', \') })" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-input mb-1">' + ' <div class="col">' + ' <form-input ref="formField2"' + ' v-bind:required="true"' + ' isUsed="true"' + ' v-bind:placeholder="(getPwdSetup && getPwdSetup.placeholder && getPwdSetup.placeholder[$i18n.locale]) || \'Password\'"' + ' type="password"' + ' v-bind:isPassword="true"' + ' v-bind:tooltipMessage="(getPwdSetup && getPwdSetup.tooltipMessage && getPwdSetup.tooltipMessage[$i18n.locale]) || \'PasswordTooltip\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-actions mb-4">' + ' <div class="col">' + ' <div class="form-group">' + ' <div class="row">' + ' <div class="col text-left">' + ' <router-link v-bind:to="{ path: \'request-reset-password\', query: this.$root.oauthVerifyRequest }">{{ $t("ResetPassword") }}</router-link>' + ' </div>' + ' <div class="col text-right">' + ' <router-link v-bind:to="{ path: \'change-password\', query: this.$root.oauthVerifyRequest }">{{ $t("ChangePassword") }}</router-link>' + ' </div>' + ' </div>' + ' </div>' + ' </div>' + ' </div>' + '' + ' <div class="row sign-actions mb-4">' + ' <div class="col text-center">' + ' <submit-button v-bind:isPending="isPending" v-bind:buttonText="\'LogIn\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-alt-actions mb-4">' + ' <div class="col text-center">' + ' {{ $t(\'NoAccount?\') }} <router-link v-bind:to="{ path: \'sign-up\', query: this.$root.oauthVerifyRequest }">{{ $t("CreateAnAccount") }}</router-link>' + ' </div>' + ' </div>' + '</form>' + '</div>' + '</main-wrapper>', data: function () { return { isIndividual: true, showGeneralError: false, errorMessage: '', isPending: false, page: 'login', submitUrl: baseUrl + 'dologin' } }, methods: { getFetchData: function (options) { return Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { code: this.$root.code, username: this.$refs.formField1.value, password: this.$refs.formField2.value, captcha: options && options.captcha_response || "" })) }) } } } }, { path: '/sign-up', component: { extends: FormBase, template: '<main-wrapper>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("CreateAnAccount") }}' + ' </div>' + ' </div>' + ' </div>' + generalErrorMessage + ' <form enctype="multipart/form-data" v-on:submit.prevent="onSubmit" novalidate="true">' + ' <div class="row sign-input mb-3">' + ' <template v-for="fieldGroup in fieldGroups">' + ' <div v-for="(field, idx) in fieldGroup" v-bind:key="idx" class="col">' + ' <transition name="fade-slide">' + ' <form-input-checkbox v-if="field.type === \'checkbox\'" ref="formField" v-bind:name="field.name" v-bind="field" v-bind:propertyId="field.id" />' + ' <form-input-file v-if="field.type === \'file\'" ref="formField" v-bind:name="field.name" v-bind="field" v-bind:propertyId="field.id" />' + ' <form-input v-if="![\'file\', \'checkbox\'].includes(field.type)" ref="formField" v-bind:name="field.name" v-bind:initValue="field.value" v-bind="field" v-on:paste="onPaste($event)" v-on:change="validateWithExtraField(field, \'password\', \'repeat_password\'); validateWithExtraField(field, \'primary_email\', \'repeat_email\'); validateUniqueLoginable(field)" />' + ' </transition>' + ' </div>' + ' <div class="w-100"></div>' + ' </template>' + ' </div>' + ' <div class="row sign-info mb-4">' + ' <div class="col">' + ' <div class="sign-infobox">' + ' {{ $t("RegisterInfobox") }}' + ' </div>' + ' </div>' + ' </div>' + ' <div class="row sign-actions mb-4">' + ' <div class="col-6 offset-3 text-center">' + ' <submit-button v-bind:isPending="isPending" v-bind:buttonText="\'RegisterForAccount\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-alt-actions mb-4">' + ' <div class="col-6 offset-3 text-center">' + ' {{ $t(\'HasAccount?\')}} <router-link v-bind:to="{ path: \'/\', query: this.$root.oauthVerifyRequest }">{{ $t("SignIn") }}</router-link>' + ' </div>' + ' </div>' + '</form>' + '</main-wrapper>', data: function () { return { isIndividual: true, showGeneralError: false, errorMessage: '', isPending: false, page: 'register', hasEID: false } }, computed: { submitUrl: function () { return baseUrl + 'signupSPA?lang=' + this.$root.$i18n.locale }, fieldConfigsClean: function () { var result = [], tmp = [], fields = this.$root.getRegistrationFields var locale = this.$i18n.locale fields = fields.filter(function (field) { return field.usedOnForm }.bind(this)) fields.forEach(function (field, index) { var fieldConfigured = Object.assign({ isUsed: true }, field) Object.keys(fieldConfigured).forEach(function (key) { if (typeof fieldConfigured[key] === 'object') { if (!fieldConfigured[key]) return fieldConfigured[key] = fieldConfigured[key][locale] } }) if (fieldConfigured.pattern) { fieldConfigured.pattern = new RegExp(fieldConfigured.pattern) } COMPONENTS.some(function (c) { if (c.meta.name === field.type) { fieldConfigured.customValidationComponent = c fieldConfigured.inputMask = c.inputMask.vue return true } }); result.push(fieldConfigured) }.bind(this)) return result }, loginable: function () { return this.fieldConfigsClean.filter(function (f) { return f.usedForLogin; }); }, fieldGroups: function () { var result = [], tmp = [] this.fieldConfigsClean.forEach(function (field, index) { tmp.push(field) if (tmp.length === 2 || (index === (this.fieldConfigsClean.length - 1))) { result.push(tmp) tmp = [] } }.bind(this)) return result } }, methods: { getFetchData: function (options) { var formData = { properties: [] }, isPrimaryEmailIncluded = false, self = this this.$refs.formField.forEach(function (field) { if (field.value == null) return if (field.name === "repeat_password") return self.fieldConfigsClean.some(function (item) { if (item.name === 'primary_email') { isPrimaryEmailIncluded = true } if (item.name === field.name) { if (field.name === "password") { formData[field.name] = { id: item.id, value: field.value } return true; } if (field.type === "checkbox") { formData.properties.push({ id: item.id, value: field.value ? "true" : "false" }) return true; } if (field.name === "name") { formData[field.name] = { id: item.id, value: field.value } } formData.properties.push({ id: item.id, value: field.value }) return true } }) }) if (!isPrimaryEmailIncluded) { this.$root.getRegistrationFields.some(function (field) { if (field.name !== 'primary_email') return formData.properties.push({ id: field.id, value: create_UUID() + '@invalidemail.com' }) return true }) } return Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { code: this.$root.code, captcha: options && options.captcha_response || "" }, formData))}) }, // onPaste: function(ev) { // if (ev.target && ev.target.name === "repeat_email") { // // ev.preventDefault(); // } // }, // name2 is the repeated one validateWithExtraField: function (target, mainName, repeatedName) { var mainField, repeatField if (![mainName, repeatedName].some(function () { return target.name === target.name; })) { return } this.$refs.formField.some(function (field) { if (field.name === mainName) { mainField = field return true } }) this.$refs.formField.some(function (field) { if (field.name === repeatedName) { repeatField = field return true } }) if (!mainField || !repeatField) return repeatField.isInvalidatedExternally = (repeatField.value !== mainField.value) }, validateUniqueLoginable: function (target) { this.errorMessage = "" this.showGeneralError = false var errMsg = this.$t("The following fields cannot be the same:") + " " + this.loginable.map(function (f) { return f.placeholder }).join(", ") var self = this if (!this.loginable.some(function (loginableField) { return loginableField.name === target.name; })) { return; } var referencedFields = []; this.loginable.forEach(function (loginable) { self.$refs.formField.some(function (field) { if (field.name === loginable.name) { referencedFields.push(field) return true } }) }) if (referencedFields.some(function (f) { f.isInvalidatedExternally = referencedFields.some(function (f2) { return (f !== f2) && f2.value && f.value && (f.value === f2.value) }) if (f.isInvalidatedExternally) { f.errorMessage = errMsg } return f.isInvalidatedExternally })) { this.showGeneralError = true this.errorMessage = errMsg } } } } }, { path: '/change-password', component: { extends: FormBase, template: '<main-wrapper>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("ChangePassword") }}' + ' </div>' + ' </div>' + ' </div>' + generalSuccessMessage + generalErrorMessage + ' <form v-on:submit.prevent="onSubmit" novalidate="true">' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ref="formField1" v-bind:required="true" isUsed="true" placeholder="LoginName" type="text" v-bind:tooltipMessage="\'LoginName\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ' + ' ref="formField4" '+ ' v-bind:required="true" '+ ' v-bind:isUsed="true" '+ ' placeholder="CurrentPassword" '+ ' type="password"' + ' v-bind:isPassword="true" />' + ' </div>' + ' </div>' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ' + ' ref="formField2" '+ ' v-bind:required="true" '+ ' v-bind:isUsed="true" '+ ' placeholder="NewPassword" '+ ' type="password" ' + ' v-bind:isPassword="true"' + ' v-bind:tooltipMessage="(getPwdSetup && getPwdSetup.tooltipMessage && getPwdSetup.tooltipMessage[$i18n.locale]) || \'PasswordTooltip\'"'+ ' v-on:change="validateRepeatPassword" />' + ' </div>' + ' </div>' + ' <div class="row sign-input mb-1">' + ' <div class="col">' + ' <form-input ' + ' ref="formField3" '+ ' v-bind:required="true" '+ ' v-bind:isUsed="true" '+ ' placeholder="RepeatNewPassword" '+ ' type="password" '+ ' v-bind:isPassword="true"' + ' v-bind:isInvalidatedExternally="false" '+ ' v-bind:externalValidationErrorMessage="\'PasswordAndRepeatDontMatch\'" '+ ' v-on:change="validateRepeatPassword" />' + ' </div>' + ' </div>' + ' <div class="row sign-actions mb-4">' + ' <div class="col text-center">' + ' <submit-button v-bind:isPending="isPending" v-bind:buttonText="\'RequestANewPassword\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-alt-actions mb-4">' + ' <div class="col-6 offset-3 text-center">' + ' <router-link v-bind:to="{ path: \'/\', query: this.$root.oauthVerifyRequest }">{{ $t("SignIn") }}</router-link> | <router-link v-bind:to="{ path: \'sign-up\', query: this.$root.oauthVerifyRequest }">{{ $t("CreateAnAccount") }}</router-link>' + ' </div>' + ' </div>' + '</form>' + '</main-wrapper>', data: function () { return { isIndividual: true, showGeneralSuccess: false, successMessage: '', showGeneralError: false, errorMessage: '', isPending: false, page: 'login', hasEID: false, submitUrl: baseUrl + 'changepsw' } }, methods: { getFetchData: function (options) { return Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { code: this.$root.code, login: this.$refs.formField1.value, newPassword: this.$refs.formField2.value, password: this.$refs.formField4.value, captcha: options && options.captcha_response || "" })) }) }, onDataReceived: function (data) { if (this.responseStatus === 200) { this.successMessage = this.$t("PasswordChangedSuccess") this.showGeneralSuccess = true this.isPending = false return; } this.throwErrorFromData(data) }, validateRepeatPassword: function () { var password = this.$refs.formField2 var repeatPassword = this.$refs.formField3 repeatPassword.isInvalidatedExternally = (repeatPassword.value !== password.value) } } } }, { path: '/request-reset-password', component: { extends: FormBase, template: '<main-wrapper>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("ResetPassword") }}' + ' </div>' + ' </div>' + ' </div>' + generalSuccessMessage + generalErrorMessage + ' <form v-on:submit.prevent="onSubmit" novalidate="true">' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ref="formField1" v-bind:required="true" isUsed="true" placeholder="LoginName" type="text" v-bind:tooltipMessage="\'LoginName\'" />' + ' </div>' + ' </div>' + ' <div class="row sign-actions mb-4">' + ' <div class="col text-center">' + ' <submit-button v-bind:isPending="isPending" v-bind:buttonText="\'ResetPassword\'" />' + ' </div>' + ' </div>' + '' + ' <div class="row sign-alt-actions mb-4">' + ' <div class="col-6 offset-3 text-center">' + ' <router-link v-bind:to="{ path: \'/\', query: this.$root.oauthVerifyRequest }">{{ $t("SignIn") }}</router-link> | <router-link v-bind:to="{ path: \'sign-up\', query: this.$root.oauthVerifyRequest }">{{ $t("CreateAnAccount") }}</router-link>' + ' </div>' + ' </div>' + '</form>' + '</main-wrapper>', data: function () { return { showGeneralSuccess: false, successMessage: '', showGeneralError: false, errorMessage: '', isPending: false, page: 'login', submitUrl: baseUrl + 'resetPassword' } }, methods: { getFetchData: function (options) { return Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { code: this.$root.code, login: this.$refs.formField1.value, captcha: options && options.captcha_response || "" })) }) }, onDataReceived: function (data) { if (this.responseStatus === 200) { this.successMessage = this.$t("ResetLinkSent") this.showGeneralSuccess = true this.isPending = false return } this.throwErrorFromData(data) } } } }, { path: '/reset-password', component: { extends: FormBase, template: '<main-wrapper>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("ResetPassword") }}' + ' </div>' + ' </div>' + ' </div>' + generalSuccessMessage + generalErrorMessage + ' <transition name="fade-slide"><form v-show="showForm" v-on:submit.prevent="onSubmit" novalidate="true">' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ' + ' ref="formField1" ' + ' v-bind:required="true"' + ' isUsed="true"' + ' placeholder="NewPassword"' + ' type="password"' + ' v-bind:isPassword="true"' + ' v-bind:tooltipMessage="(getPwdSetup && getPwdSetup.tooltipMessage && getPwdSetup.tooltipMessage[$i18n.locale]) || \'PasswordTooltip\'" ' + ' v-on:change="validateRepeatPassword" />' + ' </div>' + ' </div>' + ' <div class="row sign-input">' + ' <div class="col">' + ' <form-input ' + ' ref="formField2" '+ ' v-bind:required="true" '+ ' v-bind:isUsed="true" '+ ' placeholder="RepeatNewPassword" '+ ' type="password" '+ ' v-bind:isPassword="true"' + ' v-bind:isInvalidatedExternally="false" '+ ' v-bind:externalValidationErrorMessage="\'PasswordAndRepeatDontMatch\'" '+ ' v-on:change="validateRepeatPassword" />' + ' </div>' + ' </div>' + ' <div class="row sign-actions mb-4">' + ' <div class="col text-center">' + ' <submit-button v-bind:isPending="isPending" v-bind:buttonText="\'ResetPassword\'" />' + ' </div>' + ' </div>' + '</form></transition>' + '</main-wrapper>', data: function () { return { showGeneralSuccess: false, successMessage: '', showGeneralError: false, errorMessage: '', isPending: false, page: 'login', submitUrl: baseUrl + 'reset-password', passwordCode: null, showForm: false } }, methods: { onDataReceived: function (data) { if (this.responseStatus === 200) { this.successMessage = this.$t("PasswordResetSuccess") this.showGeneralSuccess = true this.isPending = false this.showForm = false return; } this.throwErrorFromData(data) }, getPwdCode: function () { self = this this.isPending = true if (!this.$route.query.code) { this.showGeneralError = true this.errorMessage = this.$t("InvalidLink") this.isPending = false return } fetch( this.submitUrl + '?code=' + this.$route.query.code, { method: "GET", headers: new Headers({ 'Content-Type': 'application/json;charset=UTF-8' }) } ).then(function (response) { self.responseStatus = response.status if (self.responseStatus === 200) { return response.json() } throw new Error(self.$t("InvalidLink")) }).then(function (data) { self.showForm = true self.passwordCode = data.passwordCode self.isPending = false }).catch(this.onSendError) }, getFetchData: function (options) { return Object.assign(this.$root.buildFetchData(), { body: JSON.stringify( Object.assign(this.$root.getCommonRequestParams(), { passwordCode: this.passwordCode, password: this.$refs.formField1.value, captcha: options && options.captcha_response || "" })) }) }, validateRepeatPassword: function () { var password = this.$refs.formField1 var repeatPassword = this.$refs.formField2 repeatPassword.isInvalidatedExternally = (repeatPassword.value !== password.value) } }, created: function () { var self = this; var resolve = function () { Object.keys(self.$root.pages).forEach(function (pageKey) { self.$root.pages[pageKey] = (pageKey === self.page || false); }) self.getPwdCode() } self.$root.promise.finally(resolve) } } }, { path: '/verified', component: { template: '<main-wrapper>' + ' <div class="row mb-4">' + ' <div class="col">' + ' <div class="sign-title">' + ' {{ $t("EmailVerification") }}' + ' </div>' + ' </div>' + ' </div>' + generalSuccessMessage + generalErrorMessage + '</main-wrapper>', data: function () { return { showGeneralSuccess: false, successMessage: '', showGeneralError: false, errorMessage: '', isPending: false, page: 'login' } }, methods: { sendCode: function () { var self = this if (!this.$route.query.code) { this.errorMessage = this.$t("InvalidLink") this.showGeneralError = true return } var responseStatus fetch( baseUrl + 'verified', Object.assign(this.$root.buildFetchData(), { body: JSON.stringify({ code: this.$route.query.code }) }) ).then(function (response) { responseStatus = response.status if (responseStatus === 200) { self.successMessage = self.$t("EmailVerified") self.showGeneralSuccess = true return; } throw new Error(self.$t("InvalidLink")) }).catch(function (err) { self.errorMessage = err.message self.showGeneralError = true }) } }, created: function () { var self = this; var resolve = function () { Object.keys(self.$root.pages).forEach(function (pageKey) { self.$root.pages[pageKey] = (pageKey === self.page || false); }) self.sendCode() } self.$root.promise.finally(resolve) } }, } ] }), components: { 'vue-recaptcha': VueRecaptcha }, created: function () { var self = this, params = [], clientId if (!Object.assign || !fetch) { this.isObsoleteBrowser = true; return; } var fetchData = Object.assign(self.buildFetchData(), { method: "GET" }); delete fetchData.body; var translationsPromise = fetch(baseUrl + "translations/" + this.$i18n.locale, fetchData).then(function (response) { if (response && response.status === 200) { return response.json(); } }).then(function (data) { self.$i18n.setLocaleMessage(self.$i18n.locale, data); }); this.oauthVerifyRequest.lang = this.$i18n.locale; clientId = getParameterByName("client_id") self.promise = translationsPromise.then(function () { return self.setupSystemDetailConfig().then(function () { return self.setupClientDetails().then(function () { if (!clientId) return; self.oauthVerifyRequest.client_id = clientId; return fetch(baseUrl + "OAuth2/file/" + clientId).then(function (response) { if (response && response.status === 200) { self.logoSrc = baseUrl + "OAuth2/file/" + clientId; } }) }) }) }) if (self.$route.path.indexOf('/verified') !== -1) return; if (self.$route.path.indexOf('/reset-password') !== -1) return; if (Object.keys(this.oauthVerifyRequest).some(function (k) { if (self.oauthVerifyRequest[k] == null) { self.oauthVerifyRequest[k] = self.$route.query[k]; // try to read from url } if (!self.oauthVerifyRequest[k] == null) return true; })) { //some param missing if (self.oauthVerifyRequest.redirect_uri) { window.location = self.oauthVerifyRequest.redirect_uri + "?error=missingParam" } return; } Object.keys(self.$route.query).forEach(function (key) { if (!key.startsWith("prefill_register_")) { return; } self.prefilledValues[key.slice("prefill_register_".length)] = self.$route.query[key]; }); var originalResponse fetch(baseUrl + 'authorize_token_spa', this.buildFetchData({ credentials: 'same-origin' }) ).then(function (response) { originalResponse = response return response.json(); }).then(function (data) { if (originalResponse.status !== 200) { throw new Error(data.error || data.message || 'Unknown server error') } if (data.authorization_code) { window.location = self.oauthVerifyRequest.redirect_uri + "?code=" + data.authorization_code + "&state=" + self.oauthVerifyRequest.state return } self.code = data.code; var fetchData = Object.assign(self.buildFetchData(), { method: "GET" }) delete fetchData.body fetch(baseUrl + 'configuration/property', fetchData).then(function (res) { if (res.status === 200) { return res.json() } throw new Error("Could not load configuration") }).then(function (data) { self.registrationFormConfig = data if (self.$route.query.newUser) { self.$router.push({ path: 'sign-up', query: self.oauthVerifyRequest }) } self.dataLoaded = true }).catch(function (err) { console.log(err.message) }) }).catch(function (err) { window.location = self.$route.query.redirect_uri + "?error=" + err.message }) }, data: function () { return { logoSrc: "./configuration/settings/cas-logo", code: null, landingPageUri: "", dataLoaded: false, oauthVerifyRequest: { state: null, redirect_uri: null, lang: null, client_id: null, ignoreSSO: null }, pages: { login: null, register: null }, registrationFormConfig: null, isObsoleteBrowser: false, systemDetailConfig: null, captchaLoaded: false, prefilledValues: {} } }, methods: { setupClientDetails: function () { var self = this; var fetchData = Object.assign(self.buildFetchData(), { method: "GET" }) delete fetchData.body return fetch(baseUrl + 'client_details?' + Object.keys(self.oauthVerifyRequest).map(function (pName) { return pName + '=' + encodeURIComponent(self.oauthVerifyRequest[pName]); }).join('&'), fetchData).then(function (res) { if (res.status === 200) { return res.json() } throw new Error("Could not load client details") }).then(function (data) { if (!data) return if (data.landingPageUri) { self.landingPageUri = data.landingPageUri } if (data.logoSrc) { self.$root.logoSrc = data.logoSrc } }).catch(function (e) { console.log(e.message); }); }, setupSystemDetailConfig: function () { var self = this; var fetchData = Object.assign(self.buildFetchData(), { method: "GET" }) delete fetchData.body return fetch(baseUrl + 'configuration/settings', fetchData).then(function (res) { if (res.status === 200) { return res.json() } throw new Error("Could not load system details") }).then(function (data) { self.systemDetailConfig = data if (!self.systemDetailConfig) return if (self.systemDetailConfig.landingPageUri) { self.$root.landingPageUri = self.systemDetailConfig.landingPageUri } if (self.systemDetailConfig.captchaEnabled && self.systemDetailConfig.captchaUrl) { let recaptchaScript = document.createElement('script') recaptchaScript.setAttribute('src', self.systemDetailConfig.captchaUrl) recaptchaScript.async = true recaptchaScript.defer = true document.head.appendChild(recaptchaScript) self.captchaLoaded = true } }).catch(function (e) { console.log(e.message) }); }, getCommonRequestParams: function () { return Object.assign({}, this.oauthVerifyRequest) }, buildFetchData: function (/* opts */) { return Object.assign({ method: 'POST', credentials: 'same-origin', body: JSON.stringify(this.getCommonRequestParams()), headers: new Headers({ 'Content-Type': 'application/json;charset=UTF-8' }) }, Object(arguments[0])) }, sendRequest: function (response) { this.$children.some((child) => { if (child.page !== undefined) { // its form-base component child.sendRequest({captcha_response: response}); return true; } }) this.$refs.recaptcha.reset(); } }, computed: { getRegistrationFields: function () { var result = [], self = this, defaultData if (!this.registrationFormConfig) return result this.registrationFormConfig.sort(function (a, b) { return a.ordering - b.ordering; }).forEach(function (item) { defaultData = {} if (item.name === 'password') { defaultData = { placeholder: {}, tooltipMessage: {}, type: "password", isPassword: true, customValidateAsync: validatePwd } defaultData.placeholder[self.$i18n.locale] = self.$t("Password"); defaultData.tooltipMessage[self.$i18n.locale] = self.$t("PasswordTooltip"); } if (item.name === 'name') { defaultData = { placeholder: {} } defaultData.placeholder[self.$i18n.locale] = self.$t("Surname"); } result.push(Object.assign(defaultData, item, JSON.parse(item.display), { required: item.requiredOnForm, usedForLogin: item.used_for_login })) if (item.name === 'password') { var rePwd = { name: "repeat_password", placeholder: {}, type: "password", excludeFromSubmit: true, required: true, isUsed: true, usedOnForm: true, isPassword: true, isInvalidatedExternally: false, externalValidationErrorMessage: {} } rePwd.placeholder[self.$i18n.locale] = self.$t("RepeatPassword"); rePwd.externalValidationErrorMessage[self.$i18n.locale] = self.$t("repeatPasswordErrorMessage") result.push(rePwd); } if (item.name === 'primary_email' && item.usedOnForm) { var rEmail = { name: "repeat_email", placeholder: {}, type: "email", excludeFromSubmit: true, required: true, isUsed: true, usedOnForm: true, isInvalidatedExternally: false, externalValidationErrorMessage: {} } rEmail.placeholder[self.$i18n.locale] = self.$t("confirmYourEmail") rEmail.externalValidationErrorMessage[self.$i18n.locale] = self.$t("repeatEmailErrorMessage") result.push(rEmail) } }) if (!Object.keys(self.prefilledValues).length) { return result } else { return result.map(function (field) { if (self.prefilledValues[field.name]) { field.value = self.prefilledValues[field.name] } return field }) } } } }).$mount('#app') </script> </html>