Fakultas Ilmu Komputer UI

Commit 17fafa9c authored by Jordan Muhammad Andrianda's avatar Jordan Muhammad Andrianda
Browse files

Latihan8 ab 19.1-19.5

parent c4725b20
......@@ -2,4 +2,4 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
name = "accounts"
import sys
from accounts.models import ListUser, Token
from accounts.models import User, Token
class PasswordlessAuthenticationBackend(object):
def authenticate(self, request, uid):
print("uid", uid, file=sys.stderr)
if not Token.objects.filter(uid=uid).exists():
print("no token found", file=sys.stderr)
return None
token = Token.objects.get(uid=uid)
print("got token", file=sys.stderr)
def authenticate(self, uid):
try:
user = ListUser.objects.get(email=token.email)
print("got user", file=sys.stderr)
return user
except ListUser.DoesNotExist:
print("new user", file=sys.stderr)
return ListUser.objects.create(email=token.email)
token = Token.objects.get(uid=uid)
return User.objects.get(email=token.email)
except User.DoesNotExist:
return User.objects.create(email=token.email)
except Token.DoesNotExist:
return None
def get_user(self, email):
return ListUser.objects.get(email=email)
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None
\ No newline at end of file
......@@ -7,31 +7,72 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
dependencies = [("auth", "0011_update_proxy_permissions")]
operations = [
migrations.CreateModel(
name='Token',
name="Token",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('uid', models.CharField(max_length=255)),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("email", models.EmailField(max_length=254)),
("uid", models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name='ListUser',
name="ListUser",
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('email', models.EmailField(max_length=254, primary_key=True, serialize=False)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
),
),
(
"email",
models.EmailField(
max_length=254, primary_key=True, serialize=False
),
),
(
"groups",
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.Group",
verbose_name="groups",
),
),
(
"user_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.Permission",
verbose_name="user permissions",
),
),
],
options={
'abstract': False,
},
options={"abstract": False},
),
]
# Generated by Django 2.2.5 on 2019-11-20 14:04
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [("accounts", "0001_initial")]
operations = [
migrations.CreateModel(
name="User",
fields=[
(
"email",
models.EmailField(
max_length=254, primary_key=True, serialize=False
),
)
],
),
migrations.AlterField(
model_name="token",
name="uid",
field=models.CharField(default=uuid.uuid4, max_length=40),
),
migrations.DeleteModel(name="ListUser"),
]
from django.db import models
import uuid
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin
)
class Token(models.Model):
email = models.EmailField()
uid = models.CharField(max_length=255)
class ListUserManager(BaseUserManager):
def create_user(self, email):
ListUser.objects.create(email=email)
def create_superuser(self, email, password):
self.create_user(email)
class ListUser(AbstractBaseUser, PermissionsMixin):
class User(models.Model):
email = models.EmailField(primary_key=True)
USERNAME_FIELD = 'email'
#REQUIRED_FIELDS = ['email', 'height']
objects = ListUserManager()
@property
def is_staff(self):
return self.email == 'harry.percival@example.com'
REQUIRED_FIELDS = []
USERNAME_FIELD = "email"
is_anonymous = False
is_authenticated = True
@property
def is_active(self):
return True
class Token(models.Model):
email = models.EmailField()
uid = models.CharField(default=uuid.uuid4, max_length=40)
from django.test import TestCase
from django.contrib.auth import get_user_model
from accounts.authentication import PasswordlessAuthenticationBackend
from accounts.models import Token
User = get_user_model()
class AuthenticateTest(TestCase):
def test_returns_None_if_no_such_token(self):
result = PasswordlessAuthenticationBackend().authenticate("no-such-token")
self.assertIsNone(result)
def test_returns_new_user_with_correct_email_if_token_exists(self):
email = "edith@example.com"
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
new_user = User.objects.get(email=email)
self.assertEqual(user, new_user)
def test_returns_existing_user_with_correct_email_if_token_exists(self):
email = "edith@example.com"
existing_user = User.objects.create(email=email)
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
self.assertEqual(user, existing_user)
class GetUserTest(TestCase):
def test_gets_user_by_email(self):
User.objects.create(email="another@example.com")
desired_user = User.objects.create(email="edith@example.com")
found_user = PasswordlessAuthenticationBackend().get_user("edith@example.com")
self.assertEqual(found_user, desired_user)
def test_returns_None_if_no_user_with_that_email(self):
self.assertIsNone(
PasswordlessAuthenticationBackend().get_user("edith@example.com")
)
from django.test import TestCase
from unittest.mock import patch, call
from accounts.models import Token
import accounts.views
class SendLoginEmailViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.post(
"/accounts/send_login_email", data={"email": "edith@example.com"}
)
self.assertRedirects(response, "/")
def test_sends_mail_to_address_from_post(self):
self.send_mail_called = False
def fake_send_mail(subject, body, from_email, to_list):
self.send_mail_called = True
self.subject = subject
self.body = body
self.from_email = from_email
self.to_list = to_list
accounts.views.send_mail = fake_send_mail
self.client.post(
"/accounts/send_login_email", data={"email": "edith@example.com"}
)
self.assertTrue(self.send_mail_called)
self.assertEqual(self.subject, "Your login link for Superlists")
self.assertEqual(self.from_email, "noreply@superlists")
self.assertEqual(self.to_list, ["edith@example.com"])
@patch("accounts.views.send_mail")
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post(
"/accounts/send_login_email", data={"email": "edith@example.com"}
)
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, "Your login link for Superlists")
self.assertEqual(from_email, "noreply@superlists")
self.assertEqual(to_list, ["edith@example.com"])
# def test_adds_success_message(self):
# response = self.client.post('/accounts/send_login_email', data={
# 'email': 'edith@example.com'
# }, follow=True)
# message = list(response.context['messages'])[0]
# self.assertEqual(
# message.message,
# "Check your email, we've sent you a link you can use to log in."
# )
# self.assertEqual(message.tags, "success")
# @patch('accounts.views.messages')
# def test_adds_success_message_with_mocks(self, mock_messages):
# response = self.client.post('/accounts/send_login_email', data={
# 'email': 'edith@example.com'
# })
# expected = "Check your email, we've sent you a link you can use to log in."
# self.assertEqual(
# mock_messages.success.call_args,
# call(response.wsgi_request, expected),
# )
class LoginViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')
def test_creates_token_associated_with_email(self):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
self.assertEqual(token.email, 'edith@example.com')
@patch('accounts.views.send_mail')
def test_sends_link_to_login_using_token_uid(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
expected_url = f'http://testserver/accounts/login?token={token.uid}'
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertIn(expected_url, body)
@patch('accounts.views.auth')
def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.authenticate.call_args,
call(uid='abcd123')
)
@patch('accounts.views.auth')
def test_calls_auth_login_with_user_if_there_is_one(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.login.call_args,
call(response.wsgi_request, mock_auth.authenticate.return_value)
)
@patch('accounts.views.auth')
def test_does_not_login_if_user_is_not_authenticated(self, mock_auth):
mock_auth.authenticate.return_value = None
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(mock_auth.login.called, False)
......@@ -2,7 +2,7 @@ from django.conf.urls import url
from accounts import views
urlpatterns = [
url(r'^send_email$', views.send_login_email, name='send_login_email'),
url(r'^login$', views.login, name='login'),
url(r'^logout$', views.logout, name='logout'),
]
\ No newline at end of file
url(r"^send_login_email$", views.send_login_email, name="send_login_email"),
url(r"^login$", views.login, name="login"),
# url(r'^logout$', views.logout, name='logout'),
]
import uuid
import sys
from django.contrib.auth import authenticate
from django.contrib.auth import login as auth_login
from django.core.mail import send_mail
from django.shortcuts import redirect, render
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.shortcuts import redirect
from django.contrib import auth, messages
from django.urls import reverse
from accounts.models import Token
def send_login_email(request):
email = request.POST['email']
uid = str(uuid.uuid4())
Token.objects.create(email=email, uid=uid)
print('saving uid', uid, 'for email', email, file=sys.stderr)
url = request.build_absolute_uri(f'/accounts/login?uid={uid}')
print(url)
token = Token.objects.create(email=email)
url = request.build_absolute_uri(
reverse('login') + '?token=' + str(token.uid)
)
message_body = f'Use this link to log in:\n\n{url}'
send_mail(
'Your login link for Superlists',
f'Use this link to log in:\n\n{url}',
message_body,
'noreply@superlists',
[email],
[email]
)
return render(request, 'login_email_sent.html')
def login(request):
print('login view', file=sys.stderr)
uid = request.GET.get('uid')
user = authenticate(uid=uid)
if user is not None:
auth_login(request, user)
return redirect('/')
def logout(request):
auth_logout(request)
def login(request):
user = auth.authenticate(uid=request.GET.get('token'))
if user:
auth.login(request, user)
return redirect('/')
......@@ -6,8 +6,8 @@ import time
MAX_WAIT = 10
class FunctionalTest(StaticLiveServerTestCase):
class FunctionalTest(StaticLiveServerTestCase):
def setUp(self):
self.browser = webdriver.Chrome()
self.browser.implicitly_wait(3)
......@@ -24,7 +24,7 @@ class FunctionalTest(StaticLiveServerTestCase):
start_time = time.time()
while True:
try:
return fn()
return fn()
except (AssertionError, WebDriverException) as e:
if time.time() - start_time > MAX_WAIT:
raise e
......
from selenium.webdriver.common.keys import Keys
from .base import FunctionalTest
class LayoutAndStylingTest(FunctionalTest):
class LayoutAndStylingTest(FunctionalTest):
def test_layout_and_styling(self):
# Edith goes to the home page
self.browser.get(self.live_server_url)
......
......@@ -3,41 +3,44 @@ from selenium.webdriver.common.keys import Keys
from unittest import skip
class ItemValidationTest(FunctionalTest):
def test_cannot_add_empty_list_items(self):
# Edith goes to the home page and accidentally tries to submit
# an empty list item. She hits Enter on the empty input box
self.browser.get(self.live_server_url)
self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER)
self.browser.find_element_by_id("id_new_item").send_keys(Keys.ENTER)
# The home page refreshes, and there is an error message saying
# that list items cannot be blank
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_css_selector('.has-error').text,
"You can't have an empty list item"
))
self.wait_for(
lambda: self.assertEqual(
self.browser.find_element_by_css_selector(".has-error").text,
"You can't have an empty list item",
)
)
# She tries again with some text for the item, which now works
self.browser.find_element_by_id('id_new_item').send_keys('Buy milk')
self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER)
self.wait_for_row_in_list_table('1: Buy milk')
self.browser.find_element_by_id("id_new_item").send_keys("Buy milk")
self.browser.find_element_by_id("id_new_item").send_keys(Keys.ENTER)
self.wait_for_row_in_list_table("1: Buy milk")
# Perversely, she now decides to submit a second blank list item
self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER)
self.browser.find_element_by_id("id_new_item").send_keys(Keys.ENTER)
# She receives a similar warning on the list page
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_css_selector('.has-error').text,
"You can't have an empty list item"
))
self.wait_for(
lambda: self.assertEqual(
self.browser.find_element_by_css_selector(".has-error").text,
"You can't have an empty list item",
)
)
# And she can correct it by filling some text in
self.browser.find_element_by_id('id_new_item').send_keys('Make tea')
self.browser.find_element_by_id('id_new_item').send_keys(Keys.ENTER)
self.wait_for_row_in_list_table('1: Buy milk')
self.wait_for_row_in_list_table('2: Make tea')
self.browser.find_element_by_id("id_new_item").send_keys("Make tea")
self.browser.find_element_by_id("id_new_item").send_keys(Keys.ENTER)
self.wait_for_row_in_list_table("1: Buy milk")
self.wait_for_row_in_list_table("2: Make tea")
if __name__ == "__main__":
unittest.main(warnings="ignore")
......@@ -4,36 +4,36 @@ import re
from .base import FunctionalTest
TEST_EMAIL = 'jerysha99@gmail.com'
SUBJECT = 'Your login link for Superlists'
TEST_EMAIL = "jerysha99@gmail.com"
SUBJECT = "Your login link for Superlists"
class LoginTest(FunctionalTest):
def test_can_get_email_link_to_log_in(self):
# Edith goes to the awesome superlists site
# and notices a "Log in" section in the navbar for the first time
# It's telling her to enter her email address, so she does
self.browser.get(self.live_server_url)
self.browser.find_element_by_name('email').send_keys(TEST_EMAIL)
self.browser.find_element_by_name('email').send_keys(Keys.ENTER)
self.browser.find_element_by_name("email").send_keys(TEST_EMAIL)
self.browser.find_element_by_name("email").send_keys(Keys.ENTER)
# A message appears telling her an email has been sent
self.wait_for(lambda: self.assertIn(
'Check your email',
self.browser.find_element_by_tag_name('body').text
))
self.wait_for(
lambda: self.assertIn(
"Check your email", self.browser.find_element_by_tag_name("body").text
)
)
# She checks her email and finds a message
email = mail.outbox[0]
email = mail.outbox[0]
self.assertIn(TEST_EMAIL, email.to)
self.assertEqual(email.subject, SUBJECT)
# It has a url link in it
self.assertIn('Use this link to log in', email.body)
url_search = re.search(r'http://.+/.+$', email.body)
self.assertIn("Use this link to log in", email.body)
url_search = re.search(r"http://.+/.+$", email.body)
if not url_search:
self.fail(f'Could not find url in email body:\n{email.body}')
self.fail(f"Could not find url in email body:\n{email.body}")
url = url_search.group(0)
self.assertIn(self.live_server_url, url)
......@@ -41,8 +41,6 @@ class LoginTest(FunctionalTest):
self.browser.get(url)
# she is logged in!
self.wait_for(
lambda: self.browser.find_element_by_link_text('Log out')
)
navbar = self.browser.find_element_by_css_selector('.navbar')
self.assertIn(TEST_EMAIL, navbar.text)
\ No newline at end of file
self.wait_for(lambda: self.browser.find_element_by_link_text("Log out"))