mirror of https://github.com/rancher/dashboard.git
247 lines
6.4 KiB
Vue
247 lines
6.4 KiB
Vue
<script>
|
|
import { findBy, removeObject } from '@/utils/array';
|
|
import { USERNAME } from '@/config/cookies';
|
|
import LabeledInput from '@/components/form/LabeledInput';
|
|
import AsyncButton from '@/components/AsyncButton';
|
|
import {
|
|
LOCAL, LOGGED_OUT, TIMED_OUT, _FLAGGED, SPA
|
|
} from '@/config/query-params';
|
|
import Checkbox from '@/components/form/Checkbox';
|
|
import { getVendor, getProduct } from '../../config/private-label';
|
|
import { sortBy } from '@/utils/sort';
|
|
import {configType} from '@/models/management.cattle.io.authconfig'
|
|
import {mapGetters} from 'vuex'
|
|
|
|
export default {
|
|
name: 'Login',
|
|
layout: 'unauthenticated',
|
|
components: {
|
|
LabeledInput, AsyncButton, Checkbox
|
|
},
|
|
|
|
async asyncData({ route, redirect, store }) {
|
|
const drivers = await store.dispatch('auth/getAuthProviders');
|
|
const providers = sortBy(drivers.map(x => x.id), ['id']);
|
|
|
|
|
|
const hasLocal = providers.includes('local');
|
|
const hasOthers = hasLocal && !!providers.find(x => x !== 'local');
|
|
|
|
if ( hasLocal ) {
|
|
// Local is special and handled here so that it can be toggled
|
|
removeObject(providers, 'local');
|
|
}
|
|
|
|
if ( !process.env.dev && !route.query[SPA] ) {
|
|
redirect(302, `${ store.getters['rancherLink'] }login`);
|
|
|
|
return;
|
|
}
|
|
|
|
return {
|
|
providers,
|
|
hasOthers,
|
|
hasLocal,
|
|
showLocal: !hasOthers || (route.query[LOCAL] === _FLAGGED),
|
|
};
|
|
},
|
|
|
|
data({ $cookies }) {
|
|
const username = $cookies.get(USERNAME, { parseJSON: false }) || '';
|
|
|
|
return {
|
|
vendor: getVendor(),
|
|
product: getProduct(),
|
|
|
|
username,
|
|
remember: !!username,
|
|
password: '',
|
|
|
|
timedOut: this.$route.query[TIMED_OUT] === _FLAGGED,
|
|
loggedOut: this.$route.query[LOGGED_OUT] === _FLAGGED,
|
|
err: this.$route.query.err,
|
|
|
|
providers: [],
|
|
providerComponents: [],
|
|
};
|
|
},
|
|
created() {
|
|
this.providerComponents = this.providers.map((name) => {
|
|
return () => import(/* webpackChunkName: "login" */ `@/components/auth/login/${ configType[name] }`)
|
|
});
|
|
},
|
|
|
|
mounted() {
|
|
this.$nextTick(() => {
|
|
this.focusSomething();
|
|
});
|
|
},
|
|
|
|
computed:{...mapGetters({t: 'i18n/t'})},
|
|
|
|
|
|
methods: {
|
|
displayName(provider){
|
|
return this.t(`model.authConfig.provider.${ provider }`);
|
|
},
|
|
|
|
toggleLocal() {
|
|
this.showLocal = true;
|
|
this.$router.applyQuery({ [LOCAL]: _FLAGGED });
|
|
this.$nextTick(() => {
|
|
this.focusSomething();
|
|
});
|
|
},
|
|
|
|
focusSomething() {
|
|
if ( !this.showLocal ) {
|
|
// One of the provider components will handle it
|
|
return;
|
|
}
|
|
|
|
let elem;
|
|
|
|
if ( this.username ) {
|
|
elem = this.$refs.password;
|
|
} else {
|
|
elem = this.$refs.username;
|
|
}
|
|
|
|
if ( elem?.focus ) {
|
|
elem.focus();
|
|
|
|
if ( elem.select ) {
|
|
elem.select();
|
|
}
|
|
}
|
|
},
|
|
|
|
async loginLocal(buttonCb) {
|
|
try {
|
|
this.err = null;
|
|
await this.$store.dispatch('auth/login', {
|
|
provider: 'local',
|
|
body: {
|
|
username: this.username,
|
|
password: this.password
|
|
}
|
|
});
|
|
if ( this.remember ) {
|
|
this.$cookies.set(USERNAME, this.username,{
|
|
encode: x => x,
|
|
maxAge: 86400 * 365,
|
|
secure: true,
|
|
path: '/',
|
|
});
|
|
|
|
} else {
|
|
this.$cookies.remove(USERNAME);
|
|
}
|
|
buttonCb(true);
|
|
this.$router.replace('/');
|
|
} catch (err) {
|
|
this.err = err;
|
|
buttonCb(false);
|
|
}
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<main class="login">
|
|
<div class="row mb-20">
|
|
<div class="col span-6">
|
|
<p class="text-center">
|
|
{{t('login.howdy')}}
|
|
</p>
|
|
<h1 class="text-center">
|
|
{{t('login.welcome', {vendor})}}
|
|
</h1>
|
|
<h4 v-if="err" class="text-error text-center">
|
|
{{t('login.error')}}
|
|
</h4>
|
|
<h4 v-else-if="loggedOut" class="text-success text-center">
|
|
{{t('login.loggedOut')}}
|
|
</h4>
|
|
<h4 v-else-if="timedOut" class="text-error text-center">
|
|
{{t('login.loginAgain')}}
|
|
</h4>
|
|
|
|
<div v-if="providers.length" class="mt-50 mb-50">
|
|
<component
|
|
v-for="(name, idx) in providers"
|
|
:is="providerComponents[idx]"
|
|
:key="name"
|
|
:focus-on-mount="(idx === 0 && !showLocal)"
|
|
:name='name'
|
|
/>
|
|
</div>
|
|
<template v-if='hasLocal'>
|
|
<form v-if="showLocal" class="mt-50">
|
|
<div class="span-6 offset-3">
|
|
<div class="mb-20">
|
|
<LabeledInput
|
|
ref="username"
|
|
v-model="username"
|
|
:label="t('login.username')"
|
|
autocomplete="username"
|
|
/>
|
|
</div>
|
|
<div class="">
|
|
<LabeledInput
|
|
ref="password"
|
|
v-model="password"
|
|
type="password"
|
|
:label="t('login.password')"
|
|
autocomplete="password"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="mt-20">
|
|
<div class="col span-12 text-center">
|
|
<AsyncButton
|
|
type="submit"
|
|
:action-label="t('login.loginWithLocal')"
|
|
:waiting-label="t('login.loggingIn')"
|
|
:success-label="t('login.loggedIn')"
|
|
:error-label="t('asyncButton.default.error')"
|
|
@click="loginLocal"
|
|
/>
|
|
<div class="mt-20">
|
|
<Checkbox v-model="remember" label="Remember Username" type="checkbox" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div v-if='hasLocal' class="mt-20 text-center">
|
|
<button type="button" class="btn bg-link" @click="toggleLocal">
|
|
{{ t('login.useLocal')}}
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="col span-6 landscape" />
|
|
</div>
|
|
</main>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.login {
|
|
overflow: hidden;
|
|
|
|
.row {
|
|
align-items: center;
|
|
}
|
|
|
|
.landscape {
|
|
background-image: url('~assets/images/pl/login-landscape.svg');
|
|
background-repeat: no-repeat;
|
|
background-size: cover;
|
|
background-position: center center;
|
|
height: 100vh;
|
|
}
|
|
}
|
|
</style>
|