london python developer_aws devop engineer

43
12/6/2014 London Python developer/AWS devop engineer http://10kblogger.wordpress.com/ 1/43 London Python developer/AWS devop engineer How Kickstarter will kill Open Source Anyone who works with Linux knows how valuable and revolutionary the open source movement has been. Countless hackers around the world working – often for nothing – on all types of software. Yesterday I saw a Kickstarter project asking for donations to add schema migrations to Django’s core (http://www.kickstarter.com/projects/andrewgodwin/schemamigrationsfordjango) . A £2,500 target to add some new features to Django. At the time of writing pledges for over £12,000 have been made. The trouble is, I think this will damage Django – and other open source projects – in the long run. You see, I nearly donated. I like Django, I’ve worked with it for several years, and database migrations are a pain in most languages. But I nearly donated because I like Django. This would have been a way of showing it even though I’m not working with it right now. Only the problem is that the developer who set up the Kickstarter project is funding his own work on Django. Why is that a problem? Shouldn’t people get paid to work on projects? Well yes, I have nothing against that, and £2,500 for the amount of work involved seems reasonable. But when £12,000+ has been pledged, where will the surplus go? Are core developers likely to be annoyed by the fact that this project has raised so much beyond what was asked for, and will they feel entitled to some? Stretch goals go up to £7,000, but now a further £5,000 has been pledged on top of that. Is this a situation where the first developer from a project garners lots of love – and makes a good profit – for being the first to come up with the idea of requesting funding? Should the excess be contributed back to the community/core dev team? If I were a core Django developer who worked on the project in my own time, I might be thinking around now that perhaps I should start creating a project for myself on Kickstarter. In fact, perhaps I’m reasonably owed some money for all the work I’ve put into it in the past. And this is the part that I think will hurt Django, and by extension other open source projects. The question of what happens to the surplus is really irrelevant, and this is one specific project. The idea is interesting. If this were to happen on different software projects, I think we might find more of a hesitancy on the part of developers to implement certain large pieces of work, but perhaps more importantly, resentment between those who charge and those who don’t. Alternatively it could be argued that creating Kickstarter projects to fund development will ultimately lead to more important work being prioritised because people can afford to take a break from their day jobs to work on those features important enough to others that people will fund the work. Is this model sustainable? What do you think? Is this the beginning of the end of working for free on open source projects? Let me know in your comments… AR AY AY AY AY AY AY

Upload: ms6675223

Post on 21-Jul-2016

19 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 1/43

London Python developer/AWS devopengineer

How Kickstarter will kill Open Source

Anyone who works with Linux knows how valuable and revolutionary the open source movement hasbeen. Countless hackers around the world working – often for nothing – on all types of software.

