Fakultas Ilmu Komputer UI

Commit 327749fd authored by Dwi Nanda Susanto's avatar Dwi Nanda Susanto
Browse files

Passwordless spike

parent 24264351
......@@ -27,4 +27,10 @@ Refactor ditujukan untuk melakukan penyesuaian antara fungsi yang ingin di test
# Exercise 6 - Mutant
Dalam exercise kali ini saya membuat mutant yang bertujuan untuk membuat functional_test menjadi fail. Mutant yang saya buat merupakan kondisi yang salah dari apa yang saya inginkan, yaitu item yang <= 1 dianggap `waktunya berlibur`, kemudian yang > 1 dianggap `oh tidak`, kondisi ini akan membuat functional test yang dibuat me-return error
\ No newline at end of file
Dalam exercise kali ini saya membuat mutant yang bertujuan untuk membuat functional_test menjadi fail. Mutant yang saya buat merupakan kondisi yang salah dari apa yang saya inginkan, yaitu item yang <= 1 dianggap `waktunya berlibur`, kemudian yang > 1 dianggap `oh tidak`, kondisi ini akan membuat functional test yang dibuat me-return error
# Exercise 7
Dalam exercise kali ini mempelajari tentang ```Spiking```. ```Spiking``` adalah sebuah aktivitas explore program ketika membuat sebuah fitur tanpa membuat TDD functional dan unittest. Pada exercise kali ini spiking diterapkan ketika membuat fitur *auth* tanpa tanpa menggunakan passworrd.
Sedangkan De-```Spiking``` adalah menghapus keseluruhan implementasi spiking lalu memulai membuat implementasi fitur barusan, dari awal lagi hanya saja lengkap dengan TDD nya.
\ No newline at end of file
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
import sys
from accounts.models import ListUser, Token
class PasswordlessAuthenticationBackend(object):
def authenticate(self, 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)
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)
def get_user(self, email):
return ListUser.objects.get(email=email)
\ No newline at end of file
# Generated by Django 2.2.5 on 2019-11-14 01:39
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
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(default=uuid.uuid4, max_length=40)),
],
),
migrations.CreateModel(
name='User',
fields=[
('email', models.EmailField(max_length=254, primary_key=True, serialize=False)),
],
),
migrations.CreateModel(
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')),
],
options={
'abstract': False,
},
),
]
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(default=uuid.uuid4, max_length=40)
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):
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'
@property
def is_active(self):
return True
class User(models.Model):
email = models.EmailField(primary_key=True)
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
is_anonymous = False
is_authenticated = True
\ No newline at end of file
<html>
<h1>Email sent</h1>
<p>Check your email, you'll find a message with a link that will log you into
the site.</p>
</html>
\ No newline at end of file
from django.test import TestCase
from django.contrib.auth import get_user_model
from accounts.models import Token
User = get_user_model()
class TokenModelTest(TestCase):
def test_links_user_with_auto_generated_uid(self):
token1 = Token.objects.create(email='a@b.com')
token2 = Token.objects.create(email='a@b.com')
self.assertNotEqual(token1.uid, token2.uid)
class UserModelTest(TestCase):
def test_email_is_primary_key(self):
user = User(email='a@b.com')
self.assertEqual(user.pk, 'a@b.com')
\ No newline at end of file
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
import uuid
import sys
from accounts.models import Token
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
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}')
send_mail(
'Your login link for Superlists',
f'Use this link to log in:\n\n{url}',
'noreply@superlists',
[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)
return redirect('/')
\ No newline at end of file
No preview for this file type
from django.core import mail
from selenium.webdriver.common.keys import Keys
import re
from .base import FunctionalTest
TEST_EMAIL = 'edith@example.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)
# 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
))
# She checks her email and finds a message
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)
if not url_search:
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)
# she clicks it
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)
......@@ -7,24 +7,38 @@
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/base.css" rel="stylesheet">
</head>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1>
<form method="POST" action="{% block form_action %}{% endblock %}">
<input name="item_text" id="id_new_item" class="form-control input-lg"
placeholder="Enter a to-do item" />
{% csrf_token %}
<body>
<div class="navbar">
{% if user.is_authenticated %}
<p>Logged in as {{ user.email}}</p>
<p><a id="id_logout" href="{% url 'logout' %}">Log out</a></p>
{% else %}
<form method="POST" action ="{% url 'send_login_email' %}">
Enter email to log in: <input name="email" type="text" />
{% csrf_token %}
</form>
</div>
{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% block table %}
{% endblock %}
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1>
<form method="POST" action="{% block form_action %}{% endblock %}">
<input name="item_text" id="id_new_item" class="form-control input-lg"
placeholder="Enter a to-do item" />
{% csrf_token %}
</form>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% block table %}
{% endblock %}
</div>
</div>
</div>
</div>
</html>
</body>
</html>
......@@ -36,7 +36,7 @@ class HomePageTest(TestCase):
def test_redirects_after_POST(self):
response = self.client.post('/', data={'item_text': 'A new list item'})
self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 200)
self.assertEqual(response['location'], '/')
def test_displays_all_list_items(self):
......
......@@ -38,6 +38,13 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'lists',
'accounts',
]
AUTH_USER_MODEL = 'accounts.User'
AUTH_USER_MODEL = 'accounts.ListUser'
AUTHENTICATION_BACKENDS = [
'accounts.authentication.PasswordlessAuthenticationBackend',
]
MIDDLEWARE = [
......@@ -101,6 +108,13 @@ AUTH_PASSWORD_VALIDATORS = [
]
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'obeythetestinggoat@gmail.com'
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASSWORD')
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
......
from django.conf.urls import include, url
from lists import views as list_views
from lists import urls as list_urls
from accounts import urls as accounts_urls
urlpatterns = [
url(r'^$', list_views.home_page, name='home'),
url(r'^lists/', include(list_urls)),
url(r'^accounts/', include(accounts_urls)),
]
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment