Skip to content

Commit 348d92b

Browse files
committed
Create new auth app; move user profiles, sign up and other authentication stuff there
1 parent 90a0414 commit 348d92b

File tree

11 files changed

+292
-3
lines changed

11 files changed

+292
-3
lines changed

auth/__init__.py

Whitespace-only changes.

auth/admin.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import auth.models
2+
from django.contrib import admin
3+
4+
admin.site.register(auth.models.Profile)

auth/api.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
from auth.forms import SignUpForm
2+
import sha
3+
import datetime
4+
from random import random
5+
import pytz
6+
from django.contrib.auth import authenticate
7+
from django.contrib.auth.models import User
8+
from django.core.mail import send_mail
9+
from tastypie.serializers import Serializer
10+
from tastypie.utils.mime import determine_format
11+
from django.utils.translation import ugettext as _
12+
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
13+
from tastypie.resources import ModelResource, ALL_WITH_RELATIONS
14+
from tastypie import fields
15+
from auth.models import Profile
16+
from tastypie.authorization import DjangoAuthorization, Authorization
17+
from tastypie.authentication import Authentication, ApiKeyAuthentication
18+
19+
20+
class UserResource(ModelResource):
21+
22+
class Meta:
23+
queryset = User.objects.all()
24+
excludes = ['email', 'password']
25+
26+
27+
class ProfileResource(ModelResource):
28+
user = fields.OneToOneField(UserResource, 'user')
29+
30+
class Meta:
31+
queryset = Profile.objects.all()
32+
authorization = Authorization()
33+
authentication = Authentication()
34+
excludes = ['activation_key', 'key_expires']
35+
filtering = {
36+
'user': ALL_WITH_RELATIONS,
37+
'first_name': ['exact'],
38+
'last_name': ['exact']
39+
}
40+
41+
def dehydrate(self, bundle):
42+
bundle.data['display_name'] = str(bundle.obj)
43+
44+
45+
def sign_up(request):
46+
form = SignUpForm(request.POST)
47+
serializer = Serializer()
48+
format = determine_format(request, serializer,
49+
default_format='application/json')
50+
51+
if form.is_valid():
52+
username = form.cleaned_data['username']
53+
password = form.cleaned_data['password']
54+
email = form.cleaned_data['email']
55+
first_name = form.cleaned_data['first_name']
56+
last_name = form.cleaned_data['last_name']
57+
58+
user = User.objects.create(username=username, password=password,
59+
first_name=first_name, last_name=last_name, email=email,
60+
is_active=False)
61+
user.save()
62+
63+
auth_profile = user.get_profile()
64+
65+
# Build the activation key
66+
salt = sha.new(str(random())).hexdigest()[:5]
67+
activation_key = sha.new(salt + user.username).hexdigest()
68+
key_expires = datetime.datetime.now(pytz.utc) + datetime.timedelta(2)
69+
70+
# User is unactive until visiting activation link
71+
auth_profile.activation_key = activation_key
72+
auth_profile.key_expires = key_expires
73+
activation_link = 'http://127.0.0.1:8000/auth/activate/' + activation_key
74+
75+
auth_profile.save()
76+
77+
subject = _('Welcome to ScoreIt!')
78+
message = _('To activate, please click the following link:\n' + activation_link)
79+
sender = _('noreply@score-it.de')
80+
recipients = [email]
81+
send_mail(subject, message, sender, recipients)
82+
83+
user_resource = UserResource()
84+
profile_resource = ProfileResource()
85+
86+
data = {
87+
'user': user_resource.get_resource_uri(user),
88+
'profile': profile_resource.get_resource_uri(auth_profile),
89+
'activation_key': activation_key
90+
}
91+
92+
return HttpResponse(serializer.serialize(data, format, {}))
93+
94+
else:
95+
return HttpResponseBadRequest(serializer.serialize(form.errors, format, {}))
96+
97+
98+
def validate_user(request):
99+
"""
100+
Checks a user's basic auth credentials and, if valid, returns the users data
101+
"""
102+
103+
# if not request.META.get('HTTP_AUTHORIZATION'):
104+
# return HttpResponseBadRequest('No HTTP_AUTHORIZATION header found')
105+
106+
# try:
107+
# (auth_type, data) = request.META['HTTP_AUTHORIZATION'].split()
108+
# if auth_type.lower() != 'basic':
109+
# return HttpResponseBadRequest('Wrong auth type. Use basic auth!')
110+
# user_pass = base64.b64decode(data)
111+
# except:
112+
# return HttpResponseBadRequest('Could not decode auth credentials.')
113+
114+
# bits = user_pass.split(':', 1)
115+
116+
# if len(bits) != 2:
117+
# return HttpResponseBadRequest('Could not decode auth credentials.')
118+
119+
# user = authenticate(username=bits[0], password=bits[1])
120+
121+
username = request.POST['username']
122+
password = request.POST['password']
123+
124+
if not username or not password:
125+
return HttpResponseBadRequest()
126+
127+
user = authenticate(username=username, password=password)
128+
129+
if user is None or not user.is_active:
130+
return HttpResponseNotFound('User does not exist or password incorrect.')
131+
132+
auth_profile = user.get_profile()
133+
134+
profile_resource = ProfileResource()
135+
bundle = profile_resource.build_bundle(obj=auth_profile, request=request)
136+
profile_resource.full_dehydrate(bundle)
137+
bundle.data['api_key'] = user.api_key.key
138+
139+
return HttpResponse(profile_resource.serialize(None, bundle, 'application/json'))
140+
141+
142+
def is_unique(request):
143+
data = {}
144+
145+
if 'username' in request.GET:
146+
username = request.GET['username']
147+
148+
try:
149+
User.objects.get(username=username)
150+
unique = False
151+
except User.DoesNotExist:
152+
unique = True
153+
except User.MultipleObjectsReturned:
154+
unique = False
155+
156+
data['username'] = unique
157+
158+
if 'email' in request.GET:
159+
email = request.GET['email']
160+
161+
try:
162+
User.objects.get(email=email)
163+
unique = False
164+
except User.DoesNotExist:
165+
unique = True
166+
except User.MultipleObjectsReturned:
167+
unique = False
168+
169+
data['email'] = unique
170+
171+
serializer = Serializer()
172+
173+
format = determine_format(request, serializer, default_format='application/json')
174+
175+
return HttpResponse(serializer.serialize(data, format, {}))

