Add loggedIn guard, add basic login component

This commit is contained in:
Christian Cueni 2019-10-02 16:11:15 +02:00
parent fb225b926d
commit 062269f030
11 changed files with 323 additions and 71 deletions

View File

@ -11,6 +11,7 @@
import SimpleLayout from '@/layouts/SimpleLayout';
import BlankLayout from '@/layouts/BlankLayout';
import FullScreenLayout from '@/layouts/FullScreenLayout';
import PublicLayout from '@/layouts/PublicLayout';
import Modal from '@/components/Modal';
import MobileNavigation from '@/components/MobileNavigation';
import NewContentBlockWizard from '@/components/content-block-form/NewContentBlockWizard';
@ -36,6 +37,7 @@
SimpleLayout,
BlankLayout,
FullScreenLayout,
PublicLayout,
Modal,
MobileNavigation,
NewContentBlockWizard,

View File

@ -4,9 +4,10 @@ import {ApolloClient} from 'apollo-client/index'
import {ApolloLink} from 'apollo-link'
import fetch from 'unfetch'
export default function (uri) {
const httpLink = new HttpLink({
// uri: process.env.NODE_ENV !== 'production' ? 'http://localhost:8000/api/graphql/' : '/api/graphql/',
uri: '/api/graphql/',
uri,
credentials: 'include',
fetch: fetch,
headers: {
@ -66,9 +67,10 @@ cache.readQuery = (...args) => {
};
// Create the apollo client
export default new ApolloClient({
return new ApolloClient({
link: composedLink,
// link: httpLink,
cache: cache,
connectToDevTools: true
})
}

View File

@ -0,0 +1,8 @@
mutation Login($input: LoginInput!) {
login(input: $input) {
success
errors {
field
}
}
}

View File

@ -0,0 +1,23 @@
<template>
<div class="container skillbox">
<logo></logo>
<router-view class="skillbox__content"></router-view>
<footer class="skillbox__footer">Footer</footer>
</div>
</template>
<script>
import Logo from '@/components/icons/Logo';
export default {
components: {Logo},
}
</script>
<style lang="scss" scoped>
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
@import "@/styles/_default-layout.scss";
</style>

View File

@ -3,7 +3,7 @@ import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import VueVimeoPlayer from 'vue-vimeo-player'
import apolloClient from './graphql/client'
import apolloClientFactory from './graphql/client'
import VueApollo from 'vue-apollo'
import App from './App'
import router from './router'
@ -63,8 +63,14 @@ if (process.env.GOOGLE_ANALYTICS_ID) {
Vue.directive('click-outside', clickOutside);
Vue.directive('auto-grow', autoGrow);
const publicApolloClient = apolloClientFactory('/api/graphql-public/');
const privateApolloClient = apolloClientFactory('/api/graphql/');
const apolloProvider = new VueApollo({
defaultClient: apolloClient
clients: {
publicClient: publicApolloClient
},
defaultClient: privateApolloClient
});
Validator.extend('required', required);
@ -98,6 +104,28 @@ Vue.use(VeeValidate, {
Vue.filter('date', dateFilter);
/* logged in guard */
const publicPages = ['login']
function getCookieValue(a) {
var b = document.cookie.match('(^|[^;]+)\\s*' + a + '\\s*=\\s*([^;]+)');
return b ? b.pop() : '';
}
function redirectIfLoginRequird(nameOfPage) {
return publicPages.indexOf(nameOfPage) === -1 && getCookieValue('loginStatus') !== 'True';
}
router.beforeEach((to, from, next) => {
if (redirectIfLoginRequird(to.name)) {
next('/login');
} else {
next();
}
// todo handle public pages for user
});
/* eslint-disable no-new */
new Vue({
el: '#app',

158
client/src/pages/login.vue Normal file
View File

@ -0,0 +1,158 @@
<template>
<div class="login">
<h1 class="login__title">Login</h1>
<form class="login__form login-form" novalidate @submit.prevent="validateBeforeSubmit">
<div class="login-form__field sbform-input">
<label for="email" class="sbform-input__label">E-Mail</label>
<input
id="email"
name="email"
type="text"
v-model="email"
v-validate="'required'"
:class="{ 'sbform-input__input--error': errors.has('email') }"
class="change-form__email skillbox-input sbform-input__input"
autocomplete="off"
data-cy="email"
/>
<small
v-if="errors.has('email') && submitted"
class="sbform-input__error"
data-cy="email-local-errors"
>{{ errors.first('email') }}</small>
<small
v-for="error in emailErrors"
:key="error"
class="sbform-input__error"
data-cy="email-remote-errors"
>{{ error }}</small>
</div>
<div class="change-form__field sbform-input">
<label for="pw" class="sbform-input__label">Passwort</label>
<input
id="pw"
name="password"
type="password"
v-model="password"
v-validate="'required'"
:class="{ 'sbform-input__input--error': errors.has('password') }"
class="change-form__new skillbox-input sbform-input__input"
autocomplete="off"
data-cy="password"
/>
<small
v-if="errors.has('password') && submitted"
class="sbform-input__error"
data-cy="password-local-errors"
>{{ errors.first('password') }}</small>
<small
v-for="error in passwordErrors"
:key="error"
class="sbform-input__error"
data-cy="password-remote-errors"
>{{ error }}</small>
</div>
<div class="login-error">
<small class="sbform-input__error" v-if="loginError">{{loginError}}</small>
</div>
<button class="button button--primary change-form__submit" data-cy="change-password-button">Anmelden</button>
</form>
</div>
</template>
<script>
import LOGIN_MUTATION from '@/graphql/gql/mutations/login.gql';
export default {
components: {},
methods: {
validateBeforeSubmit() {
this.$validator.validate().then(result => {
this.submitted = true;
let that = this;
if (result) {
this.$apollo.mutate({
client: 'publicClient',
mutation: LOGIN_MUTATION,
variables: {
input: {
usernameInput: this.email,
passwordInput: this.password
}
},
update(
store,
{
data: {
login: { success }
}
}
) {
try {
console.log('success', success)
if (success) {
} else {
that.loginError = 'Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.';
}
} catch (e) {
that.loginError = 'Es ist ein Fehler aufgetreten. Bitte versuchen Sie nochmals.';
}
}
});
}
});
},
resetForm() {
this.email = '';
this.password = '';
this.submitted = false;
this.$validator.reset();
}
},
data() {
return {
email: '',
password: '',
emailErrors: [],
passwordErrors: [],
loginError: ''
};
}
};
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_buttons.scss";
.sbform-input {
margin-bottom: 20px;
font-family: $sans-serif-font-family;
&__label {
margin-bottom: 10px;
display: inline-block;
}
&__input {
width: 100%;
&--error {
border-color: $color-error;
}
}
&__error {
margin-top: 10px;
color: $color-error;
display: inline-block;
}
&__hint {
margin-top: $small-spacing;
font-family: $sans-serif-font-family;
color: $color-silver-dark;
}
}
</style>

View File

@ -27,11 +27,23 @@ import newProject from '@/pages/newProject'
import surveyPage from '@/pages/survey'
import styleGuidePage from '@/pages/styleguide'
import moduleRoom from '@/pages/moduleRoom'
import login from '@/pages/login'
import store from '@/store/index';
const routes = [
{path: '/', component: start, meta: {layout: 'blank'}},
{
path: '/',
name: 'home',
component: start,
meta: {layout: 'blank'}
},
{
path: '/login',
name: 'login',
component: login,
meta: {layout: 'public'}
},
{
path: '/module/:slug',
component: moduleBase,
@ -118,6 +130,7 @@ const router = new Router({
return {x: 0, y: 0}
}
});
router.afterEach((to, from) => {
store.dispatch('showMobileNavigation', false);
});

View File

@ -75,3 +75,23 @@ class CommonRedirectMiddleware(MiddlewareMixin):
# or dummy image: return 'http://via.placeholder.com/{}'.format(m.group('dimensions'))
if '.png' in path or '.jpg' in path or '.svg' in path or 'not-found' in path:
return 'https://picsum.photos/400/400'
# https://stackoverflow.com/questions/4898408/how-to-set-a-login-cookie-in-django
class UserLoggedInCookieMiddleWare(MiddlewareMixin):
"""
Middleware to set user cookie
If user is authenticated and there is no cookie, set the cookie,
If the user is not authenticated and the cookie remains, delete it
"""
cookie_name = 'loginStatus'
def process_response(self, request, response):
#if user and no cookie, set cookie
if request.user.is_authenticated and not request.COOKIES.get(self.cookie_name):
response.set_cookie(self.cookie_name, 'true')
elif not request.user.is_authenticated and request.COOKIES.get(self.cookie_name):
#else if if no user and cookie remove user cookie, logout
response.delete_cookie(self.cookie_name)
return response

View File

@ -116,6 +116,7 @@ MIDDLEWARE += [
'core.middleware.ThreadLocalMiddleware',
'core.middleware.CommonRedirectMiddleware',
'core.middleware.UserLoggedInCookieMiddleWare',
]
ROOT_URLCONF = 'core.urls'

View File

@ -1,6 +1,5 @@
import requests
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, \
PasswordResetCompleteView
@ -16,7 +15,6 @@ class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
pass
@login_required
@ensure_csrf_cookie
def home(request):
if settings.DEBUG:

View File

@ -15,7 +15,6 @@ from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.views import PasswordResetView, PasswordResetConfirmView, INTERNAL_RESET_URL_TOKEN
from graphene import relay
from core import settings
from users.models import User