dashboard/pages/auth/login.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>