| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| <template> | |
| <a-form | |
| id="formLogin" | |
| class="user-layout-login" | |
| :ref="formRef" | |
| :model="form" | |
| :rules="rules" | |
| @finish="handleSubmit" | |
| v-ctrl-enter="handleSubmit" | |
| > | |
| <a-tabs | |
| class="tab-center" | |
| :key="$i18n.locale" | |
| :activeKey="customActiveKey" | |
| size="large" | |
| :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }" | |
| @change="handleTabClick" | |
| :animated="false" | |
| > | |
| <a-tab-pane key="cs"> | |
| <template #tab> | |
| <span> | |
| <safety-outlined /> | |
| {{ $t('label.login.portal') }} | |
| </span> | |
| </template> | |
| <a-form-item v-if="$config.multipleServer" name="server" ref="server"> | |
| <a-select | |
| size="large" | |
| :placeholder="$t('server')" | |
| v-model:value="form.server" | |
| @change="onChangeServer" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }"> | |
| <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase" :label="item.name"> | |
| <template #prefix> | |
| <database-outlined /> | |
| </template> | |
| {{ item.name }} | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item ref="username" name="username"> | |
| <a-input | |
| size="large" | |
| type="text" | |
| v-focus="true" | |
| :placeholder="$t('label.username')" | |
| v-model:value="form.username" | |
| > | |
| <template #prefix> | |
| <user-outlined /> | |
| </template> | |
| </a-input> | |
| </a-form-item> | |
| <a-form-item ref="password" name="password"> | |
| <a-input-password | |
| size="large" | |
| type="password" | |
| autocomplete="false" | |
| :placeholder="$t('label.password')" | |
| v-model:value="form.password" | |
| > | |
| <template #prefix> | |
| <lock-outlined /> | |
| </template> | |
| </a-input-password> | |
| </a-form-item> | |
| <a-form-item ref="domain" name="domain"> | |
| <a-input | |
| size="large" | |
| type="text" | |
| :placeholder="$t('label.domain')" | |
| v-model:value="form.domain" | |
| > | |
| <template #prefix> | |
| <project-outlined /> | |
| </template> | |
| </a-input> | |
| </a-form-item> | |
| <a-form-item ref="project" name="project" v-if="$config.displayProjectFieldOnLogin"> | |
| <a-input | |
| size="large" | |
| type="text" | |
| :placeholder="$t('label.project')" | |
| v-model:value="form.project" | |
| > | |
| <template #prefix> | |
| <block-outlined /> | |
| </template> | |
| </a-input> | |
| </a-form-item> | |
| </a-tab-pane> | |
| <a-tab-pane key="saml" :disabled="idps.length === 0"> | |
| <template #tab> | |
| <span> | |
| <audit-outlined /> | |
| {{ $t('label.login.single.signon') }} | |
| </span> | |
| </template> | |
| <a-form-item v-if="$config.multipleServer" name="server" ref="server"> | |
| <a-select | |
| size="large" | |
| :placeholder="$t('server')" | |
| v-model:value="form.server" | |
| @change="onChangeServer" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase" :label="item.name"> | |
| <template #prefix> | |
| <database-outlined /> | |
| </template> | |
| {{ item.name }} | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item name="idp" ref="idp"> | |
| <a-select | |
| v-model:value="form.idp" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option v-for="(idp, idx) in idps" :key="idx" :value="idp.id" :label="idp.orgName"> | |
| {{ idp.orgName }} | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| </a-tab-pane> | |
| <a-tab-pane key="oauth" :disabled="!socialLogin"> | |
| <template #tab> | |
| <span style="display: inline-flex; align-items: center; gap: 4px; color: inherit;"> | |
| <img src="/assets/github.svg" alt="GitHub" class="oauth-tab-icon" :class="{ 'oauth-tab-icon--disabled': !socialLogin }" style="width: 16px; height: 16px; display: block;" /> | |
| <img src="/assets/google.svg" alt="Google" class="oauth-tab-icon" :class="{ 'oauth-tab-icon--disabled': !socialLogin }" style="width: 16px; height: 16px; display: block;" /> | |
| <span>{{ $t('label.login.external') }}</span> | |
| </span> | |
| </template> | |
| <a-form-item name="oauthDomain"> | |
| <a-input | |
| size="large" | |
| type="text" | |
| :placeholder="$t('label.domain')" | |
| v-model:value="form.oauthDomain" | |
| @pressEnter="handleOauthDomainSubmit" | |
| @blur="handleOauthDomainSubmit" | |
| > | |
| <template #prefix> | |
| <project-outlined /> | |
| </template> | |
| </a-input> | |
| </a-form-item> | |
| <div | |
| v-if="(oauthGithubProvider || oauthGoogleProvider || oauthKeycloakProvider) && !form.oauthDomain" | |
| style="text-align: center; color: #999; font-size: 12px; margin-bottom: 8px;"> | |
| Enter your domain to see domain-specific providers | |
| </div> | |
| <div class="center" v-if="oauthGithubProvider || oauthGoogleProvider || oauthKeycloakProvider"> | |
| <div class="social-auth" v-if="oauthGithubProvider"> | |
| <a-button | |
| @click="handleGithubProviderAndDomain" | |
| tag="a" | |
| color="primary" | |
| :href="getGitHubUrl(from)" | |
| class="auth-btn github-auth" | |
| style="height: 38px; width: 185px; padding: 0; margin-bottom: 5px;" > | |
| <img src="/assets/github.svg" alt="GitHub" style="width: 32px; padding: 5px" /> | |
| <a-typography-text>Sign in with GitHub</a-typography-text> | |
| </a-button> | |
| </div> | |
| <div class="social-auth" v-if="oauthGoogleProvider"> | |
| <a-button | |
| @click="handleGoogleProviderAndDomain" | |
| tag="a" | |
| color="primary" | |
| :href="getGoogleUrl(from)" | |
| class="auth-btn google-auth" | |
| style="height: 38px; width: 185px; padding: 0; margin-bottom: 5px;" > | |
| <img src="/assets/google.svg" alt="Google" style="width: 32px; padding: 5px" /> | |
| <a-typography-text>Sign in with Google</a-typography-text> | |
| </a-button> | |
| </div> | |
| <div class="social-auth" v-if="oauthKeycloakProvider"> | |
| <a-button | |
| @click="handleKeycloakProviderAndDomain" | |
| tag="a" | |
| color="primary" | |
| :href="getKeycloakUrl(from)" | |
| class="auth-btn keycloak-auth" | |
| style="height: 38px; width: 185px; padding: 0" > | |
| <img src="/assets/keycloak.svg" alt="Keycloak" style="width: 32px; padding: 5px" /> | |
| <a-typography-text>Sign in with Keycloak</a-typography-text> | |
| </a-button> | |
| </div> | |
| </div> | |
| <div v-else-if="oauthLoading" style="text-align: center; padding: 20px 0;"> | |
| <a-spin /> | |
| </div> | |
| <div v-else style="text-align: center; color: #999; padding: 20px 0;"> | |
| <span v-if="oauthDomainQueried && form.oauthDomain">No OAuth providers configured for this domain</span> | |
| <span v-else>Enter your domain to see available providers</span> | |
| </div> | |
| </a-tab-pane> | |
| </a-tabs> | |
| <a-form-item v-if="customActiveKey !== 'oauth'"> | |
| <a-button | |
| size="large" | |
| type="primary" | |
| html-type="submit" | |
| class="login-button" | |
| :loading="state.loginBtn" | |
| :disabled="state.loginBtn" | |
| ref="submit" | |
| @click="handleSubmit" | |
| >{{ $t('label.login') }}</a-button> | |
| </a-form-item> | |
| <a-row justify="space-between"> | |
| <a-col> | |
| <translation-menu/> | |
| </a-col> | |
| <a-col v-if="forgotPasswordEnabled"> | |
| <router-link :to="{ name: 'forgotPassword' }"> | |
| {{ $t('label.forgot.password') }} | |
| </router-link> | |
| </a-col> | |
| </a-row> | |
| </a-form> | |
| </template> | |
| <script> | |
| import { ref, reactive, toRaw } from 'vue' | |
| import { getAPI, postAPI } from '@/api' | |
| import store from '@/store' | |
| import { mapActions } from 'vuex' | |
| import { sourceToken } from '@/utils/request' | |
| import { SERVER_MANAGER } from '@/store/mutation-types' | |
| import TranslationMenu from '@/components/header/TranslationMenu' | |
| export default { | |
| components: { | |
| TranslationMenu | |
| }, | |
| data () { | |
| return { | |
| idps: [], | |
| customActiveKey: 'cs', | |
| customActiveKeyOauth: false, | |
| loginBtn: false, | |
| email: '', | |
| secretcode: '', | |
| oauthexclude: '', | |
| socialLogin: false, | |
| googleprovider: false, | |
| githubprovider: false, | |
| keycloakprovider: false, | |
| googleredirecturi: '', | |
| githubredirecturi: '', | |
| keycloakredirecturi: '', | |
| googleclientid: '', | |
| githubclientid: '', | |
| keycloakclientid: '', | |
| keycloakauthorizeurl: '', | |
| oauthGoogleProvider: false, | |
| oauthGithubProvider: false, | |
| oauthKeycloakProvider: false, | |
| oauthGoogleClientId: '', | |
| oauthGithubClientId: '', | |
| oauthKeycloakClientId: '', | |
| oauthGoogleRedirectUri: '', | |
| oauthGithubRedirectUri: '', | |
| oauthKeycloakRedirectUri: '', | |
| oauthKeycloakAuthorizeUrl: '', | |
| oauthLoading: false, | |
| oauthDomainQueried: false, | |
| loginType: 0, | |
| state: { | |
| time: 60, | |
| loginBtn: false, | |
| loginType: 0 | |
| }, | |
| server: '', | |
| forgotPasswordEnabled: false, | |
| project: null | |
| } | |
| }, | |
| created () { | |
| if (this.$config.multipleServer) { | |
| this.server = this.$localStorage.get(SERVER_MANAGER) || this.$config.servers[0] | |
| } | |
| this.initForm() | |
| if (store.getters.logoutFlag) { | |
| if (store.getters.readyForShutdownPollingJob !== '' || store.getters.readyForShutdownPollingJob !== undefined) { | |
| clearInterval(store.getters.readyForShutdownPollingJob) | |
| } | |
| sourceToken.init() | |
| this.fetchData() | |
| } else { | |
| this.fetchData() | |
| } | |
| }, | |
| methods: { | |
| ...mapActions(['Login', 'Logout', 'OauthLogin']), | |
| initForm () { | |
| this.formRef = ref() | |
| this.form = reactive({ | |
| server: (this.server.apiHost || '') + this.server.apiBase, | |
| username: this.$route.query?.username || '', | |
| domain: this.$route.query?.domain || '', | |
| oauthDomain: '', | |
| project: null | |
| }) | |
| this.rules = reactive({}) | |
| this.setRules() | |
| }, | |
| setRules () { | |
| if (this.customActiveKey === 'cs' && this.customActiveKeyOauth === false) { | |
| this.rules.username = [ | |
| { | |
| required: true, | |
| message: this.$t('message.error.username'), | |
| trigger: 'change' | |
| }, | |
| { | |
| validator: this.handleUsernameOrEmail, | |
| trigger: 'change' | |
| } | |
| ] | |
| this.rules.password = [ | |
| { | |
| required: true, | |
| message: this.$t('message.error.password'), | |
| trigger: 'change' | |
| } | |
| ] | |
| } else { | |
| this.rules.username = [] | |
| this.rules.password = [] | |
| } | |
| }, | |
| fetchData () { | |
| getAPI('listIdps').then(response => { | |
| if (response) { | |
| this.idps = response.listidpsresponse.idp || [] | |
| this.idps.sort(function (a, b) { | |
| if (a.orgName < b.orgName) { return -1 } | |
| if (a.orgName > b.orgName) { return 1 } | |
| return 0 | |
| }) | |
| this.form.idp = this.idps[0].id || '' | |
| } | |
| }) | |
| this.fetchOauthProviders() | |
| postAPI('forgotPassword', {}).then(response => { | |
| this.forgotPasswordEnabled = response.forgotpasswordresponse.enabled | |
| }).catch((err) => { | |
| if (err?.response?.data === null) { | |
| this.forgotPasswordEnabled = true | |
| } else { | |
| this.forgotPasswordEnabled = false | |
| } | |
| }) | |
| }, | |
| fetchOauthProviders (domain) { | |
| const params = {} | |
| if (domain) { | |
| params.domain = domain | |
| this.oauthLoading = true | |
| } | |
| getAPI('listOauthProvider', params).then(response => { | |
| if (response) { | |
| const oauthproviders = response.listoauthproviderresponse.oauthprovider || [] | |
| if (!domain) { | |
| oauthproviders.forEach(item => { | |
| if (item.provider === 'google') { | |
| this.googleprovider = item.enabled | |
| this.googleclientid = item.clientid | |
| this.googleredirecturi = item.redirecturi | |
| } | |
| if (item.provider === 'github') { | |
| this.githubprovider = item.enabled | |
| this.githubclientid = item.clientid | |
| this.githubredirecturi = item.redirecturi | |
| } | |
| if (item.provider === 'keycloak') { | |
| this.keycloakprovider = item.enabled | |
| this.keycloakclientid = item.clientid | |
| this.keycloakredirecturi = item.redirecturi | |
| this.keycloakauthorizeurl = item.authorizeurl | |
| } | |
| }) | |
| const totalCount = response.listoauthproviderresponse.count || 0 | |
| this.socialLogin = totalCount > 0 | |
| this.oauthGithubProvider = this.githubprovider | |
| this.oauthGoogleProvider = this.googleprovider | |
| this.oauthKeycloakProvider = this.keycloakprovider | |
| this.oauthGithubClientId = this.githubclientid | |
| this.oauthGoogleClientId = this.googleclientid | |
| this.oauthKeycloakClientId = this.keycloakclientid | |
| this.oauthGithubRedirectUri = this.githubredirecturi | |
| this.oauthGoogleRedirectUri = this.googleredirecturi | |
| this.oauthKeycloakRedirectUri = this.keycloakredirecturi | |
| this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl | |
| } else { | |
| this.oauthGithubProvider = false | |
| this.oauthGoogleProvider = false | |
| this.oauthKeycloakProvider = false | |
| oauthproviders.forEach(item => { | |
| if (item.provider === 'google') { | |
| this.oauthGoogleProvider = item.enabled | |
| this.oauthGoogleClientId = item.clientid | |
| this.oauthGoogleRedirectUri = item.redirecturi | |
| } | |
| if (item.provider === 'github') { | |
| this.oauthGithubProvider = item.enabled | |
| this.oauthGithubClientId = item.clientid | |
| this.oauthGithubRedirectUri = item.redirecturi | |
| } | |
| if (item.provider === 'keycloak') { | |
| this.oauthKeycloakProvider = item.enabled | |
| this.oauthKeycloakClientId = item.clientid | |
| this.oauthKeycloakRedirectUri = item.redirecturi | |
| this.oauthKeycloakAuthorizeUrl = item.authorizeurl | |
| } | |
| }) | |
| } | |
| } | |
| }).finally(() => { | |
| this.oauthLoading = false | |
| }) | |
| }, | |
| // handler | |
| async handleUsernameOrEmail (rule, value) { | |
| const { state } = this | |
| const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/ | |
| if (regex.test(value)) { | |
| state.loginType = 0 | |
| } else { | |
| state.loginType = 1 | |
| } | |
| return Promise.resolve() | |
| }, | |
| handleTabClick (key) { | |
| this.customActiveKey = key | |
| if (key === 'oauth') { | |
| this.oauthGithubProvider = this.githubprovider | |
| this.oauthGoogleProvider = this.googleprovider | |
| this.oauthKeycloakProvider = this.keycloakprovider | |
| this.oauthGithubClientId = this.githubclientid | |
| this.oauthGoogleClientId = this.googleclientid | |
| this.oauthKeycloakClientId = this.keycloakclientid | |
| this.oauthGithubRedirectUri = this.githubredirecturi | |
| this.oauthGoogleRedirectUri = this.googleredirecturi | |
| this.oauthKeycloakRedirectUri = this.keycloakredirecturi | |
| this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl | |
| } | |
| this.setRules() | |
| }, | |
| handleOauthDomainSubmit () { | |
| const domain = this.form.oauthDomain | |
| if (domain) { | |
| this.oauthDomainQueried = true | |
| this.fetchOauthProviders(domain) | |
| } else { | |
| this.oauthDomainQueried = false | |
| this.oauthGithubProvider = this.githubprovider | |
| this.oauthGoogleProvider = this.googleprovider | |
| this.oauthKeycloakProvider = this.keycloakprovider | |
| this.oauthGithubClientId = this.githubclientid | |
| this.oauthGoogleClientId = this.googleclientid | |
| this.oauthKeycloakClientId = this.keycloakclientid | |
| this.oauthGithubRedirectUri = this.githubredirecturi | |
| this.oauthGoogleRedirectUri = this.googleredirecturi | |
| this.oauthKeycloakRedirectUri = this.keycloakredirecturi | |
| this.oauthKeycloakAuthorizeUrl = this.keycloakauthorizeurl | |
| } | |
| }, | |
| handleGithubProviderAndDomain () { | |
| this.handleDomain() | |
| this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'github') | |
| }, | |
| handleGoogleProviderAndDomain () { | |
| this.handleDomain() | |
| this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'google') | |
| }, | |
| handleKeycloakProviderAndDomain () { | |
| this.handleDomain() | |
| this.$store.commit('SET_OAUTH_PROVIDER_USED_TO_LOGIN', 'keycloak') | |
| }, | |
| handleDomain () { | |
| const values = toRaw(this.form) | |
| const domain = this.customActiveKey === 'oauth' ? values.oauthDomain : values.domain | |
| this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', this.getLoginDomain(domain)) | |
| }, | |
| getLoginDomain (domain) { | |
| if (this.$config.loginBaseDomain) { | |
| if (domain) { | |
| return this.$config.loginBaseDomain + '/' + domain | |
| } | |
| return this.$config.loginBaseDomain | |
| } | |
| if (domain) { | |
| return domain | |
| } | |
| return '/' | |
| }, | |
| getGitHubUrl (from) { | |
| const rootURl = 'https://github.com/login/oauth/authorize' | |
| const clientId = this.customActiveKey === 'oauth' ? this.oauthGithubClientId : this.githubclientid | |
| const options = { | |
| client_id: clientId, | |
| scope: 'user:email', | |
| state: 'cloudstack' | |
| } | |
| const qs = new URLSearchParams(options) | |
| return `${rootURl}?${qs.toString()}` | |
| }, | |
| getGoogleUrl (from) { | |
| const rootUrl = 'https://accounts.google.com/o/oauth2/v2/auth' | |
| const redirectUri = this.customActiveKey === 'oauth' ? this.oauthGoogleRedirectUri : this.googleredirecturi | |
| const clientId = this.customActiveKey === 'oauth' ? this.oauthGoogleClientId : this.googleclientid | |
| const options = { | |
| redirect_uri: redirectUri, | |
| client_id: clientId, | |
| access_type: 'offline', | |
| response_type: 'code', | |
| prompt: 'consent', | |
| scope: [ | |
| 'https://www.googleapis.com/auth/userinfo.profile', | |
| 'https://www.googleapis.com/auth/userinfo.email' | |
| ].join(' '), | |
| state: from | |
| } | |
| const qs = new URLSearchParams(options) | |
| return `${rootUrl}?${qs.toString()}` | |
| }, | |
| getKeycloakUrl (from) { | |
| const rootURl = this.customActiveKey === 'oauth' ? this.oauthKeycloakAuthorizeUrl : this.keycloakauthorizeurl | |
| const redirectUri = this.customActiveKey === 'oauth' ? this.oauthKeycloakRedirectUri : this.keycloakredirecturi | |
| const clientId = this.customActiveKey === 'oauth' ? this.oauthKeycloakClientId : this.keycloakclientid | |
| const options = { | |
| redirect_uri: redirectUri, | |
| client_id: clientId, | |
| response_type: 'code', | |
| scope: 'openid email', | |
| state: 'cloudstack' | |
| } | |
| const qs = new URLSearchParams(options) | |
| return `${rootURl}?${qs.toString()}` | |
| }, | |
| handleSubmit (e) { | |
| e.preventDefault() | |
| if (this.state.loginBtn) return | |
| this.formRef.value.validate().then(() => { | |
| this.state.loginBtn = true | |
| const values = toRaw(this.form) | |
| if (this.$config.multipleServer) { | |
| this.axios.defaults.baseURL = (this.server.apiHost || '') + this.server.apiBase | |
| store.dispatch('SetServer', this.server) | |
| } | |
| if (this.customActiveKey === 'cs') { | |
| const loginParams = { ...values } | |
| delete loginParams.username | |
| loginParams[!this.state.loginType ? 'email' : 'username'] = values.username | |
| loginParams.password = values.password | |
| loginParams.domain = this.getLoginDomain(values.domain) | |
| this.Login(loginParams) | |
| .then((res) => this.loginSuccess(res)) | |
| .catch(err => { | |
| this.requestFailed(err) | |
| this.state.loginBtn = false | |
| }) | |
| } else if (this.customActiveKey === 'saml') { | |
| this.state.loginBtn = false | |
| var samlUrl = this.$config.apiBase + '?command=samlSso' | |
| if (values.idp) { | |
| samlUrl += ('&idpid=' + values.idp) | |
| } | |
| window.location.href = samlUrl | |
| } | |
| }).catch(error => { | |
| this.formRef.value.scrollToField(error.errorFields[0].name) | |
| }) | |
| }, | |
| handleSubmitOauth (provider) { | |
| this.customActiveKeyOauth = true | |
| this.setRules() | |
| this.formRef.value.validate().then(() => { | |
| const values = toRaw(this.form) | |
| const loginParams = { ...values } | |
| delete loginParams.username | |
| loginParams.email = this.email | |
| loginParams.provider = provider | |
| loginParams.secretcode = this.secretcode | |
| loginParams.domain = this.getLoginDomain(values.domain) | |
| this.OauthLogin(loginParams) | |
| .then((res) => this.loginSuccess(res)) | |
| .catch(err => { | |
| this.requestFailed(err) | |
| this.state.loginBtn = false | |
| }) | |
| }) | |
| }, | |
| async loginSuccess (res) { | |
| this.$notification.destroy() | |
| this.$store.commit('SET_COUNT_NOTIFY', 0) | |
| if (store.getters.twoFaEnabled === true && store.getters.twoFaProvider !== '' && store.getters.twoFaProvider !== undefined) { | |
| this.$router.push({ path: '/verify2FA' }).catch(() => {}) | |
| } else if (store.getters.twoFaEnabled === true && (store.getters.twoFaProvider === '' || store.getters.twoFaProvider === undefined)) { | |
| this.$router.push({ path: '/setup2FA' }).catch(() => {}) | |
| } else { | |
| this.$store.commit('SET_LOGIN_FLAG', true) | |
| const values = toRaw(this.form) | |
| if (values.project) { | |
| await this.getProject(values.project) | |
| this.$store.dispatch('ProjectView', this.project.id) | |
| this.$store.dispatch('SetProject', this.project) | |
| this.$store.dispatch('ToggleTheme', this.project.id === undefined ? 'light' : 'dark') | |
| } | |
| this.$router.push({ path: '/dashboard' }).catch(() => {}) | |
| } | |
| }, | |
| getProject (projectName) { | |
| return new Promise((resolve, reject) => { | |
| getAPI('listProjects', { | |
| response: 'json', | |
| domainId: this.selectedDomain, | |
| details: 'min' | |
| }).then((response) => { | |
| const projects = response.listprojectsresponse.project | |
| this.project = projects.filter(project => project.name === projectName)?.[0] || null | |
| resolve(this.project) | |
| }).catch((error) => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| }) | |
| }, | |
| requestFailed (err) { | |
| if (err && err.response && err.response.data && err.response.data.loginresponse) { | |
| const error = err.response.data.loginresponse.errorcode + ': ' + err.response.data.loginresponse.errortext | |
| this.$message.error(`${this.$t('label.error')} ${error}`) | |
| } else if (err && err.response && err.response.data && err.response.data.oauthloginresponse) { | |
| const error = err.response.data.oauthloginresponse.errorcode + ': ' + err.response.data.oauthloginresponse.errortext | |
| this.$message.error(`${this.$t('label.error')} ${error}`) | |
| } else { | |
| this.$message.error(this.$t('message.login.failed')) | |
| } | |
| }, | |
| onChangeServer (server) { | |
| const servers = this.$config.servers || [] | |
| const serverFilter = servers.filter(ser => (ser.apiHost || '') + ser.apiBase === server) | |
| this.server = serverFilter[0] || {} | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="less" scoped> | |
| .user-layout-login { | |
| min-width: 300px; | |
| width: 500px; | |
| margin: 0 auto; | |
| :deep(.tab-center .ant-tabs-tab) { | |
| margin: 0 16px 0 0; | |
| } | |
| .oauth-tab-icon--disabled { | |
| filter: grayscale(100%); | |
| opacity: 0.45; | |
| } | |
| .mobile & { | |
| max-width: 500px; | |
| width: 98%; | |
| } | |
| label { | |
| font-size: 14px; | |
| } | |
| button.login-button { | |
| margin-top: 8px; | |
| padding: 0 15px; | |
| font-size: 16px; | |
| height: 40px; | |
| width: 100%; | |
| } | |
| .user-login-other { | |
| text-align: left; | |
| margin-top: 24px; | |
| line-height: 22px; | |
| .item-icon { | |
| font-size: 24px; | |
| color: rgba(0, 0, 0, 0.2); | |
| margin-left: 16px; | |
| vertical-align: middle; | |
| cursor: pointer; | |
| transition: color 0.3s; | |
| &:hover { | |
| color: #1890ff; | |
| } | |
| } | |
| .register { | |
| float: right; | |
| } | |
| .g-btn-wrapper { | |
| background-color: rgb(221, 75, 57); | |
| height: 40px; | |
| width: 80px; | |
| } | |
| } | |
| .center { | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100px; | |
| } | |
| .content { | |
| margin: 10px auto; | |
| width: 300px; | |
| } | |
| .or { | |
| text-align: center; | |
| font-size: 16px; | |
| background: | |
| linear-gradient(#CCC 0 0) left, | |
| linear-gradient(#CCC 0 0) right; | |
| background-size: 40% 1px; | |
| background-repeat: no-repeat; | |
| } | |
| } | |
| </style> |