Accounts app and 1.1 Road Map

165 views
Skip to first unread message

Stephen McDonald

unread,
Apr 24, 2012, 1:34:29 PM4/24/12
to mezzani...@googlegroups.com
Hi guys,

I've implemented the basic user/profile enhancements we talked about a month ago in this thread: http://groups.google.com/group/mezzanine-users/browse_thread/thread/55c10a0bd41284d6

There has always been basic account features in mezzanine.core - a single view/template/form for login and signup with an email and password field. While this served the most basic use of signing up and logging in, it missed some mandatory features, namely letting a user update their email/password (including forgotten passwords), and letting the developer extend the user info with extra fields. So I've moved those existing parts from mezzanine.core into the new mezzanine.accounts app. Here's a breakdown of everything:

- Removed the ACCOUNTS_ENABLED setting which would toggle the existing account features. This is now controlled by having mezzanine.accounts in INSTALLED_APPS.
- Separated the login/signup templates and views.
- Added handling for Django's AUTH_PROFILE_MODULE setting, which accepts the app.model name of a profile model to use. 
- Added signal for creating a related profile instance when a user is created.
- Added a view for the logged in user updating their info. 
- Added a view for displaying a user's profile (only when AUTH_PROFILE_MODULE is defined)
- Signup and update profile forms now include user fields (username, first name, last name) as well as any profile fields defined by the AUTH_PROFILE_MODULE model. User form in the admin will also include any profile fields.
- Lost password form/view - this is actually very similar to the signup verification feature recently added. It just sends an email to the user with login token that redirects them to update their password via the update profile form.
- I've added an auth backend for Mezzanine that accepts either username or email address along with the password for logging in. It also handles logins via access tokens for signup verification and password reset. This means you can login using email instead of password via the admin as well.

One thing I was considering but didn't do, is to hide all fields but the email address and password field on the signup/update forms, if a profile model isn't defined. That way there's a clean configuration point of "I just want logins" versus "I want full blown profiles".

Some other housekeeping bits that came out of it:

- Created a Html5Mixin class for forms. With the new account forms, there were 3 different instances of adding custom HTML5 attributes to form fields. The forms in mezzanine.forms still use their own as they're a bit more advanced, but the others use the Html5Mixin. By default it just adds the "required" attribute to required fields, which means you get nice client-side validation without any JavaScript. As previously, if the FORMS_USE_HTML5 setting is True, more enhancements are made such as custom field types which result is more client-side validation.
- Deprecated the try_url template tag. All it did was wrap url resolving in an exception and return an empty string if it failed, which was needed for certain admin urls to work during tests being run. I've also started using it to check for urls that legitimately might not exist, such as for an app not being installed. Anyway it turns out if you use the "as" arg of Django's normal url template tag, you can achieve the same result, so I've updated all instance of try_url will that, and left the template tag in with a warning.
- Removed pre-hashing of email addresses for gravatar URLs. Previously this existing on comments - the email address given would be hashed and stored in the DB to use for showing gravatar avatars. I've added gravatar avatars to the new profile view, and since there's nowhere to store the hash, I've changed the gravatar_url template tag to take an email and hash it on the fly, rather than the pre-computed hash.
- Made settings defined in TEMPLATE_ACCESSIBLE_SETTINGS that don't exist just return an empty string in templates rather than raising an exception.
- Made primary nav items in the admin navigation clickable - they just go to the first item in the dropdown lists which for the default setup makes sense: Content->Pages Sites->Sites Users->Users

This of course will make up one of the bigger features of 1.1. I'm not going to release that for a little while longer though, so there's plenty of time now to give feedback and collaborate on this new stuff. Please have a look at the code and if you think anything should be different, or have any idea around how this could be better, now is definitely the time to chime in. As always, all feedback is welcome.


The other things that might go into 1.1 are:

