to require a logged in user, use the @login_required decorator • This decorator will redirect a user to the login page if they aren’t authenticated 3 from django.contrib.auth.decorators import login_required ... @login_required def game_delete(request, game_id): ...
link to users in a prominent location • Once user is logged in, always show a menu or header that provides • A display that the user is logged in • A way to access user profile • A way to change user’s password • A way to log out • Users have come to expect that this information and functionality is available at the top of every page • Include identifying information about the user (email or password) so that they know which account they are logged in as 4
of information that contains the state of a set of network interactions • They are usually set up during the initial interaction and expire after some period of time • Sessions are expected to live for longer than a single interaction in order to accelerate subsequent interactions by reducing setup time and the amount of information that must be gathered in order to perform actions • Contain information about the current state of a series of interactions, and are used primarily when the communication protocol is stateless • Identified using a key or ID that is passed back and forth between the client and server to enable the server to identify the client identity • For web interactions, sessions are maintained on the server in some form of simple and fast storage mechanism • Database, memory cache, key/value storage, disk 6
a browser will store the session key or identifier in a cookie and send the session ID with every HTTP request • On the server, the session keeps track of the user identity and access credentials to enable actions to be authorized without requiring login for every interaction • Web sessions can contain additional data, but its best to keep as little as possible in the session on the server since it needs to be stored and retrieved for every HTTP request • If the expiration time of a session is long enough, a user can come back to the web application at a later date and not have to log in • The longer the expiration time, the less secure the site, since an attacker only needs the session cookie (available on the user’s computer) in order to access the site as that user 7
but can be configured to use in- process memory, Memcached, local disk, or other storage locations • A Django session contains the user that was authenticated • If a user hasn’t authenticated, there will still be a session, with an AnonymousUser • When a session expires, the user must log in again • When a web browser makes an HTTP request, if it provides a session identifier via a cookie, Django will look up the session based on the session key, retrieve it, and reuse it • Django sessions require cookies in the web browser • The HTTPRequest object that is passed to a view contains both a user object (cached in the session) and the session data • Django provides an API for managing session data 8 request.session.get('foo', False) request.session['bar'] = 123 del request.session['baz']
in auth application that can be used to build a user sign-up view • Significant differences from a standard ModelForm: • username is checked for existence prior to creating new user • password has 2 fields, to ensure user has entered it properly; Form checks to ensure fields are the same before saving • When creating User Signup view, ensure it isn’t protected by @login_required (for obvious reasons) • After successfully creating user, log them in • authenticate method takes username and password arguments and retrieves user instance • login method takes request and user arguments and sets the user as the authenticated user for the session • Be sure to redirect the user to a sensible page upon sign-up 9
account information works just like any other ModelForm • Only requirement is that users should only ever be allowed to view and update their own account information • Instead of identifying the user ID in the URL, user identification should be handled in the view, using request.user • Update urls.py: • Create a UserForm, but limit the fields presented to the user: 11 from django.contrib.auth.models import User ... class UserForm(ModelForm): class Meta: model = User fields = ('email', 'first_name', 'last_name', ) ... url(r'^user/$', 'softball.views.user_edit', name='user_edit'), ...
the user from request and instantiate the form using the user as the instance • Redirect the user to the user_edit view on success, including a message 12 @login_required def user_edit(request): user = request.user if request.method == 'POST': form = forms.UserForm(request.POST, instance=user) if form.is_valid(): form.save() messages.success(request, u'User profile updated') return redirect('user_edit') else: form = forms.UserForm(instance=user) return TemplateResponse(request, 'softball/user_edit.html', { 'user': user, 'form': form, })
• Permissions belong to Models • By default, each Model will have 3 permissions defined: add_<model>, change_<model>, delete_<model> • Permission names are always <application_name>.<permission_name> • Permissions can be checked by querying user • For views, use the @permission_required decorator (like @login_required) to ensure user has permission to access view • If action in a view must be protected, use user.has_perm • In template, perms context variable can be queried for the user’s permissions • Best practice: hide buttons for actions user is not allowed to use 13 user.has_perm('foo.add_bar') user.has_perm('foo.change_bar') user.has_perm('foo.delete_bar')
Groups are collections of permissions to which a User can be assigned • They can be managed entirely through Django admin • Permissions can be assigned to Groups • Group membership can be selected for each User • Permissions assigned to a Group are inherited by the Users that are members of that group • When user.has_perm is used, it checks the permissions assigned to the user and those that are assigned to a User’s Groups 17
Model to a User, add an owned_by field • This will require an update to database tables • To make this work properly requires changes to all create and edit views to assign request.user to owned_by field for instance being created or edited 18 owned_by = django.db.models.ForeignKey('auth.User', related_name='teams') ALTER TABLE softball_team ADD COLUMN "owned_by_id" INTEGER NOT NULL REFERENCES "auth_user" ("id"); ALTER TABLE softball_player ADD COLUMN "owned_by_id" INTEGER NOT NULL REFERENCES "auth_user" ("id"); ALTER TABLE softball_game ADD COLUMN "owned_by_id" INTEGER NOT NULL REFERENCES "auth_user" ("id"); ALTER TABLE softball_roster ADD COLUMN "owned_by_id" INTEGER NOT NULL REFERENCES "auth_user" ("id"); ALTER TABLE softball_statistic ADD COLUMN "owned_by_id" INTEGER NOT NULL REFERENCES "auth_user" ("id");
be shown to users, since it is internal functionality • Make sure ModelForms exclude owned_by field • For ModelForms that enable editing of Foreign Key relationships, add user to __init__() method • Save user locally in form instance • Update queryset for fields to only allow selection of related objects that belong to the indicated user • Override save() method to assign owned_by if necessary • For FormSets, override ModelForm and FormSet used to support user argument