auth/forms.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django import forms
2+
from django.utils.translation import ugettext as _
3+
from django.contrib.auth.models import User
4+
5+
6+
class SignUpForm(forms.Form):
7+
username = forms.CharField(label=_('User name'), required=True)
8+
email = forms.EmailField(label=_('Email'), required=True)
9+
password = forms.CharField(widget=forms.PasswordInput,
10+
label=_('Password'), required=True)
11+
# password_repeat = forms.CharField(widget=forms.PasswordInput,
12+
# label=_('Repeat Password'), required=True)
13+
14+
first_name = forms.CharField(label=_('First Name'), required=True)
15+
last_name = forms.CharField(label=_('Last Name'), required=True)
16+
17+
def clean_username(self):
18+
data = self.cleaned_data['username']
19+
try:
20+
User.objects.get(username=data)
21+
except User.DoesNotExist:
22+
return data
23+
raise forms.ValidationError(_('This username is already taken.'))
24+
25+
def clean_email(self):
26+
data = self.cleaned_data['email']
27+
try:
28+
User.objects.get(email=data)
29+
except User.DoesNotExist:
30+
return data
31+
raise forms.ValidationError(
32+
_('A user with this email is already registered.'))

auth/models.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from django.db import models
2+
from django.contrib.auth.models import User
3+
from django.db.models.signals import post_save
4+
5+
6+
class Profile(models.Model):
7+
user = models.OneToOneField(User)
8+
9+
# Fields used for user activation after signup
10+
activation_key = models.CharField(max_length=40, blank=True)
11+
key_expires = models.DateTimeField(null=True, blank=True)
12+
13+
def __unicode__(self):
14+
return '%s %s (%s)' % (self.user.first_name, self.user.last_name, self.user.username)
15+
16+
17+
def create_user_profile(sender, instance, created, **kwargs):
18+
# Create user profile for user after creation
19+
if created:
20+
Profile.objects.create(user=instance)
21+
22+
post_save.connect(create_user_profile, sender=User)

auth/tests.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
This file demonstrates writing tests using the unittest module. These will pass
3+
when you run "manage.py test".
4+
5+
Replace this with more appropriate tests for your application.
6+
"""
7+
8+
from django.test import TestCase
9+
10+
11+
class SimpleTest(TestCase):
12+
def test_basic_addition(self):
13+
"""
14+
Tests that 1 + 1 always equals 2.
15+
"""
16+
self.assertEqual(1 + 1, 2)

auth/urls.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from django.conf.urls.defaults import *
2+
from tastypie.api import Api
3+
from auth.api import *
4+
5+
6+
v1_api = Api(api_name='v1')
7+
v1_api.register(UserResource())
8+
v1_api.register(ProfileResource())
9+
10+
urlpatterns = patterns('auth.views',
11+
(r'^activate/([abcdef0123456789]+)$', 'activate')
12+
)
13+
14+
urlpatterns += patterns('', (r'^api/', include(v1_api.urls)))
15+
16+
# Non-resource api endpoints
17+
urlpatterns += patterns('auth.api',
18+
(r'^api/v1/validate/$', 'validate_user'),
19+
(r'^api/v1/unique/$', 'is_unique'),
20+
(r'^api/v1/signup/$', 'sign_up')
21+
)

auth/views.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import datetime
2+
import pytz
3+
from django.shortcuts import get_object_or_404, render_to_response
4+
from auth.models import Profile
5+
6+
7+
def activate(request, activation_key):
8+
auth_profile = get_object_or_404(Profile, activation_key=activation_key)
9+
user = auth_profile.user
10+
11+
if auth_profile.key_expires < datetime.datetime.now(pytz.utc):
12+
user.delete()
13+
return render_to_response('activate.html', {'expired': True})
14+
15+
user.is_active = True
16+
user.save()
17+
return render_to_response('activate.html', {'success': True})

settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@
120120
'tastypie',
121121
# Uncomment the next line to enable admin documentation:
122122
# 'django.contrib.admindocs',
123+
'auth',
123124
'handball'
124125
)
125126

126-
AUTH_PROFILE_MODULE = 'handball.Person'
127+
AUTH_PROFILE_MODULE = 'auth.profile'
127128

128129
EMAIL_HOST = 'wp253.webpack.hosteurope.de'
129130

urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
admin.autodiscover()
66

77
urlpatterns = patterns('',
8-
(r'^', include('handball.urls')),
8+
(r'^auth/', include('auth.urls')),
9+
(r'^handball/', include('handball.urls')),
910
(r'^admin/', include(admin.site.urls))
1011
)

0 commit comments

Comments
 (0)