Yesterday I saw a Kickstarter project asking for donations to add schema migrations to Django’s core(http://www.kickstarter.com/projects/andrewgodwin/schemamigrationsfordjango). A £2,500 target toadd some new features to Django. At the time of writing pledges for over £12,000 have been made. Thetrouble is, I think this will damage Django – and other open source projects – in the long run.

You see, I nearly donated. I like Django, I’ve worked with it for several years, and database migrations area pain in most languages. But I nearly donated because I like Django. This would have been a way ofshowing it even though I’m not working with it right now. Only the problem is that the developer who setup the Kickstarter project is funding his own work on Django. Why is that a problem? Shouldn’t peopleget paid to work on projects? Well yes, I have nothing against that, and £2,500 for the amount of workinvolved seems reasonable. But when £12,000+ has been pledged, where will the surplus go? Are coredevelopers likely to be annoyed by the fact that this project has raised so much beyond what was asked for,and will they feel entitled to some? Stretch goals go up to £7,000, but now a further £5,000 has beenpledged on top of that. Is this a situation where the first developer from a project garners lots of love – andmakes a good profit – for being the first to come up with the idea of requesting funding? Should the excessbe contributed back to the community/core dev team?

If I were a core Django developer who worked on the project in my own time, I might be thinking aroundnow that perhaps I should start creating a project for myself on Kickstarter. In fact, perhaps I’m reasonablyowed some money for all the work I’ve put into it in the past. And this is the part that I think will hurtDjango, and by extension other open source projects.

The question of what happens to the surplus is really irrelevant, and this is one specific project. The idea isinteresting. If this were to happen on different software projects, I think we might find more of a hesitancyon the part of developers to implement certain large pieces of work, but perhaps more importantly,resentment between those who charge and those who don’t.

Alternatively it could be argued that creating Kickstarter projects to fund development will ultimately leadto more important work being prioritised because people can afford to take a break from their day jobs towork on those features important enough to others that people will fund the work. Is this modelsustainable? What do you think? Is this the beginning of the end of working for free on open sourceprojects? Let me know in your comments…

MARMAYMAYMAYMAYMAYMAY

Page 2: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 2/43

COMMENTS 4 CommentsCATEGORIES Uncategorized

A RESTful password locker with Django andbackbone.js part 6

This series (http://10kblogger.wordpress.com/2012/05/29/arestfulpasswordlockerwithdjangoandbackbonejspart5/) has explained how to create a RESTful web application using Django RESTframework and Backbone.js. The code is available (https://github.com/boosh/pwlocker) for you to exploreand play with.

To conclude, I’d like to discuss what other additions we could make to make the application more secure,performant and robust.

Security

Storing passwords in plain text in a database is a Bad Idea. Django hashes user passwords for us, so thosecredentials are fine. But how could we secure the passwords users want to put in our password locker?

If we didn’t have the requirement to allow passwords to be shared, we could use a symmetric keyencryption algorithm with the user’s raw authentication password as a secret key – possibly munged withsome extra data. This would mean that passwords would only be able to be decrypted once a user loggedin and would make largescale bruteforcing of the database unfeasible if we chose our algorithm carefullysince every user’s password would need to be cracked to decrypt their data. We would be storing userpasswords in memory and it’s possible they could leak to some degree, but it’d be safer if a hacker wasonly able to download a dump of the database.

One possibility for supporting sharing and making the stored data more secure would be to use public keycryptography. The private key could require the user’s password to decrypt data. If a user shares a password, we could encrypt it with the recipient’s public key and they’d be able to decrypt it with theirprivate key when they log in.

Cryptography is computationally expensive, and since our code is in python we may find it better to codethese modules in a compiled language. Some python libraries implement their encryption routines in C, sowe could use these. However, if we were interested in scalability, we may find it more performant to usededicated servers to handle the cryptography. In this scenario, we could use an RPC framework such asApache Thrift (http://thrift.apache.org/) to handle communication between the frontend web nodes andJava/C backends.

Page 3: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 3/43

Also, the entire application must run over SSL for the site to be secure, and ideally not contain any thirdparty content (such as adverts) to make sure that there’s no possibility for some kind of crossdomainJavascript bug to steal user passwords.

Performance and robustness

Before putting this code into production, we should create a full suite of unit & functional tests. It alsoneeds testing crossbrowser testing to make sure there are no quirks in different browsers.

Because the majority of the application is loaded via AJAX, we can cache web templates to a large degreewhich will reduce load on the web nodes. Of course, we should also combine and minify Javascript andCSS.

That’s all. I hope you’ve found this tutorial useful.

COMMENTS Leave a CommentCATEGORIES Programming

A RESTful password locker with Django andbackbone.js part 5

In this penultimate part of this series (http://10kblogger.wordpress.com/2012/05/28/arestfulpasswordlockerwithdjangoandbackbonejspart4/), we’re going to add the ability to share passwords betweenusers. The spec for sharing passwords is as follows:

1. Users should be able to maintain a contact list of other users with whom they can share passwords.Users must be able to search for other users by user name, and be able to view their first and last nameto confirm the user is who they think they are.

2. If a user removes another user from their contact list, all passwords shared with that user should stopbeing shared with the removed contact.

3. Passwords must be able to be shared with multiple users in the creator’s contact list.4. Only the creator of a password is allowed to modify data. Users with whom it’s shared have readonly

access.5. Only the creator of a password may share a password (i.e. if User A has shared a password with User

B, User B may not share that password with anyone else).

Since this series is primarily about Backbone.js, we’ll implement the above in a single paged web app.

The contact list

Page 4: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 4/43

To allow users to share passwords, we’ll create a manytomany relationship to a new model. Update`apps/passwords/models.py` to the following:

This is standard Django. Migrate with south and apply it: `./manage.py schemamigration passwords –auto&& ./manage.py migrate passwords`.

We’ll create 2 new APIs – one for the PasswordContact resource, and another for User objects which willallow members to search for other users.

Create `apps/users/resources.py` and enter the following:

123456789

10111213141516171819202122232425262728293031323334353637

from django.db import modelsfrom django.contrib.auth.models import User class Password(models.Model): """ Represents a username and password together with several other fields """ created_by = models.ForeignKey(User, related_name='+', editable title = models.CharField(max_length=200) username = models.CharField(max_length=200, blank=True) password = models.CharField(max_length=200) url = models.URLField(max_length=500, blank=True, verbose_name='Site URL') notes = models.CharField( max_length=500, blank=True) created_at = models.DateTimeField(auto_now_add=True, editable updated_at = models.DateTimeField(auto_now=True, editable= shares = models.ManyToManyField('PasswordContact', verbose_name='Share with', blank=True) def __unicode__(self): return self.title class PasswordContact(models.Model): """ Someone with whom a user can share a Password """ from_user = models.ForeignKey(User, related_name="passwordcontactfrom" to_user = models.ForeignKey(User, related_name="passwordcontactto" created_at = models.DateTimeField(auto_now_add=True, editable updated_at = models.DateTimeField(auto_now=True, editable= def __unicode__(self): return "%s %s (%s)" % (self.to_user.first_name, self.to_user.last_name,

1234

from djangorestframework.resources import ModelResource from django.contrib.auth.models import User

Page 5: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 5/43

Update `apps/passwords/resources.py` to the following:

56789

10111213141516171819202122

class UserResource(ModelResource): """ Lets users search for other users by username. """ model = User fields = ('id', 'first_name', 'last_name', 'username', 'url' def validate_request(self, data, files=None): """ Backbone.js will submit all fields in the model back to us, but some fields are set as uneditable in our Django model. So we need to remove those extra fields before performing validation. """ for key in self.ignore_fields: if key in data: del data[key] return super(UserResource, self).validate_request(data, files)

123456789

1011121314151617181920212223242526272829303132333435

from djangorestframework.resources import ModelResourcefrom djangorestframework.serializer import Serializerfrom django.core.urlresolvers import reverse from apps.users.resources import UserResourcefrom models import Password, PasswordContact class PasswordContactResource(ModelResource): model = PasswordContact ordering = ('to_user__first_name',) fields = ('id', 'url', ('to_user', 'UserResource'), ('from_user' ignore_fields = ('id',) def validate_request(self, data, files=None): """ Backbone.js will submit all fields in the model back to us, but some fields are set as uneditable in our Django model. So we need to remove those extra fields before performing validation. """ for key in self.ignore_fields: if key in data: del data[key] return super(PasswordContactResource, self).validate_request(data, files) class CurrentUserSingleton(object): """ Literally the only way I can find to give the PasswordResource access to the current user object. """ user = None @classmethod def set_user(cls, user): cls.user = user

Page 6: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 6/43

Change `apps/api/urls.py` to the following to wire up URLs:

36373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384

class PasswordResource(ModelResource): model = Password # by default, django rest framework won't return the ID backbone.js # needs it though, so don't exclude it exclude = ('created_by',) ordering = ('title',) # django rest framework will overwrite our 'url' attribute with its own # that points to the resource, so we need to provide an alternative. include = ('resource_url',) ignore_fields = ('created_at', 'updated_at', 'id', 'maskedPassword' 'resource_url', 'is_owner') fields = ('id', 'title', 'username', 'password', 'url', 'notes' 'resource_url', 'shares', 'is_owner') related_serializer = PasswordContactResource def is_owner(self, instance): """ Returns True if this resource was created by the current user. """ return instance.created_by == CurrentUserSingleton.user def url(self, instance): """ Return the instance URL. If we don't specify this, django rest framework will return a generated URL to the resource """ return instance.url def resource_url(self, instance): """ An alternative to the 'url' attribute django rest framework will add to the model. """ return reverse('passwords_api_instance', kwargs='id': instance.id) def validate_request(self, data, files=None): """ Backbone.js will submit all fields in the model back to us, but some fields are set as uneditable in our Django model. So we need to remove those extra fields before performing validation. """ for key in self.ignore_fields: if key in data: del data[key] return super(PasswordResource, self).validate_request(data, files)

1234

from django.conf.urls.defaults import patterns, url from views import PasswordListView, PasswordInstanceViewfrom views import PasswordContactListView, PasswordContactReadOrDeleteInstanceView

Page 7: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 7/43

We also need to update the views in `apps/api/views.py`:

56789

101112131415

from views import UserView urlpatterns = patterns('', url(r'passwords/$', PasswordListView.as_view(), name='passwords_api_root' url(r'passwords/(?P[09]+)$', PasswordInstanceView.as_view(), name url(r'passwordcontacts/$', PasswordContactListView.as_view(), name='password_contacts_api_root'), url(r'passwordcontacts/(?P[09]+)$', PasswordContactReadOrDeleteInstanceView.as_view(), name='password_contacts_api_instance'), url(r'user/(?P.+)$', UserView.as_view(), name='user_api'),)

123456789

101112131415161718192021222324252627282930313233343536373839404142

from django.db.models import Qfrom djangorestframework.mixins import ModelMixin, InstanceMixin, \ReadModelMixin, DeleteModelMixinfrom djangorestframework.permissions import IsAuthenticatedfrom djangorestframework.response import ErrorResponsefrom djangorestframework import statusfrom djangorestframework.views import ListOrCreateModelView, InstanceModelView, ModelView from apps.passwords.models import PasswordContactfrom apps.passwords.resources import PasswordResource, PasswordContactResource, \CurrentUserSingletonfrom apps.users.resources import UserResource class RestrictPasswordToUserMixin(ModelMixin): """ Mixin that restricts users to working with their own data """ def get_queryset(self): """ Only return objects created by, or shared with, the currently authenticated user. """ return self.resource.model.objects.filter(Q(created_by Q(shares__to_user=self.user)).distinct() def get_instance_data(self, model, content, **kwargs): """ Set the created_by field to the currently authenticated user. """ content['created_by'] = self.user return super(RestrictPasswordToUserMixin, self).get_instance_data(model, content, def initial(self, request, *args, **kwargs): """ Set the currently authenticated user on the resource """ CurrentUserSingleton.set_user(request.user) return super(ModelMixin, self).initial(request, *args, def final(self, request, response, *args, **kargs): """ Clear the current user singleton to make sure it doesn't leak

Page 8: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 8/43

434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899

""" CurrentUserSingleton.set_user(None) return super(ModelMixin, self).final(request, response, class PasswordListView(RestrictPasswordToUserMixin, ListOrCreateModelView): """ List view for Password objects. """ resource = PasswordResource permissions = (IsAuthenticated, ) class PasswordInstanceView(RestrictPasswordToUserMixin, InstanceModelView): """ View for individual Password instances """ resource = PasswordResource permissions = (IsAuthenticated, ) def put(self, request, *args, **kwargs): """ Only allow the creating user to modify an instance. """ model = self.resource.model query_kwargs = self.get_query_kwargs(request, *args, try: self.model_instance = self.get_instance(**query_kwargs) if self.model_instance.created_by == self.user: return super(RestrictPasswordToUserMixin, self except model.DoesNotExist: pass raise ErrorResponse(status.HTTP_401_UNAUTHORIZED, None def delete(self, request, *args, **kwargs): """ Only the creator should be able to delete an instance. """ model = self.resource.model query_kwargs = self.get_query_kwargs(request, *args, try: instance = self.get_instance(**query_kwargs) except model.DoesNotExist: raise ErrorResponse(status.HTTP_404_NOT_FOUND, None if instance.created_by == self.user: instance.delete() else: raise ErrorResponse(status.HTTP_401_UNAUTHORIZED, class PasswordContactListView(ListOrCreateModelView): """ List view for PasswordContact objects. """ resource = PasswordContactResource

Page 9: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 9/43

100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156

permissions = (IsAuthenticated, ) def get_queryset(self): """ Only return objects where the from_user is the currently authenticated user. """ return self.resource.model.objects.filter(from_user=self def get_instance_data(self, model, content, **kwargs): """ Set the from_user field to the currently authenticated user. """ content['from_user'] = self.user return super(PasswordContactListView, self).get_instance_data(model, content, class ReadOnlyInstanceModelView(InstanceMixin, ReadModelMixin, ModelView): """ A view which provides default operations for read/delete against a model instance but that prevents updates. """ _suffix = 'Instance' class PasswordContactReadOrDeleteInstanceView(ReadOnlyInstanceModelView): """ View for individual PasswordContact instances """ resource = PasswordContactResource permissions = (IsAuthenticated, ) def delete(self, request, *args, **kwargs): """ Deletes shares from Passwords when a PasswordContact is deleted """ model = self.resource.model query_kwargs = self.get_query_kwargs(request, *args, try: instance = self.get_instance(**query_kwargs) except model.DoesNotExist: raise ErrorResponse(status.HTTP_404_NOT_FOUND, None # remove any shares from any passwords shared with this contact password_contacts = PasswordContact.objects.filter(from_user to_user=instance.to_user) for password_contact in password_contacts: password_contact.delete() instance.delete() return class UserView(InstanceMixin, ReadModelMixin, ModelView): """ View for individual Users lets users find other users by username """ resource = UserResource permissions = (IsAuthenticated, )

Page 10: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 10/43

Finally,

There’s quite a lot going on in the above code:

We’re restricting users to only viewing those objects for which they’re the creator or a recipient of ashare.A singleton is used to enable the PasswordResource to determine whether the currently authenticateduser created a resource or not, and this is returned as the `is_owner` property we added to thePasswordResource.We restrict CRUD operations on password instances so they can only be performed by the creator of apassword.Users can only create or delete password contacts, they can’t update them. When a password contact isdeleted, we remove all shares associated with that user. So when a user removes someone from theircontact list, access to all shared passwords is automatically revoked.Finally we prevent the UserView from returning the current user. This view only support GETpreventing users from browsing all members.

One last thing we need before we can hook things up on the frontend is to update the PasswordForm so itallows users to share their passwords with users in their contact list. Update `apps/passwords/forms.py`with the following:

157158159160161162163

def get_queryset(self): """ Filter the current user from search results to prevent them sharing with themselves. """ return self.resource.model.objects.filter(~Q(id=self.user.

123456789

10111213141516171819202122232425

from django.forms import ModelFormfrom django.forms import widgetsfrom django.forms.models import ModelMultipleChoiceFieldfrom django.utils.translation import ugettext_lazy as _ from models import Password, PasswordContact class PasswordForm(ModelForm): class Meta: model = Password widgets = 'shares': widgets.CheckboxSelectMultiple def __init__(self, user, *args, **kwargs): super(PasswordForm, self).__init__(*args, **kwargs) remove_message = unicode(_('Hold down "Control", or "Command" on a Mac, to select more than one.' for field in self.fields: if remove_message in self.fields[field].help_text: self.fields[field].help_text = self.fields[field].help_text.replace(remove_message, '').strip() # restrict the choice of users to share passwords with to a # user's PasswordContacts self.fields['shares'] = ModelMultipleChoiceField(

Page 11: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 11/43

Backbone.js

Let’s create a separate application for handling contacts, although we’ll load it all on the same page.

First, update `templates/passwords/password_list.html` as follows:

262728

queryset=PasswordContact.objects.filter(from_user= .order_by('to_user__first_name'), widget=widgets.CheckboxSelectMultiple())

123456789

1011121314151617181920212223242526272829303132333435363738394041

% extends "base.html" % % load sekizai_tags %% load bootstrap_toolkit % % block content % % addtoblock "js" %<script type="text/javascript" src=" STATIC_URL bootstrap/js/bootstrap.min.js" <! backbone ><script type="text/javascript" src=" STATIC_URL contrib/backbone/ICanHaz.min.js"<script type="text/javascript" src=" STATIC_URL contrib/backbone/json2.js"<script type="text/javascript" src=" STATIC_URL contrib/backbone/backbonemin.js"<script type="text/javascript" src=" STATIC_URL /js/contacts.js" % endaddtoblock %</pre> &nbsp; <ul class="nav navtabs"><ul class="nav navtabs"> <li class="active"><a href="#passwordPanel" datatoggle="tab"</ul></ul> &nbsp; <ul class="nav navtabs"><ul class="nav navtabs"> <li><a href="#contactPanel" datatoggle="tab">Contacts</a</ul></ul> &nbsp; <pre> </pre><div class="tabcontent"> <div id="passwordPanel" class="tabpane active"> <h1 class="pageheader">Passwords</h1>

Page 12: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 12/43

424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798

Move your mouse over a password to reveal it. You can only edit your own passwords, not those that have been shared with you. <table class="table tablestriped"><thead><tr><th>Title</th><th>User name</th><th>Password</th><th>Notes</th><th>Actions</th></tr></thead><tfoot><tr><td colspan="5"><button class="btn btnprimary" datatoggle="modal"</tr></tfoot></table> <div id="passwordModal" class="modal hide fade"><form id="passwordForm" method="post"> <div class="modalheader"><button class="close" datadismiss= <h3>Password Details</h3></div> <div class="modalbody"> form|as_bootstrap % csrf_token %</ <div class="modalfooter"><a class="btn" href="#" datadismiss</form></div></div> <div id="contactPanel" class="tabpane"> <h1 class="pageheader">Manage contacts</h1> <div class="well"> <h3>Add new contacts</h3> To find other users to share passwords with, enter their username below: <form class="formsearch"><input id="userSearch" class="searchquery" <h3>My contacts</h3> You can share passwords with the following users. If you want to stop sharing all passwords with a certain user, simply delete them from your list by clicking on the icon. <table class="table tablestriped"><thead><tr><th>Name</th><th>User name</th><th>Actions</th></tr>

Page 13: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 13/43

We’ve added a few new templates and have created a nice tabbed interface courtesy of Twitter Bootstrap.

So, to create our contact application, enter the following into a new file, `staticfiles/js/contacts.js`:

99100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149

</thead></table></div></div><pre> % load verbatim % <! ICanHaz templates >% comment % Mustache and django both use tags for templates, so we need to use a custom template tag to output the mustache template exactly as it is. % endcomment % % verbatim %<script id="passwordRowTpl" type="text/html"<td> <a href=" url " target="_blank"> title </a></td> <td> username </td> <td class="password"> maskedPassword </td> <td> notes </td> <td> #is_owner <a href="#" class="edit" title="Edit this entry"><i class="iconpencil"></i></a> <a href="#" class="destroy" title="Delete this entry"><i class="iconremove"></i></a> /is_owner</td> // ]]></script> <script id="contactRowTpl" type="text/html">// <![CDATA[<td> to_user.first_name to_user.last_name </td> <td> to_user.username </td> <td> <a href="#" class="destroy" title="Delete this contact"><i class="iconremove"></i></a></td> // ]]></script> <script id="shareOption" type="text/html">// <![CDATA[ <label class="checkbox"> <input type="checkbox" value=" id " name="shares"> to_user.first_name to_user.last_name ( to_user.username ) </label> // ]]></script> % endverbatim %% endblock %

Page 14: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 14/43

123456789

101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657

// load the following using JQuery's document ready function$(function() // Contact model var Contact = Backbone.Model.extend( remove: function(options) mergedOptions = wait: true; $.extend(mergedOptions, options); this.destroy(mergedOptions); ); // set up the view for a contact var ContactView = Backbone.View.extend( tagName: 'tr', events: "click a.destroy" : "remove" , remove: function(event) event.stopImmediatePropagation(); event.preventDefault(); if (confirm("Are you sure you want to delete this contact?")) var that = this; this.model.remove(error: function(model, response) if (response.status == 403) alert("You don't have permission to else alert("Unable to delete that data" , success: function() // update the form options a little hacky, but oh well $('#passwordForm').find(':checkbox').remove(); $('#passwordForm').find('.checkbox').remove(); var shareOptions = new Array(); that.options.collection.each(function shareOptions.push(ich.shareOption(data.toJSON(), ); $(shareOptions.join('')).insertAfter( ); , render: function () // template with ICanHaz.js (ich) $(this.el).html(ich.contactRowTpl(this.model.toJSON())); return this;

Page 15: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 15/43

585960616263646566676869707172737475767778798081828384858687888990919293949596979899

100101102103104105106107108109110111112113114

); // define the collection of contacts var ContactCollection = Backbone.Collection.extend( model: Contact, url: '/api/1.0/passwordcontacts/', // maintain ordering by first_name comparator: function(obj1, obj2) return obj1.get('to_user').first_name.localeCompare(obj2.get( ); /** * Manages the list of contacts. */ var ContactListView = Backbone.View.extend( tagName: 'tbody', /** * Constructor. Takes a reference to the parent view so we can invoke * methods on it. */ initialize: function(options) // instantiate a password collection this.collection = new ContactCollection(); this.collection.bind('all', this.render, this); this.collection.fetch(); , addOne: function(contact) this.$el.append(new ContactView(model: contact, collection: return this; , addNew: function(data, options) mergedOptions = wait: true; $.extend(mergedOptions, options); var contact = to_user: data.id ; this.collection.create(contact, mergedOptions); return this; , render: function() this.$el.html(''); this.collection.each(this.addOne, this); return this; ); /** * View for the overall application. We need this because backbone can only

Page 16: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 16/43

115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171

* bind events for children of 'el'. * * In our template our modal is inside #app, so this class handles * interaction at the application level rather than strictly with a * collection of Passwords (that's the job of the PasswordListView). */ var ContactPanelView = Backbone.View.extend( el: '#contactPanel', events: "click #contactPanel :submit": "handleSearch", "keydown #contactPanel :input[type=text]": "handleSearchOnEnter" , initialize: function() this.dataList = new ContactListView(app: this); , displayError: function(model, response) if (response.status == 403) alert("You don't have permission to edit that data" else alert("Unable to create or edit that data. Please make sure you entered valid data." , render: function() this.$el.find('table').append(this.dataList.render().el); , handleSearch: function(event) event.preventDefault(); event.stopImmediatePropagation(); var username = $('#userSearch').val(); var that = this; // perform a GET request to the userSearch service and if it // returns a user, create a new PasswordContact $.ajax( url: '/api/1.0/user/' + username, dataType: 'json', success: function(data, textStatus, jqXHR) that.dataList.addNew(data, success: function $('#userSearch').val(''); // update the form options $('#passwordForm').find(':checkbox').remove(); $('#passwordForm').find('.checkbox').remove(); var shareOptions = new Array(); that.dataList.collection.each(function shareOptions.push(ich.shareOption(data.toJSON(), );

Page 17: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 17/43

The above is quite similar to passwords.js, but generally simpler. About the only complex code is to dowith updating the list of checkboxes on the password form when users add or delete contacts. It’s not idealhaving selectors in the view like that, but we’ve managed to keep it to a minimum, so I can live with ithere.

The `handleSearch` method uses the `user` API – if it successfully receives a response it creates a newPasswordContact and updates the user’s contact list.

Try it out

This post is a bit like a shopping list, but hopefully it’ll help make the repository(https://github.com/boosh/pwlocker) more accessible. If you open two different browsers you’ll be able tocreate two different users, add them to each others’ contact lists and share and revoke passwords betweenthem.

172173174175176177178179180181182183184185186187188189190191192193194195196197198199

$(shareOptions.join('')).insertAfter( ); , error: function(jqXHR, textStatus, errorThrown) if (jqXHR.status) alert("Sorry, we couldn't find that user" else alert("There was a problem searching for that user." ); return this; , handleSearchOnEnter: function(event) // process the modal if the user pressed the ENTER key if (event.keyCode == 13) return this.handleSearch(event); ); var contactPanel = new ContactPanelView(); contactPanel.render(););

Page 18: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 18/43

(http://10kblogger.files.wordpress.com/2012/05/final.png)

We’ll finish off this series with a discussion of the current architecture(http://10kblogger.wordpress.com/2012/05/29/arestfulpasswordlockerwithdjangoandbackbonejspart6/) and what could be done to make the application more secure.

COMMENTS 1 CommentCATEGORIES Programming

A RESTful password locker with Django andbackbone.js part 4

So far we’ve got a singleuser password storing application(http://10kblogger.wordpress.com/2012/05/26/arestfulpasswordlockerwithdjangoandbackbonejspart3/). That’s not very secure or useful So now we’re going to support multiple users and lock downthe app so users must be authenticated. We’ll also lock down the REST API.

There’s now sufficient code that I’ll point you in the direction of certain files in the repository(https://github.com/boosh/pwlocker) for some of the more standard Django code for things such as userregistration.

Supporting multiple users

I created a registration form in `apps/users/forms.py` and updated `urls.py` so the django registration appwould use it. The registration form asks for users first and last names, email address, a user name and theirpassword (twice). This is pretty standard stuff.

Page 19: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 19/43

(http://10kblogger.files.wordpress.com/2012/05/registration.png)

Now users can register, we need to update the Password model in `apps/passwords/models.py` sopasswords are associated with the user that created them. Add a new foreign key todjango.contrib.auth.models.User:

We’ll use South to create a migration with `./manage.py schemamigration passwords –auto`. When it askswhat to do about defaults for the created_by column, just make it set the column to ’1′ (or any number youlike). Since we’re developing, and we don’t have legacy data to support, we can don’t need to maintain theintegrity of the database at this point.

Apply the migration with `./manage.py migrate passwords`.

Locking down the REST API

123456789

1011121314151617181920212223

from django.db import modelsfrom django.contrib.auth.models import User class Password(models.Model): """ Represents a username and password together with several other fields """ created_by = models.ForeignKey(User, related_name='+', editable title = models.CharField(max_length=200) username = models.CharField(max_length=200, blank=True) password = models.CharField(max_length=200) url = models.URLField(max_length=500, blank=True, verbose_name='Site URL') notes = models.CharField( max_length=500, blank=True) created_at = models.DateTimeField(auto_now_add=True, editable updated_at = models.DateTimeField(auto_now=True, editable= def __unicode__(self): return self.title

Page 20: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 20/43

We need to do two things with the API:

1. Require users to be authenticated to access it,2. Restrict data users can work with to only that which they have created.

Because Django REST API uses generic classbased views(https://docs.djangoproject.com/en/1.4/topics/classbasedviews/), it’s very simple to add these constraints.Simply create `apps/api/views.py` and add the following:

We’ve created a mixin to add the same logic to both classes. The mixin filters the queryset used by the APImethods so that the `created_by` field is the currently authenticated user. This prevents users fromaccessing data belonging to other users. It also sets the `created_by` field to the currently authenticated userfor CREATE (and UPDATE) operations. It’s very simple and very elegant.

The `permissions` tuple instructs Django REST API to require that users are authenticated in order toaccess those resources.

123456789

101112131415161718192021222324252627282930313233343536

from djangorestframework.mixins import ModelMixinfrom djangorestframework.permissions import IsAuthenticatedfrom djangorestframework.views import ListOrCreateModelView, InstanceModelView from apps.passwords.resources import PasswordResource class RestrictToUserMixin(ModelMixin): """ Mixin that restricts users to working with their own data """ def get_queryset(self): """ Only return objects created by the currently authenticated user. """ return self.resource.model.objects.filter(created_by=self def get_instance_data(self, model, content, **kwargs): """ Set the created_by field to the currently authenticated user. """ content['created_by'] = self.user return super(RestrictToUserMixin, self).get_instance_data(model, content, class PasswordListView(RestrictToUserMixin, ListOrCreateModelView): """ List view for Password objects. """ resource = PasswordResource permissions = (IsAuthenticated, ) class PasswordInstanceView(RestrictToUserMixin, InstanceModelView): """ View for individual Password instances """ resource = PasswordResource permissions = (IsAuthenticated, )

Page 21: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 21/43

And now update `apps/api/urls.py` to use these views instead of the others we had configured:

If you check out the API browser at http://localhost:8000/api/1.0/passwords/(http://localhost:8000/api/1.0/passwords/) you should notice you need to be logged in to access anything.Once you register and log in, you can use the API, but you can only view data associated with the accountyou’ve logged in as. However, if you try to use the frontend application, it will load the list of passwords,but CREATE, UPDATE and DELETE API access will be refused. You can see this if you have firebugopen, or if you reload the page. This is because Django is now enforcing CSRF tokens.

Before we fix this, lets go off on a little tangent. Unless you had firebug open, you may not have spottedthat the API was refusing your access – the app appeared to work correctly. So let’s add error handling tothe backbone.js application so it’ll be easier for us to know when we’ve fixed this issue.

To be alerted when deletions fail, update `staticfiles/js/passwords.js` as follows:

12345678

from django.conf.urls.defaults import patterns, url from views import PasswordListView, PasswordInstanceView urlpatterns = patterns('', url(r'passwords/$', PasswordListView.as_view(), name='passwords_api_root' url(r'passwords/(?P<id>[09]+)$', PasswordInstanceView.as_view(), name)

123456789

10111213141516171819202122232425262728293031

var Password = Backbone.Model.extend( ... remove: function(options) mergedOptions = wait: true; $.extend(mergedOptions, options); this.destroy(mergedOptions); ,...); var PasswordView = Backbone.View.extend( ... remove: function(event) event.stopImmediatePropagation(); event.preventDefault(); if (confirm("Are you sure you want to delete this entry?" this.model.remove(error: function(model, response) if (response.status == 403) alert("You don't have permission to delete that data" else alert("Unable to delete that data"); ); , ...);

Page 22: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 22/43

323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788

var PasswordListView = Backbone.View.extend( ... addNew: function(password, options) mergedOptions = wait: true; $.extend(mergedOptions, options); this.passwords.create(password, mergedOptions); return this; , updatePassword: function(passwordData, options) options = options || ; var password = this.passwords.get(passwordData.id); if (_.isObject(password)) // iterate through all the data in passwordData, setting it // to the password model for (var key in passwordData) // ignore the ID attribute if (key != 'id') password.set(key, passwordData[key]); // persist the change password.save(, options); this.passwords.sort(); , ...); var AppView = Backbone.View.extend( ... displayError: function(model, response) if (response.status == 403) alert("You don't have permission to edit that data"); else alert("Unable to create or edit that data. Please make sure you entered valid data." , handleModal: function(event) event.preventDefault(); event.stopImmediatePropagation(); var form = $('#passwordForm'); var passwordData = title: $(form).find('#id_title').val(), username: $(form).find('#id_username').val(), password: $(form).find('#id_password').val(), url: $(form).find('#id_url').val(), notes: $(form).find('#id_notes').val() ;

Page 23: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 23/43

We’re passing an error handler through the code to the CRUD methods. The handler checks the statuscode and displays an appropriate message. Now when we try to use the javascript app, we at least havesome feedback that things are failing.

We’ve also made backbone.js wait until it receives a response from the server before firing a ‘change’event and updating the UI (with the `wait: true` option), so the UI will only update on success.

Finally, to fix this CSRF issue, we need to add the `% csrf_token %` template tag to`templates/passwords/password_list.html`. Just add it after the ` form ` tag. This adds the actual tokento the template.

Then we need to tweak `staticfiles/js/passwords.js` so jQuery will send the token as a header with eachAJAX request.

Add the following at the end of the javascript code, just inside the final closing braces of the `$(function())` function:

Now, try creating several different users, create, edit and delete some data, and try to access data owned byother users. You should find that the API correctly restricts your access to data, and that the frontendjavascript app works correctly too.

Masking passwords

8990919293949596979899

100101102103104105106

if ($('#passwordModal').data('passwordId')) passwordData.id = $('#passwordModal').data('passwordId' this.passwordList.updatePassword(passwordData, error: else // add or update the password this.passwordList.addNew(passwordData, error: this // hide the modal $('#passwordModal').modal('hide'); return this; , ...);

12345

// Setup $.ajax to always send an XCSRFToken header: var csrfToken = $('input[name=csrfmiddlewaretoken]').val(); $(document).ajaxSend(function(e, xhr, settings) xhr.setRequestHeader('XCSRFToken', csrfToken); );

Page 24: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 24/43

For added security, we’ll mask the passwords in the UI, and only reveal them when the user moves theirmouse over them. This stops people being able to see your complete list of credentials if they can see yourscreen.

First, we’ll update the backbone.js app in `staticfiles/js/passwords.js` to set a new property on the modelcalled ‘maskedPassword’ which will just be a string of asterisks. We’ll also add some events for displayingand hiding the clear passwords:

Now update the ICanHaz template in `templates/passwords/password_list.html` to populate the passwordfield using `maskedPassword` instead of `password`:

123456789

10111213141516171819202122232425262728293031323334353637

var Password = Backbone.Model.extend( initialize: function() this.hidePassword(); , // display the password showPassword: function() this.set("maskedPassword": this.get('password')); , // hide the password hidePassword: function() this.set("maskedPassword": '********'); , ...); var PasswordView = Backbone.View.extend( ... events: "mouseover .password": "showPassword", "mouseout .password": "hidePassword", "click a.edit" : "editPassword", "click a.destroy" : "remove" , showPassword: function(event) event.stopImmediatePropagation(); this.model.showPassword(); , hidePassword: function(event) event.stopImmediatePropagation(); this.model.hidePassword(); , ...);

1234567

<script id="passwordRowTpl" type="text/html"> <td> <a href=" url " target="_blank"> title </a> </td> <td> username </td>

Page 25: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 25/43

Reload the frontend app and it should load the list correctly with the passwords masked. It should alsodisplay the clear password when you hover the mouse over the asterisks. However, CRUD operations willfail because backbone.js will submit the extra `maskedPassword` field to the API, and Django RESTframework will complain.

We’ve already got a way of ignoring certain fields submitted to the API, so just add `maskedPassword` tothe `ignore_fields` tuple in `apps/passwords/resources.py`:

Summary

We’re done for this iteration. The app now supports multiple users, displays error messages to users andmasks passwords.

In the penultimate part of this series (http://10kblogger.wordpress.com/2012/05/29/arestfulpasswordlockerwithdjangoandbackbonejspart5/), we’ll add the ability to share passwords between users.

COMMENTS 3 CommentsCATEGORIES Programming

A RESTful password locker with Django andbackbone.js part 3

We left our application loading data via the API (http://10kblogger.wordpress.com/2012/05/25/arestfulpasswordlockerwithdjangoandbackbonejspart2/). Now we need to support CRUD operations on it.

To support deletions, we need to tweak one setting in our Django settings.py file so. Bootstrap willperform CRUD operations against a URL without a trailing slash, but by default, Django will add a trailingslash to any URLs without one. To disable this behaviour, set `APPEND_SLASH = False` in settings.py.Also update your `apps/api/urls.py` file to remove the trailing slash. It should look like this:

89

1011121314

<td class="password"> maskedPassword </td> <td> notes </td> <td> <a href="#" class="edit" title="Edit this entry"><i class <a href="#" class="destroy" title="Delete this entry">< </td></script>

1234

class PasswordResource(ModelResource): ... ignore_fields = ('created_at', 'updated_at', 'id', 'maskedPassword' ...

Page 26: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 26/43

We need to update the template to include an ‘actions’ column to let users edit and delete rows. We’ll alsoadd a modal using Twitter bootstrap that will contain a form to let users add new entries or update existingones.

Update `templates/passwords/password_list.html` as follows:

123456789

101112

from django.conf.urls.defaults import patterns, url from djangorestframework.views import ListOrCreateModelView, InstanceModelViewfrom apps.passwords.resources import PasswordResource password_list = ListOrCreateModelView.as_view(resource=PasswordResource)password_instance = InstanceModelView.as_view(resource=PasswordResource) urlpatterns = patterns('', url(r'passwords/$', password_list, name='passwords_api_root' url(r'passwords/(?P[09]+)$', password_instance, name='passwords_api_instance')

123456789

10111213141516171819202122232425262728293031323334353637

% extends "base.html" % % block content %</pre><h1 class="pageheader">Passwords</h1><div id="app"><table class="table tablestriped"><thead><tr><th>Title</th><th>User name</th><th>Password</th><th>Notes</th><th>Actions</th></tr></thead><tfoot><tr><td colspan="5"><button class="btn btnprimary" datatoggle="modal"</tr></tfoot></table><div id="passwordModal" class="modal hide fade"><form id="passwordForm"<div class="modalheader"><button class="close" datadismiss="modal"<h3>Password Details</h3></div><div class="modalbody"> form </div><div class="modalfooter"><a class="btn" href="#" datadismiss <input class="btn btnprimary" type="submit" value="Save" /></</form></div></div><pre> % load verbatim % <! ICanHaz templates > % comment % Mustache and django both use tags for templates, so we need to use a custom template tag to output the mustache template exactly as it is.

Page 27: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 27/43

Since we want to include a form in the template, we need to create a Django view for this page so we caninclude it.

Edit `apps/passwords/url.py` as follows:

And create a simple view in `apps/passwords/view.py`:

383940414243444546474849505152535455565758596061626364

% endcomment % % verbatim %<script id="passwordRowTpl" type="text/html">// <![CDATA[<td> <a href=" url " target="_blank"> title </a></td> <td> username </td> <td class="password"> password </td> <td> notes </td> <td> <a href="#" class="edit" title="Edit this entry"><i class="iconpencil"></i></a> <a href="#" class="destroy" title="Delete this entry"><i class="iconremove"></i></a></td> // ]]></script> % endverbatim %% endblock %

1234567

from django.conf.urls.defaults import patterns, url from models import Password urlpatterns = patterns('apps.passwords.views', url(r'$', 'password_list', name='password_list'),)

123456789

10

from django.shortcuts import render_to_responsefrom django.template import RequestContext from forms import PasswordForm def password_list(request): context = RequestContext(request) form = PasswordForm() context.update('form': form) return render_to_response('passwords/password_list.html', context)

Page 28: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 28/43

Now we need to create the form – just a standard model form will do. Create `apps/passwords/forms.py`and add the following:

Now we can create our application using backbone.js. Update `staticfiles/js/passwords.js` to contain thefollowing:

1234567

from django.forms import ModelForm from models import Password class PasswordForm(ModelForm): class Meta: model = Password

123456789

10111213141516171819202122232425262728293031323334353637383940414243

// load the following using JQuery's document ready function$(function() // Password model var Password = Backbone.Model.extend( remove: function() this.destroy(); , validate: function(attrs) if (attrs.title.length == 0 || attrs.password.length == 0) return "Please enter a title and a password"; if (attrs.url) var re = /(http[s]?:\/\/)0,1(www\.)0,1[azAZ09\.\]+\.[azAZ]2,5[\.]0,1/; if (!re.test(attrs.url)) return "Please enter a valid URL"; ); // set up the view for a password var PasswordView = Backbone.View.extend( tagName: 'tr', events: "click a.edit" : "editPassword", "click a.destroy" : "remove" , editPassword: function(event) event.preventDefault(); event.stopImmediatePropagation(); // call back up to the main app passing the current model for it // to allow a user to update the details this.options.app.editPassword(this.model); ,

Page 29: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 29/43

4445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899

100

remove: function(event) event.stopImmediatePropagation(); event.preventDefault(); if (confirm("Are you sure you want to delete this entry?")) this.model.remove(); , render: function () // template with ICanHaz.js (ich) $(this.el).html(ich.passwordRowTpl(this.model.toJSON())); return this; ); // define the collection of passwords var PasswordCollection = Backbone.Collection.extend( model: Password, url: '/api/1.0/passwords/', // maintain ordering by password title comparator: function(obj1, obj2) return obj1.get('title').localeCompare(obj2.get(' ); /** * Manages the list of passwords and related data. Events are only for * child nodes of the generated element. */ var PasswordListView = Backbone.View.extend( tagName: 'tbody', /** * Constructor. Takes a reference to the parent view so we can invoke * methods on it. */ initialize: function(options) // instantiate a password collection this.passwords = new PasswordCollection(); this.passwords.bind('all', this.render, this); this.passwords.fetch(); , addOne: function(password) // pass a reference to the main application into the password view // so it can call methods on it this.$el.append(new PasswordView(model: password, app: this.options.app).render().el); return this; , addNew: function(password) this.passwords.create(password); return this;

Page 30: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 30/43

101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157

, updatePassword: function(passwordData) var password = this.passwords.get(passwordData.id); if (_.isObject(password)) // iterate through all the data in passwordData, setting it // to the password model for (var key in passwordData) // ignore the ID attribute if (key != 'id') password.set(key, passwordData[key]); // persist the change password.save(); this.passwords.sort(); , render: function() this.$el.html(''); this.passwords.each(this.addOne, this); return this; ); /** * View for the overall application. We need this because backbone can only * bind events for children of 'el'. * * In our template our modal is inside #app, so this class handles * interaction at the application level rather than strictly with a * collection of Passwords (that's the job of the PasswordListView). */ var AppView = Backbone.View.extend( el: '#app', events: "click #passwordForm :submit": "handleModal", "keydown #passwordForm": "handleModalOnEnter", "hidden #passwordModal": "prepareForm" , initialize: function() this.passwordList = new PasswordListView(app: this , render: function() this.$el.find('table').append(this.passwordList.render().el); , /** * Allows users to update an existing password *

Page 31: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 31/43

158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214

* @param Password password: A Password Model of the password to edit. */ editPassword: function(password) this.prepareForm(password.toJSON()); // store the password ID as data on the modal itself $('#passwordModal').data('passwordId', password.get( $('#passwordModal').modal('show'); , /** * Sets up the password form. * * @param object passwordData: An object containing data to use for the * form values. Any fields not present will be set to defaults. */ prepareForm: function(passwordData) passwordData = passwordData || ; var data = 'title': '', 'username': '', 'password': '', 'url': '', 'notes': '' ; $.extend(data, passwordData); var form = $('#passwordForm'); $(form).find('#id_title').val(data.title); $(form).find('#id_username').val(data.username); $(form).find('#id_password').val(data.password); $(form).find('#id_url').val(data.url); $(form).find('#id_notes').val(data.notes); // clear any previous references to passwordId in case the user // clicked the cancel button $('#passwordModal').data('passwordId', ''); , handleModal: function(event) event.preventDefault(); event.stopImmediatePropagation(); var form = $('#passwordForm'); var passwordData = title: $(form).find('#id_title').val(), username: $(form).find('#id_username').val(), password: $(form).find('#id_password').val(), url: $(form).find('#id_url').val(), notes: $(form).find('#id_notes').val() ; if ($('#passwordModal').data('passwordId')) passwordData.id = $('#passwordModal').data('passwordId' this.passwordList.updatePassword(passwordData);

Page 32: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 32/43

Explanation of the backbone.js code

We’ve added validation to the model although we don’t currently display the error messages to the user.We’ll address this at a future stage.

We’ve also added a method that allows us to delete instances.

The PasswordView listens to events for the ‘actions’ we added to the password template and allowsobjects to be edited and deleted.

We’ve added a comparator to the PasswordCollection so it stays nicely ordered by password title.

The PasswordListView handles updates to passwords as well as adding new ones.

Finally, the AppView listens to events related to submitting the password form and resetting the form whenthe modal is closed.

When passwords are edited, we keep track of which password is being edited by setting a data attribute onthe modal itself with the ID of the model to edit. This isn’t set if we need to add a new password. Everytime the modal is hidden, we reset the form and remove this ID data attribute in case users cancel themodal.

215216217218219220221222223224225226227228229230231232233234235236237238239

else // add or update the password this.passwordList.addNew(passwordData); // hide the modal $('#passwordModal').modal('hide'); return this; , handleModalOnEnter: function(event) // process the modal if the user pressed the ENTER key if (event.keyCode == 13) return this.handleModal(event); ); var app = new AppView(); app.render(););

Page 33: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 33/43

The modal is displayed and hidden thanks to Twitter Bootstrap(http://twitter.github.com/bootstrap/javascript.html#modals) purely due to classes and data attributes in theHTML.

One final tweak

Run the server with `./manage.py runserver` and everything should work – except updates. Bootstrapsubmits the complete model, but in our Django model we’ve defined several fields as uneditable. So thefinal thing we need to do is to edit `apps/passwords/resources.py` and make it drop the uneditable fields(created_at, updated_at and id) before validating the form, otherwise Django REST framework willcomplain that extra fields have been submitted. Edit the file so it contains the following:

123456789

101112131415161718192021222324252627282930313233343536373839

from djangorestframework.resources import ModelResourcefrom django.core.urlresolvers import reverse from models import Password class PasswordResource(ModelResource): model = Password # by default, django rest framework won't return the ID backbone.js # needs it though, so don't exclude it exclude = None ordering = ('title',) # django rest framework will overwrite our 'url' attribute with its own # that points to the resource, so we need to provide an alternative. include = ('resource_url',) ignore_fields = ('created_at', 'updated_at', 'id') def url(self, instance): """ Return the instance URL. If we don't specify this, django rest framework will return a generated URL to the resource """ return instance.url def resource_url(self, instance): """ An alternative to the 'url' attribute django rest framework will add to the model. """ return reverse('passwords_api_instance', kwargs='id': instance.id) def validate_request(self, data, files=None): """ Backbone.js will submit all fields in the model back to us, but some fields are set as uneditable in our Django model. So we need to remove those extra fields before performing validation. """ for key in self.ignore_fields: if key in data:

Page 34: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 34/43

Editing data now works:

(http://10kblogger.files.wordpress.com/2012/05/editing.png)

Summary

At this point our app supports the following:

When we load the page http://localhost:8000/passwords/ (http://localhost:8000/passwords/) backbone.jsloads our passwords via the API and renders a table containing the data.We’re able to add new entries with AJAX via our API.We can update entriesWe can delete entries

What’s not supported:

We are validating our backbone.js model on the client side and server side, but if validation fails wedon’t inform the user. We should provide them with this feedback.There’s no authentication and passwords aren’t associated with specific users.We need to support sharing passwords between users since that’s the purpose of this app.It’d be nice to be able to put passwords into categories or to tag them.It’d also be nice to mask passwords, and only reveal them when users hover over them.Users’ saved passwords are stored in plain text in the database. This is really bad, but if we encryptthem, how do we decrypt them when they’re shared between users? We’ll solve this later.

In our next iteration (http://10kblogger.wordpress.com/2012/05/28/arestfulpasswordlockerwithdjangoandbackbonejspart4/) we’ll relate passwords with users and add authentication to our API.

COMMENTS Leave a CommentCATEGORIES Programming

404142

del data[key] return super(PasswordResource, self).validate_request(data, files)

Page 35: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 35/43

A RESTful password locker with Django andbackbone.js part 2

In the first part of this series (http://10kblogger.wordpress.com/2012/05/25/arestfulpasswordlockerwithdjangoandbackbonejs/), we set up Django, created a basic Password model and created a RESTfulinterface for it. In this post, we’re going to set up backbone.js(http://documentcloud.github.com/backbone/) to load our data from our API.

Get the code

Don’t forget you can browse or clone the code from the github repo (https://github.com/boosh/pwlocker).

Javascript dependencies

Download the following dependencies and put them into a subdirectory inside a directory managed byDjango’s staticfiles app. I’ve used `contrib/backbone`.

backbonemin.js (http://documentcloud.github.com/backbone/backbonemin.js) – core libraryunderscoremin.js (http://documentcloud.github.com/underscore/underscoremin.js) – backbonedependencyjson2.js (https://github.com/douglascrockford/JSONjs/blob/master/json2.js) – backbone dependencyICanHaz.min.js (http://icanhazjs.com/) – for templating

If you don’t have jQuery or Zepto installed, you’ll need one of those too.

Also create a file called `contrib/js/passwords.js` – this is where we’ll actually create our backboneapplication. Add all of these to your template:

123456789

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js <! backbone ><script type="text/javascript" src=" STATIC_URL contrib/backbone/ICanHaz.min.js"<script type="text/javascript" src=" STATIC_URL contrib/backbone/json2.js"<script type="text/javascript" src=" STATIC_URL contrib/backbone/underscoremin.js"<script type="text/javascript" src=" STATIC_URL contrib/backbone/backbonemin.js" <script type="text/javascript" src=" STATIC_URL /js/passwords.js"

Page 36: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 36/43

Now we’re ready to start building the backbone part of the application. My goal for this stage is just to getsomething up and running where it’s pulling data in via the API.

Open http://localhost:8000/passwords/ (http://localhost:8000/passwords/) to view what we’ve currently got– data loaded via Django. We want to recreate this, but loading data using backbone.

Add the following to `contrib/js/passwords.js`:

123456789

101112131415161718192021222324252627282930313233343536373839404142434445

// load the following using JQuery's document ready function$(function() // Password model var Password = Backbone.Model.extend(); // set up the view for a password var PasswordView = Backbone.View.extend( render: function () // template with ICanHaz.js (ich) this.el = ich.passwordRowTpl(this.model.toJSON()); return this; ); // define the collection of passwords var PasswordCollection = Backbone.Collection.extend( model: Password, url: '/api/1.0/passwords/' ); // main app var AppView = Backbone.View.extend( tagName: 'tbody', initialize: function() // instantiate a password collection this.passwords = new PasswordCollection(); this.passwords.bind('all', this.render, this); this.passwords.fetch(); , render: function () // template with ICanHaz.js (ich) this.passwords.each(function (password) $(this.el).append(new PasswordView(model: password).render().el); , this); return this; ); var app = new AppView(); $('#app').append(app.render().el););

Page 37: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 37/43

There’s not much to it. Backbone.js represents models as keyvalue objects to which you can add methods.We don’t need anything fancy at this stage, so we just extend the default class. Collections contain thelogic for interacting with an endpoint via REST which we’ve already set up. We’ve also created a viewthat can render individual password objects, and one that renders the collection by delegating to thepassword object view.

One point worth noting in the above code is that there are no selectors in the backbone classes. This makesthem reusable and more robust. The only line that contains a reference to a selector is the very last linewhich appends the output of the rendering of the whole application to a specific element.

The last thing to do is to create some templates. We’re using ICanHaz which includes Mustache(http://mustache.github.com/). Update `templates/passwords/password_list.html` so it contains thefollowing:

123456789

10111213141516171819202122232425262728293031323334353637

% extends "base.html" % % block content % <h1 class="pageheader">Passwords</h1> <table class="table tablestriped" id="app"> <thead> <tr> <th>Title</th> <th>User name</th> <th>Password</th> <th>Notes</th> </tr> </thead> </table> % load verbatim % <! ICanHaz templates > % comment % Mustache and django both use tags for templates, so we need to use a custom template tag to output the mustache template exactly as it is. % endcomment % % verbatim % <script id="passwordRowTpl" type="text/html"> <tr> <td> <a href=" url " target="_blank"> title </a> </td> <td> username </td> <td class="password"> password </td> <td> notes </td> </tr> </script> % endverbatim %% endblock %

Page 38: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 38/43

Save everything and reload the page and you should see your page loading as before. If you’ve got firebuginstalled, open it, switch to the Net tab and reload the page to make sure that the data is being loadedremotely.

Summary

Now we’ve got a readonly application using backbone and Django, it’s time to move on and add CRUDsupport (http://10kblogger.wordpress.com/2012/05/26/arestfulpasswordlockerwithdjangoandbackbonejspart3/) to the app.

COMMENTS 1 CommentCATEGORIES Programming

A RESTful password locker with Djangoand backbone.js

In this series I’m going to show you how to use backbone.js (http://documentcloud.github.com/backbone/)with Django (https://www.djangoproject.com/). We’re going to be creating a password locker – a site thatwill let you keep track of your passwords and share them with colleagues. My development environment isFedora 16 so shell scripts are in bash.

Disclaimer: The site will evolve and to begin with will be very naive with passwords stored unencryptedin the database. Don’t use it in production

To implement a RESTful interface in Django, we’ll use Django REST framework (http://djangorestframework.org/) which makes creating RESTful interfaces from Django models super easy, and gives youa nice little browser so the APIs are selfdescribing.

Get the code

You can browse or clone the source code for this application from github(https://github.com/boosh/pwlocker). It’s opensourced under the MIT licence. I’ve also added tags formost of the pages so you can follow along with the tutorial if you wish.

Page 39: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 39/43

Set up

Let’s set up the django installation quickly. I cloned my standard foundational Django project and tweakedit. It’s just a basic Django package but with a few fabric (http://docs.fabfile.org/) scripts that make buildingthe project easy. It also includes user registration and authentication which we’ll need later as well asTwitter bootstrap (http://twitter.github.com/bootstrap/).

You can check out my code, or install Django yourself. Once you’re set up, create a ‘passwords’application inside an ‘apps’ directory with `mkdir apps/passwords && manage.py startapp passwordsapps/passwords` – I do this so code is better namespaced instead of having application packages and otherpackages all mixed together:

The model

To begin with we’ll create a simple model without any user authentication. Add the following toapps/passwords/models.py:

Run `./manage.py syncdb` to create your model in your database. We’ll use South(http://south.readthedocs.org/en/latest/) to manage migrations, so convert your new password app to southwith `./manage.py convert_to_south passwords`.

123456789

10111213141516171819202122

from django.db import models class Password(models.Model): """ Represents a username and password together with several other fields """ title = models.CharField(max_length=200) username = models.CharField(max_length=200, blank=True) password = models.CharField(max_length=200) url = models.URLField(max_length=500, blank=True, verbose_name='Site URL') notes = models.TextField( max_length=500, blank=True, help_text='Any extra notes') created_at = models.DateTimeField(auto_now_add=True, editable updated_at = models.DateTimeField(auto_now=True, editable= def __unicode__(self): return self.title

Page 40: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 40/43

Now we can start the server with `./manage.py runserver`.

Now we need to create a resource for this model so Django REST framework knows how to serve it up.This is pretty simple when using a basic model. Create apps/passwords/resources.py and add the following:

Finally, we need to set up some URLs. To namespace all the APIs together, create an API app with `mkdirapps/api && ./manage.py startapp api apps/api` and then wire up Django REST framework by editingapps/api/urls.py:

Also add the following to apps/passwords/urls.py to use Django’s classbased generic views to create a listview of passwords:

123456789

10111213141516171819202122232425262728

from djangorestframework.resources import ModelResourcefrom django.core.urlresolvers import reversefrom models import Password class PasswordResource(ModelResource): model = Password # by default, django rest framework won't return the ID backbone.js # needs it though, so don't exclude it exclude = None ordering = ('created_at',) # django rest framework will overwrite our 'url' attribute with its own # that points to the resource, so we need to provide an alternative. include = ('resource_url',) def url(self, instance): """ Return the instance URL. If we don't specify this, django rest framework will return a generated URL to the resource """ return instance.url def resource_url(self, instance): """ An alternative to the 'url' attribute django rest framework will add to the model. """ return reverse('passwords_api_instance', kwargs='id': instance.id)

123456789

101112

from django.conf.urls.defaults import patterns, url from djangorestframework.views import ListOrCreateModelView, InstanceModelViewfrom apps.passwords.resources import PasswordResource my_model_list = ListOrCreateModelView.as_view(resource=PasswordResource)my_model_instance = InstanceModelView.as_view(resource=PasswordResource) urlpatterns = patterns('', url(r'passwords/$', my_model_list, name='passwords_api_root' url(r'passwords/(?P<id>[09]+)/$', my_model_instance, name)

Page 41: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 41/43

Finally we need to include these two url.py files into the main urls.py file in your project root directory.Add the following to it inside your urlpatterns:

Now, we’ve got a nice browser for the API available at http://localhost:8000/api/1.0/passwords/(http://localhost:8000/api/1.0/passwords/). Open it up and check it out, and add some entries.

(http://10kblogger.files.wordpress.com/2012/05/djangorestframeworkpasswordlist_13379349076311.png)

That’s really cool. Not only has it saved us the repetitive effort of creating a REST interface ourselves, butwe can get on and populate it without using any browser extensions. It’s also validating our input using adefault model form for our model, so for example it’s ensuring that input in the site URL field validates as aURL.

Finally, before we can browse the list of passwords we need to create a template intemplates/passwords/password_list.html so the generic view can render the list:

12345678

from django.conf.urls.defaults import patterns, urlfrom django.views.generic import ListView from models import Password urlpatterns = patterns('', url(r'$', ListView.as_view(model=Password), name='password_list')

12

url(r'passwords/', include('apps.passwords.urls')),url(r'api/1.0/', include('apps.api.urls')),

1234567

% extends "base.html" % % block content % <h1 class="pageheader">Passwords</h1> <table class="table tablestriped"> <thead> <tr>

Page 42: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 42/43

Now, you can browse your passwords at http://localhost:8000/passwords/(http://localhost:8000/passwords/)

(http://10kblogger.files.wordpress.com/2012/05/examplecom_1337934961899.png)

Now we’ve got some data in the database, have a RESTful interface to access it and have a list ofpasswords on the front end, we’re ready to start ajaxing it with backbone.js. That’s in part 2(http://10kblogger.wordpress.com/2012/05/25/arestfulpasswordlockerwithdjangoandbackbonejspart2/).

COMMENTS 2 CommentsCATEGORIES Programming

London Python developer/AWS devop engineerBlog at WordPress.com. The Bueno Theme.

Follow

Follow “London Python developer/AWS devop engineer”

89

1011121314151617181920212223242526272829303132

<th>Title</th> <th>User name</th> <th>Password</th> <th>Notes</th> </tr> </thead> <tbody> % for password in object_list % <tr> <td>% if password.site_url % <a href=" password.site_url " target="_blank" password.title </a> % else % password.title % endif % </td> <td> password.username </td> <td> password.password </td> <td> password.notes </td> </tr> % endfor % </tbody> </table>% endblock %

Page 43: London Python Developer_AWS Devop Engineer

12/6/2014 London Python developer/AWS devop engineer

http://10kblogger.wordpress.com/ 43/43

Powered by WordPress.com