- Basic multi-tenancy. We briefly mentioned a while back about introducing a "current request" object using threadlocals into the code base, which would make certain things very easy to develop. What we could do with this is dynamically have access to the current host name, and use that to look up the site object to load anything for, instead of the single SITE_ID. This is just a high-level thought at the moment, but theoretically with that in place we could have multiple domains with their own content (or shared) running off a single Django instance. If this turns out to be a considerable amount of work (eg not as simple as I'm describing it right now), I don't think we will include it in 1.1.

- A fabric script for deployment. There's been mention before about including some default files for deploying Mezzanine such as web server configs. I've been working on a fabric deploy script that takes a vanilla Ubuntu image and by the end of it has a complete Mezzanine instance running using Postgres, Memcached, gunicorn, nginx, supervisord, virtualenv, etc. It still needs a bit more work, and is obviously very opinionated, but I think it'll make a starting point for a very good tool for people new to deploying Django projects in general, as I think a lot of newcomers to Mezzanine are. The nice thing about this is that it can always be an ongoing work in progress. Hopefully over time it'll grow into something very powerful.


--
Stephen McDonald
http://jupo.org

Josh Cartmell

unread,
Apr 24, 2012, 1:49:33 PM4/24/12
to mezzani...@googlegroups.com
Thanks for putting this together Steve, it sounds great!  I really like how Mezzanine keeps getting pushed to the next level of ease of use and functionality.

I will try to play with it a bit, and look at it more thoroughly later on today.  One small issue I did notice is that there should probably be some commas separating acceptable username characters here:
https://github.com/stephenmcd/mezzanine/compare/12ed9db25c...80d60a9542#L2R107

Stephen McDonald

unread,
Apr 24, 2012, 1:58:24 PM4/24/12
to mezzani...@googlegroups.com
Thanks Josh, well spotted. I've added commas to it now.

Josh Cartmell

unread,
Apr 25, 2012, 2:23:55 AM4/25/12
to mezzani...@googlegroups.com
Ok I'm trying to take a more thorough look now here are a few comments:

-I think that for a user to change their password or email address they should be required to enter their current password, otherwise if someone found their computer or somehow hijacked their session that person would be able to take complete control of the account.

-I like the idea of being able to only show email addresses and passwords signup/update forms as many sites that allow login really don't need to know your first and last name or any other info.  It might be nice to have a setting that controls this.

-I think it would also be good to have a way to control what parts of a user profile model are editable by users in the profile form as there are many use cases where it is good to be able to store info about users that you don't want them to be able to edit.  Potentially it could be in the Meta class of the profile model and work they way exclude and fields work for model forms.

-It would be nice if there was a way to enforce a minimum password complexity.

-I like the htlm5 validation stuff and I think it makes for a better ux.  Is it possible to enforce that two passwords match in the same way?

-If a user is already logged in should they be able to access the signup form? (currently they can) Should they be redirected to their profile form?

-This may not belong in this discussion but I was thinking about it since the roadmap for 1.1 was mentioned:
I just realized that I don't know too much about Django's built in caching middleware which I think Mezzanine basically uses.  Are there any considerations with this and the new accounts functionality?  Steve, or anyone else, could you explain a little bit about how Mezzanine uses the built in caching or point me towards a resource that might explain this.  Would we ever want to take Mezzanine's caching further than it already goes, or leave that up to end users?

-As you may already know I am in favor of multi tenancy and I'm glad to see that it is still on the radar!

-I like the fabric script and think that could develop into something really nice to allow people to get a tested and stable production environment up and running really quick.  I think it will help developers new to Django or Mezzanine have confidence that they can set up Mezzanine effectively.

Those are my thought for now, I will update the thread if I think of anything else.

Stephen McDonald

unread,
Apr 25, 2012, 2:37:33 AM4/25/12
to mezzani...@googlegroups.com
Thanks for the great feedback! Replies inline:

On Wed, Apr 25, 2012 at 4:23 PM, Josh Cartmell <joshc...@gmail.com> wrote:
Ok I'm trying to take a more thorough look now here are a few comments:

-I think that for a user to change their password or email address they should be required to enter their current password, otherwise if someone found their computer or somehow hijacked their session that person would be able to take complete control of the account.

Great idea, we'll do this.
 

-I like the idea of being able to only show email addresses and passwords signup/update forms as many sites that allow login really don't need to know your first and last name or any other info.  It might be nice to have a setting that controls this.

Cool - I was on the fence with this, but if it seems good we'll do it. My preference is to have it implicitly controlled by whether a profile model is set up.
 

-I think it would also be good to have a way to control what parts of a user profile model are editable by users in the profile form as there are many use cases where it is good to be able to store info about users that you don't want them to be able to edit.  Potentially it could be in the Meta class of the profile model and work they way exclude and fields work for model forms.

Unsure about the best way to implement this. My default is always to avoid settings whenever we can, there are so many already. Of course you can mark the fields as not editable on the profile model, but then they won't be editable via the admin either. Seems like a bit of an edge case as well - the profile fields are conceptually what makes up a person's profile. Perhaps the data should be stored elsewhere. Definitely needs more thought.
 

-It would be nice if there was a way to enforce a minimum password complexity.

Great idea. Not sure what configuring would look like. We could have a setting with a regex for the password format, but we'd need a second setting for the error message describing the format. Seems a bit clunky with two settings. Anyone have any thoughts on how this might be more cleanly configured? 

We could also take a more opinionated approach and just say that passwords need to be 6 chars or greater. There's a zillion arguments online around poorly implemented password restrictions. Why bother the user with saying that can't do such and such in their password. If we force required non-alpha characters and the like, then it can be argued we're enforcing passwords that can't be remembered. Some would say the only concrete requirement should be the minimum length.
 

-I like the htlm5 validation stuff and I think it makes for a better ux.  Is it possible to enforce that two passwords match in the same way?

Not sure if HTML5 forms support this, but if it does we can easily add it to the signup/update form in its __init__
 

-If a user is already logged in should they be able to access the signup form? (currently they can) Should they be redirected to their profile form?

Not too fussed about this. Someone might like to create a second account, don't see a reason to prevent it. Anyone feel strongly about this?

Josh Cartmell

unread,
Apr 26, 2012, 4:51:43 AM4/26/12
to mezzani...@googlegroups.com
Hey Steve I would like to push back a bit on your comments about user profiles.  My reading of the django docs is that the AUTH_PROFILE_MODULE defines a model which is in many ways an extension of the User model.  The user model definitely contains stuff that you wouldn't want users to be able to edit themselves so it would make sense to me for the user profile to also contain information that you wouldn't want the user to directly be able to edit.  I think that every time I have used AUTH_PROFILE_MODULE in a django project I have stored information that I wouldn't want the user editing so it does not seem like an edge case from my point of view.  I agree that not creating unnecessary settings is desirable but to me the ability to control the profile form seems worthy of a setting.

As far as implementation there could be a setting that defines what to use for ProfileFieldsForm that defaults to what you already have.  Then if a developer wanted they could override it with their own form class where they could specify their own meta.  Or there could just be a setting called PROFILE_FORM_EXCLUDE (or whatever) which would accept a tuple and then in ProfileFieldsForm we could have:
exclude = (get_profile_user_fieldname(),) + settings.PROFILE_FORM_EXCLUDE

It also might be nice to make ProfileForm overridable via a setting as well so that it could be subclassed to add custom clean methods or other logic.

I also still think it would be nice to continue to be able to have the option of a signup form that only asks for an email address, username and password, even if you wanted to collect other "profile" information.  On the flipside it may be that you would want to collect first and last name but not have a profile model defined because you don't want to collect anything else.  I think a bit more thought may be needed on that issue.

Thanks for the continued hard work.

Stephen McDonald

unread,
Apr 26, 2012, 5:08:12 AM4/26/12
to mezzani...@googlegroups.com
Ok those are some good arguments.

If you look at the ProfileForm class in there, it's actually a ModelForm for the User model, with fields injected from a ModelForm for the Profile model.

What if we just made both those form class configurable - then the developer could specify their own versions of these, which subclass the base form classes, allowing them to define what fields they want on them.

Another approach might be to have a single setting with a whitelist of editable fields, that include both User and Profile fields to use, something like:

ACCOUNT_EDITABLE_FIELDS = ("first_name", "last_name", "bio") # where bio is a Profile field.

I kinda like the field list approach - more simple and less room to break things.

What do you think?

Josh Cartmell

unread,
Apr 26, 2012, 6:25:15 AM4/26/12
to mezzani...@googlegroups.com
I like both for different reasons.  I like overriding the forms because it gives the developer more power and ability to customize things to their needs and with good defaults things would continue to "just work", but I agree that it would introduce possible breakage if they subclassed things incorrectly or overrode something important.  I would like to hear from some other people if they see potential use cases in specifying the form class.  If we go this route we could document how you could subclass the forms to exclude certain fields and add some custom validation

I like the idea of a setting because it would be very simple and easy to use and wouldn't require any extra knowledge about how django and Mezzanine in particular handles the forms.  If we go that route would we want to make it possible to specify a whitelist or a blacklist (like model forms) or just stick with a whitelist?

I'm torn between greater flexibility and ease of use.  I think that custom validation could be useful in some cases, but I would really like to hear some more people thoughts and opinions.

Stephen McDonald

unread,
Apr 26, 2012, 7:09:42 AM4/26/12
to mezzani...@googlegroups.com
Maybe we just start out with the field settings and see how that goes.

It could just be a list of non-editable fields, since I imagine the most common case would be to expose most if not all of the fields.

Josh Cartmell

unread,
Apr 26, 2012, 2:08:34 PM4/26/12
to mezzani...@googlegroups.com
I think that sounds like a good idea.

I imagine that it won't require much work so if later on down the road it seems like overriding the forms would be useful not much will be lost.

My only concern with a blacklist is that if someone added a sensitive field and forgot to add it to the blacklist (I'm thinking about that because of the recent RoR shenanigans, obviously this would be nothing like that attack vector).  I'm not saying we shouldn't do it, we just need to clearly document that it is a blacklist and that you need to keep that in mind when dealing with sensitive data.

Alexander Hill

unread,
Apr 26, 2012, 11:21:16 PM4/26/12
to mezzani...@googlegroups.com
Hi Stephen,

This is my first post to mezzanine-users so firstly, thanks for putting out a great product and especially for being actively engaged with your users. It's a really reassuring sign when choosing an open-source project to develop on.

We could also take a more opinionated approach and just say that passwords need to be 6 chars or greater. There's a zillion arguments online around poorly implemented password restrictions. Why bother the user with saying that can't do such and such in their password. If we force required non-alpha characters and the like, then it can be argued we're enforcing passwords that can't be remembered. Some would say the only concrete requirement should be the minimum length.

Dropbox recently posted an article about their new password strength estimator, "zxcvbn", on their tech blog: http://tech.dropbox.com/?p=165. It contains some really interesting discussion and analysis of password strength and entropy. The short version is that long passphrases without special characters or numbers are both more secure and easier for users to remember. Mezzanine is in a position to help to turn the tide against "one number and one capital letter" passwords, even if just a tiny bit. :)

Maybe zxcvbn could be added to the default template too.

Cheers,
Alex

Stephen McDonald

unread,
Apr 27, 2012, 7:09:24 AM4/27/12
to mezzani...@googlegroups.com
Thanks Alex, great read. Adding the JS to the templates would be cool if we can make it really subtle.

Stephen McDonald

unread,
May 11, 2012, 12:58:28 PM5/11/12
to mezzani...@googlegroups.com
I've made most of the suggested improvements to the accounts app:

- ACCOUNTS_MIN_PASSWORD_LENGTH setting - defaults to 6.
- ACCOUNTS_PROFILE_FORM_EXCLUDE_FIELDS setting - list of field names on the profile and user models that'll be excluded from the signup/update forms. You can use this to strip right down to just username/email/password
Reply all
Reply to author
Forward
0 new messages