Commit aec71cc5 authored by den's avatar den
Browse files

Merge branch 'feature/first-login' into 'master'

First login backend

Closes #2

See merge request !2
parents 3c8ab170 a6293440
Pipeline #4262 failed with stage
in 6 minutes and 8 seconds
from django.core import exceptions
from django.shortcuts import redirect
from social_core.pipeline.partial import partial
from . import models
def create_profile(backend, user, response, *args, **kwargs):
@partial
def set_display_name(strategy, backend, is_new=False, *args, **kwargs):
if backend.name == "authsch" and is_new:
display_name = strategy.session_get("displayName", None)
if not display_name:
return redirect("/onboarding")
def create_profile(strategy, backend, request, details, user, *args, **kwargs):
if backend.name == "authsch":
try:
user.profile
except exceptions.ObjectDoesNotExist:
models.Profile.objects.create(user=user)
display_name = strategy.session_get("displayName", None)
models.Profile.objects.create(user=user, display_name=display_name)
# Generated by Django 2.2.9 on 2020-02-09 18:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0004_auto_20190315_1033'),
]
operations = [
migrations.AddField(
model_name='profile',
name='display_name',
field=models.CharField(default='', max_length=50),
preserve_default=False,
),
]
# Generated by Django 2.2.12 on 2020-05-02 15:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0005_profile_display_name'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='display_name',
field=models.CharField(max_length=50, unique=True),
),
]
# Generated by Django 2.2.12 on 2020-05-02 17:00
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0006_auto_20200502_1554'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='display_name',
field=models.CharField(error_messages={'unique': 'Foglalt.'}, max_length=50, unique=True, validators=[django.core.validators.MinLengthValidator(2)]),
),
]
# Generated by Django 2.2.12 on 2020-05-10 07:30
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0007_auto_20200502_1700'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='display_name',
field=models.CharField(error_messages={'unique': 'Foglalt.'}, max_length=50, unique=True, validators=[django.core.validators.MinLengthValidator(3)]),
),
]
from django.contrib.auth.models import User
from django.db import models
from soft_delete_it.models import SoftDeleteModel
from django.core.validators import MinLengthValidator
class Profile(SoftDeleteModel):
......@@ -9,13 +10,19 @@ class Profile(SoftDeleteModel):
about_me = models.TextField(blank=True)
is_score_visible = models.BooleanField(default=False)
ranked = models.BooleanField(default=False)
display_name = models.CharField(
max_length=50,
unique=True,
validators=[MinLengthValidator(3)],
error_messages={"unique": "Foglalt."},
)
@property
def full_name(self):
return self.user.get_full_name()
def __str__(self):
return self.full_name
return self.display_name
def delete(self, using=None):
self.user.is_active = False
......
......@@ -120,3 +120,9 @@ class AvatarSerializer(serializers.ModelSerializer):
class Meta:
model = models.Profile
fields = ("avatar",)
class DisplayNameSerializer(serializers.ModelSerializer):
class Meta:
model = models.Profile
fields = ("display_name",)
from django.contrib.auth.models import User
from django.urls import reverse
from faker import Faker
from rest_framework import status
from rest_framework.test import APITestCase
from .models import Profile
from .serializers import ProfileSerializer
fake = Faker()
fake_email = "fake@fake.com"
class AccountsTests(APITestCase):
def setUp(self):
# Users
User.objects.create(
username="admin", is_superuser=True, is_staff=True
).set_password("adminpw")
User.objects.create(
username="mod", is_superuser=False, is_staff=True
).set_password("modpw")
User.objects.create(
username="user", is_superuser=False, is_staff=False
).set_password("userpw")
User.objects.create(
username="other_user", is_superuser=False, is_staff=False
).set_password("userpw")
# Profiles
for user in User.objects.all():
Profile.objects.create(
user=user,
about_me=fake.paragraph(nb_sentences=4),
is_score_visible=True,
ranked=True,
)
# -------------------------------------------------------------------------
# LIST view
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# GET
# -------------------------------------------------------------------------
def test_get_list_admin(self):
self.client.force_login(User.objects.get(username="admin"))
url = reverse("profile-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), User.objects.all().count())
self.client.logout()
def test_get_list_mod(self):
self.client.force_login(User.objects.get(username="mod"))
url = reverse("profile-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), User.objects.all().count())
self.client.logout()
def test_get_list_user(self):
self.client.force_login(User.objects.get(username="user"))
url = reverse("profile-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), User.objects.all().count())
self.client.logout()
def test_get_list_no_login(self):
url = reverse("profile-list")
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
# -------------------------------------------------------------------------
# SINGLE view
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# GET
# -------------------------------------------------------------------------
def test_get_single_random_admin(self):
self.client.force_login(User.objects.get(username="admin"))
get_idx = Profile.objects.order_by("?").first().pk
url = "".join([reverse("profile-list"), str(get_idx), "/"])
response = self.client.get(url)
profile = Profile.objects.get(pk=get_idx)
serializer = ProfileSerializer(profile)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, serializer.data)
self.client.logout()
def test_get_single_random_mod(self):
self.client.force_login(User.objects.get(username="mod"))
get_idx = Profile.objects.order_by("?").first().pk
url = "".join([reverse("profile-list"), str(get_idx), "/"])
response = self.client.get(url)
profile = Profile.objects.get(pk=get_idx)
serializer = ProfileSerializer(profile)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, serializer.data)
self.client.logout()
def test_get_single_own_user(self):
self.client.force_login(User.objects.get(username="user"))
get_idx = User.objects.get(username="user").pk
url = "".join([reverse("profile-list"), str(get_idx), "/"])
response = self.client.get(url)
profile = Profile.objects.get(pk=get_idx)
serializer = ProfileSerializer(profile)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, serializer.data)
self.client.logout()
def test_get_single_not_own_user(self):
self.client.force_login(User.objects.get(username="user"))
get_idx = User.objects.get(username="other_user").pk
url = "".join([reverse("profile-list"), str(get_idx), "/"])
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.client.logout()
def test_get_single_non_exist_admin(self):
self.client.force_login(User.objects.get(username="admin"))
get_idx = Profile.objects.all().count() + 1
url = "".join([reverse("profile-list"), str(get_idx), "/"])
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.client.logout()
# -------------------------------------------------------------------------
# PUT
# -------------------------------------------------------------------------
def test_put_single_admin(self):
admin_user = User.objects.get(username="admin")
self.client.force_login(admin_user)
put_idx = User.objects.get(username="user").pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
data = {
"id": put_idx,
"user": {
"username": User.objects.get(pk=put_idx).username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": False,
"is_active": True,
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
# test if changes took place in the database
changed_user = User.objects.get(pk=put_idx)
changed_profile = Profile.objects.get(pk=put_idx)
self.assertEqual(changed_user.email, data["user"]["email"])
self.assertEqual(changed_user.first_name, data["user"]["first_name"])
self.assertEqual(changed_user.last_name, data["user"]["last_name"])
self.assertEqual(changed_user.is_staff, data["user"]["is_staff"])
self.assertEqual(changed_user.is_active, data["user"]["is_active"])
self.assertEqual(changed_profile.about_me, data["about_me"])
self.assertEqual(changed_profile.is_score_visible, data["is_score_visible"])
self.assertEqual(changed_profile.ranked, data["ranked"])
self.client.logout()
def test_put_single_mod(self):
self.client.force_login(User.objects.get(username="mod"))
put_idx = User.objects.get(username="user").pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
data = {
"id": put_idx,
"user": {
"username": User.objects.get(pk=put_idx).username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": False,
"is_active": True,
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.client.logout()
def test_put_single_user_other(self):
user = User.objects.get(username="user")
self.client.force_login(user)
other_user = User.objects.get(username="other_user")
put_idx = other_user.pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
data = {
"id": put_idx,
"user": {
"username": other_user.username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": other_user.is_staff,
"is_active": other_user.is_active,
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN)
self.client.logout()
def test_put_single_user_own_valid(self):
user = User.objects.get(username="user")
self.client.force_login(user)
put_idx = user.pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
data = {
"id": put_idx,
"user": {
"username": user.username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": user.is_staff,
"is_active": user.is_active,
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
self.assertEquals(response.status_code, status.HTTP_200_OK)
# test if changes took place in the database
changed_user = User.objects.get(pk=put_idx)
changed_profile = Profile.objects.get(pk=put_idx)
self.assertEqual(changed_user.email, data["user"]["email"])
self.assertEqual(changed_user.first_name, data["user"]["first_name"])
self.assertEqual(changed_user.last_name, data["user"]["last_name"])
self.assertEqual(changed_user.is_staff, data["user"]["is_staff"])
self.assertEqual(changed_user.is_active, data["user"]["is_active"])
self.assertEqual(changed_profile.about_me, data["about_me"])
self.assertEqual(changed_profile.is_score_visible, data["is_score_visible"])
self.assertEqual(changed_profile.ranked, data["ranked"])
self.client.logout()
def test_user_change_own_is_active(self):
user = User.objects.get(username="user")
self.client.force_login(user)
put_idx = user.pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
data = {
"id": put_idx,
"user": {
"username": user.username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": user.is_staff,
"is_active": not user.is_active,
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(
user.is_active, User.objects.get(username=user.username).is_active
class post(APITestCase):
def test_display_name(self):
User.objects.create(username="asd")
Profile.objects.create(
user=User.objects.create(username="bsd"), display_name="bsd"
)
self.client.logout()
self.client.force_login(User.objects.get(username="asd"))
def test_user_change_own_is_staff(self):
user = User.objects.get(username="user")
self.client.force_login(user)
put_idx = user.pk
url = "".join([reverse("profile-list"), str(put_idx), "/"])
# ok
response = self.client.post(
"/api/v1/auth/display-name/", {"display_name": "asd"}
)
self.assertEquals(response.status_code, status.HTTP_302_FOUND)
data = {
"id": put_idx,
"user": {
"username": user.username,
"email": fake_email,
"first_name": fake.text(max_nb_chars=20),
"last_name": fake.text(max_nb_chars=20),
"is_staff": not user.is_staff,
"is_active": user.is_active,
# taken
response = self.client.post(
"/api/v1/auth/display-name/", {"display_name": "bsd"}
)
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(response.data, {"display_name": ["Foglalt."]})
# too long
response = self.client.post(
"/api/v1/auth/display-name/",
{
"display_name": "asdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd"
},
"about_me": fake.text(max_nb_chars=50),
"is_score_visible": True,
"ranked": True,
}
response = self.client.put(url, data, format="json")
)
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEquals(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(
user.is_staff, User.objects.get(username=user.username).is_staff
# too short
response = self.client.post(
"/api/v1/auth/display-name/", {"display_name": "as"}
)
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
# blank
response = self.client.post("/api/v1/auth/display-name/", {"display_name": ""})
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
self.client.logout()
# random
response = self.client.post("/api/v1/auth/display-name/", {"kek": "xd"})
self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
......@@ -9,5 +9,6 @@ router.register(r"accounts", views.ProfileViewSet)
router.register(r"avatar", views.AvatarViewSet)
urlpatterns = router.urls + [
path("logout/", LogoutView.as_view(next_page="/"), name="logout")
path("logout/", LogoutView.as_view(next_page="/"), name="logout"),
path("auth/display-name/", views.set_display_name),
]
......@@ -3,6 +3,9 @@ from rest_framework import generics
from rest_framework import permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import redirect
from django.contrib.auth import logout
from rest_framework.decorators import api_view
from common.mixins import RelativeURLFieldMixin
from .permissions import IsSafeMethodOrIsOwnOrIsAdmin
......@@ -10,6 +13,25 @@ from . import models
from . import serializers
@api_view(["POST"])
def set_display_name(request):
# If user calls this endpoint outside auth pipeline, 401
if len(request.session.values()) == 0:
return Response(status=401)
# Interrupt auth flow from browser
if "cancel" in request.data:
logout(request)
return Response(status=204)
serializer = serializers.DisplayNameSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
request.session["displayName"] = request.data["display_name"]
return redirect("/api/v1/complete/authsch")
class ProfileViewSet(
RelativeURLFieldMixin,
generics.ListAPIView,
......
......@@ -54,19 +54,25 @@ class Command(BaseCommand):
admin.last_name = "Adminsson"
admin.set_password("admin")
admin.save()
Profile.objects.create(user=User.objects.get(username="admin"))
Profile.objects.create(
user=User.o