mirror of https://github.com/Lissy93/dashy.git
✨ Re: #64 - Implements a 'Remember Me' dropdown in the Login form
This commit is contained in:
parent
11e0abf4b8
commit
ebcd8c584f
|
@ -1,8 +1,18 @@
|
||||||
import sha256 from 'crypto-js/sha256';
|
import sha256 from 'crypto-js/sha256';
|
||||||
import { cookieKeys, localStorageKeys } from './defaults';
|
import { cookieKeys, localStorageKeys } from './defaults';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a 1-way hash, in order to be stored in local storage for authentication
|
||||||
|
* @param {String} user The username of user
|
||||||
|
* @returns {String} The hashed token
|
||||||
|
*/
|
||||||
const generateUserToken = (user) => sha256(user.toString()).toString().toLowerCase();
|
const generateUserToken = (user) => sha256(user.toString()).toString().toLowerCase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user is currently authenticated
|
||||||
|
* @param {Array[Object]} users An array of user objects pulled from the config
|
||||||
|
* @returns {Boolean} Will return true if the user is logged in, else false
|
||||||
|
*/
|
||||||
export const isLoggedIn = (users) => {
|
export const isLoggedIn = (users) => {
|
||||||
const validTokens = users.map((user) => generateUserToken(user));
|
const validTokens = users.map((user) => generateUserToken(user));
|
||||||
let userAuthenticated = false;
|
let userAuthenticated = false;
|
||||||
|
@ -20,6 +30,15 @@ export const isLoggedIn = (users) => {
|
||||||
return userAuthenticated;
|
return userAuthenticated;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks credentials entered by the user against those in the config
|
||||||
|
* Returns an object containing a boolean indicating success/ failure
|
||||||
|
* along with a message outlining what's not right
|
||||||
|
* @param {String} username The username entered by the user
|
||||||
|
* @param {String} pass The password entered by the user
|
||||||
|
* @param {String[]} users An array of valid user objects
|
||||||
|
* @returns {Object} An object containing a boolean result and a message
|
||||||
|
*/
|
||||||
export const checkCredentials = (username, pass, users) => {
|
export const checkCredentials = (username, pass, users) => {
|
||||||
let response;
|
let response;
|
||||||
if (!username) {
|
if (!username) {
|
||||||
|
@ -40,12 +59,23 @@ export const checkCredentials = (username, pass, users) => {
|
||||||
return response || { correct: false, msg: 'User not found' };
|
return response || { correct: false, msg: 'User not found' };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const login = (username, pass) => {
|
/**
|
||||||
|
* Sets the cookie value in order to login the user locally
|
||||||
|
* @param {String} username - The users username
|
||||||
|
* @param {String} pass - Password, not yet hashed
|
||||||
|
* @param {Number} timeout - A desired timeout for the session, in ms
|
||||||
|
*/
|
||||||
|
export const login = (username, pass, timeout) => {
|
||||||
|
const now = new Date();
|
||||||
|
const expiry = new Date(now.setTime(now.getTime() + timeout)).toGMTString();
|
||||||
const userObject = { user: username, hash: sha256(pass).toString().toLowerCase() };
|
const userObject = { user: username, hash: sha256(pass).toString().toLowerCase() };
|
||||||
document.cookie = `authenticationToken=${generateUserToken(userObject)}; max-age=600`;
|
document.cookie = `authenticationToken=${generateUserToken(userObject)}; expires=${expiry}`;
|
||||||
localStorage.setItem(localStorageKeys.USERNAME, username);
|
localStorage.setItem(localStorageKeys.USERNAME, username);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removed the browsers cookie, causing user to be logged out
|
||||||
|
*/
|
||||||
export const logout = () => {
|
export const logout = () => {
|
||||||
document.cookie = 'authenticationToken=null';
|
document.cookie = 'authenticationToken=null';
|
||||||
localStorage.removeItem(localStorageKeys.USERNAME);
|
localStorage.removeItem(localStorageKeys.USERNAME);
|
||||||
|
@ -57,8 +87,8 @@ export const logout = () => {
|
||||||
* But if auth is configured, then will verify user is correctly
|
* But if auth is configured, then will verify user is correctly
|
||||||
* logged in and then check weather they are of type admin, and
|
* logged in and then check weather they are of type admin, and
|
||||||
* return false if any conditions fail
|
* return false if any conditions fail
|
||||||
* @param users[] : Array of users
|
* @param {String[]} - Array of users
|
||||||
* @returns Boolean : True if admin privileges
|
* @returns {Boolean} - True if admin privileges
|
||||||
*/
|
*/
|
||||||
export const isUserAdmin = (users) => {
|
export const isUserAdmin = (users) => {
|
||||||
if (!users || users.length === 0) return true; // Authentication not setup
|
if (!users || users.length === 0) return true; // Authentication not setup
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
<h2 class="login-title">Dashy</h2>
|
<h2 class="login-title">Dashy</h2>
|
||||||
<Input v-model="username" label="Username" class="login-field username" type="text" />
|
<Input v-model="username" label="Username" class="login-field username" type="text" />
|
||||||
<Input v-model="password" label="Password" class="login-field password" type="password" />
|
<Input v-model="password" label="Password" class="login-field password" type="password" />
|
||||||
|
<label>Remember me for</label>
|
||||||
|
<v-select
|
||||||
|
v-model="timeout"
|
||||||
|
:options="dropDownMenu"
|
||||||
|
label="label"
|
||||||
|
:selectOnTab="true"
|
||||||
|
class="login-time-dropdown"
|
||||||
|
/>
|
||||||
<Button class="login-button" :click="submitLogin">Login</Button>
|
<Button class="login-button" :click="submitLogin">Login</Button>
|
||||||
<transition name="bounce">
|
<transition name="bounce">
|
||||||
<p :class="`login-error-message ${status}`" v-show="message">{{ message }}</p>
|
<p :class="`login-error-message ${status}`" v-show="message">{{ message }}</p>
|
||||||
|
@ -30,6 +38,13 @@ export default {
|
||||||
password: '',
|
password: '',
|
||||||
message: '',
|
message: '',
|
||||||
status: 'waiting', // wating, error, success
|
status: 'waiting', // wating, error, success
|
||||||
|
timeout: { label: 'Never', time: 0 },
|
||||||
|
dropDownMenu: [ // Data for timeout dropdown menu, label + value
|
||||||
|
{ label: 'Never', time: 0 }, // Time is specified in ms
|
||||||
|
{ label: '4 Hours', time: 14400 * 1000 },
|
||||||
|
{ label: '1 Day', time: 86400 * 1000 },
|
||||||
|
{ label: '1 Week', time: 604800 * 1000 },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -38,11 +53,12 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submitLogin() {
|
submitLogin() {
|
||||||
|
const timeout = this.timeout.time || 0;
|
||||||
const response = checkCredentials(this.username, this.password, this.appConfig.auth || []);
|
const response = checkCredentials(this.username, this.password, this.appConfig.auth || []);
|
||||||
this.message = response.msg; // Show error or success message to the user
|
this.message = response.msg; // Show error or success message to the user
|
||||||
this.status = response.correct ? 'success' : 'error';
|
this.status = response.correct ? 'success' : 'error';
|
||||||
if (response.correct) { // Yay, credentials were correct :)
|
if (response.correct) { // Yay, credentials were correct :)
|
||||||
login(this.username, this.password); // Login, to set the cookie
|
login(this.username, this.password, timeout); // Login, to set the cookie
|
||||||
setTimeout(() => { // Wait a short while, then redirect back home
|
setTimeout(() => { // Wait a short while, then redirect back home
|
||||||
router.push({ path: '/' });
|
router.push({ path: '/' });
|
||||||
}, 250);
|
}, 250);
|
||||||
|
@ -130,4 +146,30 @@ export default {
|
||||||
100% { transform: scale(1); }
|
100% { transform: scale(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.v-select.login-time-dropdown {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
.vs__dropdown-toggle {
|
||||||
|
border-color: var(--login-form-color);
|
||||||
|
background: var(--login-form-background);
|
||||||
|
span.vs__selected {
|
||||||
|
color: var(--login-form-color);
|
||||||
|
}
|
||||||
|
.vs__actions svg path { fill: var(--login-form-color); }
|
||||||
|
}
|
||||||
|
ul.vs__dropdown-menu {
|
||||||
|
background: var(--login-form-background);
|
||||||
|
border-color: var(--login-form-color);
|
||||||
|
li {
|
||||||
|
color: var(--login-form-color);
|
||||||
|
&:hover {
|
||||||
|
color: var(--login-form-background);
|
||||||
|
background: var(--login-form-color);
|
||||||
|
}
|
||||||
|
&.vs__dropdown-option--highlight {
|
||||||
|
color: var(--login-form-background) !important;
|
||||||
|
background: var(--login-form-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue