Skip to content
This repository has been archived by the owner on Jan 19, 2021. It is now read-only.

Commit

Permalink
Merge branch 'invite-fixes'
Browse files Browse the repository at this point in the history
  • Loading branch information
davedash committed Aug 17, 2011
2 parents fc9c4db + bf4e131 commit aa9e54f
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 28 deletions.
3 changes: 2 additions & 1 deletion apps/phonebook/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
class Invite(models.Model):
# uid of the person inviting, ozten can clarify size
inviter = models.CharField(max_length=32, editable=False)
destination = models.EmailField()
recipient = models.EmailField()
redeemer = models.CharField(max_length=32, editable=False)
code = models.CharField(max_length=32, editable=False, unique=True)
redeemed = models.DateTimeField(null=True, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
Expand Down
2 changes: 1 addition & 1 deletion apps/phonebook/templates/phonebook/invite.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<form method="POST" action="{{ url('invite') }}">
{{ form.errors }}
{{ csrf() }}
{{ form.destination }}
{{ form.recipient }}
<input type="submit">
</form>

Expand Down
2 changes: 1 addition & 1 deletion apps/phonebook/templates/phonebook/invited.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block main_content %}
<p>
{% trans email=invite.destination %}
{% trans email=invite.recipient %}
{{ email }} has been invited to Mozillians.
{% endtrans %}
</p>
Expand Down
17 changes: 15 additions & 2 deletions apps/phonebook/tests/test_invites.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def invite_someone(self):
"""
# Send an invite.
url = reverse('invite')
d = dict(destination='mr.fusion@gmail.com')
d = dict(recipient='mr.fusion@gmail.com')
r = self.mozillian_client.post(url, d, follow=True)
eq_(r.status_code, 200)
eq_(pq(r.content)('div#main-content p').text(),
Expand All @@ -34,7 +34,7 @@ def invite_someone(self):
def get_register(self, invite):
r = self.client.get(invite.get_url())
doc = pq(r.content)
eq_(doc('input#id_email')[0].value, invite.destination)
eq_(doc('input#id_email')[0].value, invite.recipient)
eq_(doc('input#id_code')[0].value, invite.code)
return r

Expand Down Expand Up @@ -66,7 +66,20 @@ def test_send_invite_flow(self):
d = r.context['form'].initial
r = self.redeem_invite(invite, **d)
eq_(r.context['user'].is_vouched(), True)
eq_(r.context['user'].unique_id,
Invite.objects.get(pk=invite.pk).redeemer)

# Don't reuse codes.
r = self.redeem_invite(invite, email='mr2@gmail.com')
eq_(r.context['user'].is_vouched(), False)

def test_unvouched_cant_invite(self):
"""
Let's make sure the unvouched don't let in their friends...
Their stupid friends...
"""
url = reverse('invite')
d = dict(recipient='mr.fusion@gmail.com')
r = self.pending_client.post(url, d, follow=True)
eq_(r.status_code, 403)
23 changes: 20 additions & 3 deletions apps/phonebook/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from functools import wraps
from ldap import SIZELIMIT_EXCEEDED

import django.contrib.auth
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.http import (Http404, HttpResponse, HttpResponseRedirect,
HttpResponseForbidden)
from django.shortcuts import redirect, render
from django.views.decorators.http import require_POST

Expand All @@ -17,6 +19,21 @@
from phonebook.models import Invite


def vouch_required(f):
"""
If a user is not vouched they get a 403.
"""
@login_required
@wraps(f)
def wrapped(request, *args, **kwargs):
if request.user.is_vouched():
return f(request, *args, **kwargs)
else:
return HttpResponseForbidden(_('You must be vouched to do this.'))

return wrapped


def profile_uid(request, unique_id):
"""
View a profile by unique_id, which is a stable,
Expand Down Expand Up @@ -197,7 +214,7 @@ def invited(request, id):
return render(request, 'phonebook/invited.html', dict(invite=invite))


@login_required
@vouch_required
def invite(request):
if request.method == 'POST':
f = forms.InviteForm(request.POST)
Expand All @@ -217,7 +234,7 @@ def invite(request):
link = _("Join Mozillians: %s") % invite.get_url()
message = "%s\n\n%s" % (message, link)
send_mail(subject, message, request.user.email,
[invite.destination])
[invite.recipient])

return HttpResponseRedirect(reverse(invited, args=[invite.id]))
else:
Expand Down
3 changes: 2 additions & 1 deletion apps/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def register(request):
code = request.GET['code']
try:
invite = get_invite(code)
initial['email'] = invite.destination
initial['email'] = invite.recipient
initial['code'] = invite.code
except Invite.DoesNotExist:
pass
Expand Down Expand Up @@ -67,6 +67,7 @@ def _save_new_user(request, form):
if voucher:
registrar.record_vouch(voucher=voucher, vouchee=uniq_id)
invite.redeemed = datetime.datetime.now()
invite.redeemer = uniq_id
invite.save()

user = auth.authenticate(username=username, password=password)
Expand Down
1 change: 1 addition & 0 deletions directory/devslapd/bin/x-root-search
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

PROG=`basename "$0"`

cd $(dirname $(dirname $0))
# Find the vars file for this example
# The working directory must be in the example subtree

Expand Down
17 changes: 4 additions & 13 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
# -*- coding: utf-8 -*-
#
# Mozillians documentation build configuration file, created by
# sphinx-quickstart on Thu Jul 14 14:45:32 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
Expand Down Expand Up @@ -217,4 +205,7 @@


# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}
intersphinx_mapping = dict(
python= ('http://docs.python.org/', None),
django= ('https://docs.djangoproject.com/en/dev/', 'django.inv')
)
11 changes: 6 additions & 5 deletions docs/demo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Demo
If you load the sample LDAP data (using `x-rebuild`), then you can use these
instructions for running the demo.

-----
------------------
Login Pending User
-----
------------------

You can log in using::

Expand All @@ -18,16 +18,17 @@ You can log in using::

You are now logged in as "Scott Wingfield". You can't search or do much ;)

-----
--------------------
Login Mozillian User
-----
--------------------

You can log in using::

Email: u000001@mozillians.org
Password: secret

You are now logged in as "Bridget Hill". You can search for people. You can vouch them.
You are now logged in as "Bridget Hill". You can search for people. You can
vouch them.

------
Search
Expand Down
Binary file added docs/django.inv
Binary file not shown.
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Contents:
:maxdepth: 2

installation
sessions
registration
invites
demo
ldap
ldap-developer-notes
Expand Down
37 changes: 37 additions & 0 deletions docs/invites.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=================
Invitation System
=================

Mozillians has an inivitation system that let's vouched users invite others to
join Mozillians. These users who join are automatically vouched.


The Model
---------

The invite model stores the following:

.. py:attribute:: Invite.inviter
This is the ``uid`` of the vouched Mozillian who is doing the inviting.

.. py:attribute:: Invite.recipient
This is the email address of where the invitation is sent.

.. py:attribute:: Invite.code
This is the randomly generated invite code. This code is used in the
:ref:`registration` system.

.. py:attribute:: Invite.redeemer
This is the ``uid`` of the newly created user who has redeemed the invite.

.. py:attribute:: Invite.redeemed
This is the date the invite was redeemed.

.. py:attribute:: Invite.created
This is the date the invite was created.
35 changes: 35 additions & 0 deletions docs/sessions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
=============
LDAP Sessions
=============

Access to the LDAP directory is done by logging into an LDAP server as the
end-user. This is different
from a database which traditionally has the same privileged account regardless
of who the end user is.

For example:

===== =================== ====================
User LDAP directory user Mysql Database user
===== =================== ====================
Sally sally@draper.com mozillians@localhost
Gene gene@draper.com mozillians@localhost
Don don@draper.com mozillians@localhost
Bobby bobby@draper.com mozillians@localhost
Betty betty@draper.com mozillians@localhost
===== =================== ====================

This means we need to somehow store the users credentials each time they log
in.

.. note::
This may change down the road if we implement different LDAP
authentication layers.

We do this by writing the password to an encrypted cookie in a post-login
signal.

This takes advantage of the Django's
:ref:`cookie based session backend<django:cookie-session-backend>` and
the new :py:mod:`django.core.signing`.

5 changes: 4 additions & 1 deletion migrations/03-invite.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
DROP TABLE invite; -- things we can do before we launch.

CREATE TABLE `invite` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`inviter` varchar(32) NOT NULL,
`destination` varchar(75) NOT NULL,
`recipient` varchar(75) NOT NULL,
`redeemer` varchar(32) NOT NULL,
`code` varchar(32) NOT NULL UNIQUE,
`redeemed` datetime,
`created` datetime NOT NULL
Expand Down

0 comments on commit aa9e54f

Please sign in to comment.