Validate license on login

This commit is contained in:
Christian Cueni 2019-10-23 16:08:34 +02:00
parent bceb01c5f4
commit b20917d29a
5 changed files with 71 additions and 17 deletions

View File

@ -14,7 +14,7 @@ describe('The Login Page', () => {
cy.visit('/'); cy.visit('/');
cy.login(username, password); cy.login(username, password);
cy.get('[data-cy=email-local-errors]').contains('ist ein Pflichtfeld'); cy.get('[data-cy=email-local-errors]').contains('E-Mail ist ein Pflichtfeld');
}); });
it('user sees error message if password is omitted', () => { it('user sees error message if password is omitted', () => {
@ -23,7 +23,7 @@ describe('The Login Page', () => {
cy.visit('/'); cy.visit('/');
cy.login(username, password); cy.login(username, password);
cy.get('[data-cy=password-local-errors]').contains('ist ein Pflichtfeld'); cy.get('[data-cy=password-local-errors]').contains('Passwort ist ein Pflichtfeld');
}); });
it('user sees error message if credentials are invalid', () => { it('user sees error message if credentials are invalid', () => {

View File

@ -10,6 +10,7 @@
type="text" type="text"
v-model="email" v-model="email"
v-validate="'required'" v-validate="'required'"
data-vv-as="E-Mail"
:class="{ 'skillboxform-input__input--error': errors.has('email') }" :class="{ 'skillboxform-input__input--error': errors.has('email') }"
class="change-form__email skillbox-input skillboxform-input__input" class="change-form__email skillbox-input skillboxform-input__input"
autocomplete="off" autocomplete="off"
@ -33,6 +34,7 @@
id="pw" id="pw"
name="password" name="password"
type="password" type="password"
data-vv-as="Passwort"
v-model="password" v-model="password"
v-validate="'required'" v-validate="'required'"
:class="{ 'skillboxform-input__input--error': errors.has('password') }" :class="{ 'skillboxform-input__input--error': errors.has('password') }"
@ -93,16 +95,24 @@ export default {
store, store,
{ {
data: { data: {
login: { success } login
} }
} }
) { ) {
try { try {
if (success) { if (login.success) {
const redirectUrl = that.$route.query.redirect ? that.$route.query.redirect : '/' const redirectUrl = that.$route.query.redirect ? that.$route.query.redirect : '/'
that.$router.push(redirectUrl); that.$router.push(redirectUrl);
} else { } else {
that.loginError = 'Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.'; const firstError = login.errors[0];
switch (firstError.field) {
case 'invalid_credentials':
that.loginError = 'Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.';
break;
case 'license_inactive':
that.loginError = 'Ihre Lizenz ist nicht mehr aktiv.';
break;
}
} }
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);

View File

@ -14,7 +14,15 @@ from core.views import SetPasswordView
from registration.models import License from registration.models import License
from registration.serializers import RegistrationSerializer from registration.serializers import RegistrationSerializer
from users.models import User, Role, UserRole, SchoolClass from users.models import User, Role, UserRole, SchoolClass
from users.mutations_public import MutationError, PublicFieldError
class PublicFieldError(graphene.ObjectType):
code = graphene.String()
class MutationError(graphene.ObjectType):
field = graphene.String()
errors = graphene.List(PublicFieldError)
class Registration(relay.ClientIDMutation): class Registration(relay.ClientIDMutation):

View File

@ -12,14 +12,11 @@ import graphene
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from graphene import relay from graphene import relay
from registration.models import License
class PublicFieldError(graphene.ObjectType):
code = graphene.String()
class MutationError(graphene.ObjectType): class LoginError(graphene.ObjectType):
field = graphene.String() field = graphene.String()
errors = graphene.List(PublicFieldError)
class Login(relay.ClientIDMutation): class Login(relay.ClientIDMutation):
@ -28,17 +25,30 @@ class Login(relay.ClientIDMutation):
password_input = graphene.String() password_input = graphene.String()
success = graphene.Boolean() success = graphene.Boolean()
errors = graphene.List(MutationError) # todo: change for consistency errors = graphene.List(LoginError) # todo: change for consistency
@classmethod @classmethod
def mutate_and_get_payload(cls, root, info, **kwargs): def mutate_and_get_payload(cls, root, info, **kwargs):
user = authenticate(username=kwargs.get('username_input'), password=kwargs.get('password_input')) user = authenticate(username=kwargs.get('username_input'), password=kwargs.get('password_input'))
if user is not None: if user is None:
login(info.context, user) error = LoginError(field='invalid_credentials')
return cls(success=True, errors=[]) return cls(success=False, errors=[error])
else:
return cls(success=False, errors=['invalid_credentials']) user_license = None
try:
user_license = License.objects.get(licensee=user)
except License.DoesNotExist:
# current users have no license, allow them to login
pass
if user_license is not None and not user_license.license_type.active:
error = LoginError(field='license_inactive')
return cls(success=False, errors=[error])
login(info.context, user)
return cls(success=True, errors=[])
class UserMutations: class UserMutations:

View File

@ -13,11 +13,15 @@ from graphene.test import Client
from api.schema_public import schema from api.schema_public import schema
from core.factories import UserFactory from core.factories import UserFactory
from registration.factories import LicenseTypeFactory, LicenseFactory
from users.models import Role
class PasswordResetTests(TestCase): class PasswordResetTests(TestCase):
def setUp(self): def setUp(self):
self.user = UserFactory(username='aschi@iterativ.ch', email='aschi@iterativ.ch') self.user = UserFactory(username='aschi@iterativ.ch', email='aschi@iterativ.ch')
self.teacher_role = Role.objects.create(key=Role.objects.TEACHER_KEY, name="Teacher Role")
self.teacher_license_type = LicenseTypeFactory(for_role=self.teacher_role)
request = RequestFactory().post('/') request = RequestFactory().post('/')
@ -62,3 +66,25 @@ class PasswordResetTests(TestCase):
result = self.make_login_mutation(self.user.email, 'test1234') result = self.make_login_mutation(self.user.email, 'test1234')
self.assertFalse(result.get('data').get('login').get('success')) self.assertFalse(result.get('data').get('login').get('success'))
def test_user_with_active_license_can_login(self):
password = 'test123'
self.user.set_password(password)
self.user.save()
LicenseFactory(license_type=self.teacher_license_type, licensee=self.user)
result = self.make_login_mutation(self.user.email, password)
self.assertTrue(result.get('data').get('login').get('success'))
def test_user_with_inactive_license_cannot_login(self):
password = 'test123'
self.user.set_password(password)
self.user.save()
self.teacher_license_type.active = False
self.teacher_license_type.save()
LicenseFactory(license_type=self.teacher_license_type, licensee=self.user)
result = self.make_login_mutation(self.user.email, password)
self.assertFalse(result.get('data').get('login').get('success'))