User Authentication and Authorization

The built-in LoginUser class is the base class for all User objects that Snakelets uses. If you declare on a page or in a snakelet that the session requires a logged in user, Snakelets will only allow a visitor to access that page if there is a logged in user on the session. You have to code that login mechanism yourself, but Snakelets then uses the authenticated user object for various security related things.

The LoginUser class

The class contains the following properties, and some methods, that will be explained below:

Passwords: the password attribute of the User object contains a secure hash of the user's userid+password (not the password itself!). You must use the checkPassword() method to check if the given password is equal to the password of that user. Note that the hash is a binary string and that you may have to convert it to hex codes or similar if you want to store or print it.

Configuring the Secure Hash function: The default hash function is the sha-module (SHA-1). If you need to use a different function, for instance MD5 (maybe because of backward compatibility with older Snakelet versions, or when you can't or don't want to reset all passwords) you'll have to specify that like this in your webapp init code:

    import md5
    import snakeserver.user
    snakeserver.user.securehash=md5

Note that you must set it to a module or object that has the new() method to create a new hashing object (Python's md5 and sha modules both have this). Also note that this is a global setting that will affect all webapps on the server. (Even if you set this in the init code of only one of your webapps). The password hash function was changed in Snakelets 1.44 from md5 to sha. If you have passwords on storage created by an older Snakelets version, you will have to reset all passwords. Conversion is impossible, because the original passwords cannot be reconstructed from the existing md5 hash.

Authentication methods

Snakelets supports browser-based HTTP authentication (you know, where the browser shows a username/password dialog). Use the 'httpbasic' or 'httpdigest' method for this. You can also use a login page to do the authentication ('loginpage' method). If user authentication is required (because the session type is 'user', or there is an authorizedRole defined for the page), Snakelets will start using the configured authentication method to validate the user. The following methods are available:

How to specify the method to use?

Checking the user and Logging in, and out

When you use any of the above authentication methods, and the session type is suitable, Snakelets will automatically log in an authenticated user (see above). You won't have to do anything by yourself when using the 'http'-methods, but you will have to do a little extra work if you use 'loginpage'.

Your login page must have a session (session type may not be 'no'), and a form containing a 'login' and a 'password' input field, and the form's action url should be the login page itself (self.getURL() is handy). Inherit the page from the snakeserver.user.LoginPage baseclass to gain access to the attemptLogin(returnpage) method. Call this method first thing in your login page, and you're set. If the login succeeds, it will redirect you to the return page (you can override this by passing it as argument to the authentication method). If the login fails, your Ypage simply continues. (The attemptLogin method does some trickery to remember the page you must return to after a succesful login!) The login logic looks first for form field values for the 'login' and 'password' form fields, if these are not available it also looks on the request Context object for the same attributes. This is useful to be able to set the login/password by another means than a user form (such as a remember-me-cookie).

Password check: If you use one of Snakelet's built-in authentication methods (as described above) you still have to provide a special method in your webapp's init file, that Snakelets will use to check username+password when it needs to:
  def authorizeUser(authmethod, url, username, password, request)
It must return a snakeserver.user.LoginUser object (or subclass thereof), or a set/sequence of privilege names, or None if the given user+password is not correct. The url parameter is the page that was requested, and the request parameter is the Request object (you can get ip addresses or session information from it, for instance).

Logging out: Somewhere in your code you have to logout the user and delete the session associated with it. You must use the logoutUser and deleteSession functions for that, see the next paragraph. Note that when using http authentication it is not possible to truly log a user out; the web browser continues to send the previously accepted credentials, and the user is logged in again at the next request...

Custom login mechanism: If you program your own authentication/login mechanism, you won't need any of the stuff described above (no authmethod and so on, because you are programming this yourself). In your custom login code you will need the following methods:

See the 'account' example webapp for a possible implementation of all this. The 'manage' webapp also uses authentication with a login page. In the 'test' webapp is an example of httpbasic authentication.

Access control using user privileges

You can require a user to simply log in using a password, and be done with it, but every authenticated user in Snakelets can also have a set of "roles" (or "privileges"). When creating a User object you can provide a list or set of privileges (usually just strings/names, such as "administrator", "moderator"...) that are given to this user. You can then grant or deny access to pages based on these privileges. The user object has a property (attribute) privileges that contains a set of the privileges the user has, and some methods to aid checking for privileges:

You can set required privileges on several levels:

'Not authorized' message

When you're not authorized to view a page, you'll get a HTTP 403 error page 'You don't have the required privileges to access this page' (or a similar message).

Accessing the user object

You can get the currently logged in user from the Session object, using something like: req.getSession().getLoggedInUser() or, when in an Ypage, using the shortcut object: self.User. You will get the currently logged-in user object, or None when nobody is logged in.

Shared authentication and Single signon

With the help of the "SharedAuth" plugin (available separately in the Plugins package; this function is not available by default) it is possible to do shared authentication (share a single authentication method over multiple webapps). This means that if you have some central user administration datastore, you only have to program an authentication method in one webapp that uses this datastore to authenticate users. It can then register other webapps to be allowed to 'share' the authentication method. Now the other webapps can ask the SharedAuth plugin to do the authentication for them, and it will use the authentication method of the first webapp. For more information on how to do this, see the source code of the SharedAuth plugin.

Single singon is different: this means that a user only logs in on one webapp, and other webapps will now share this login information, so that the user doesn't have to log in for each webapp. The latter is the normal behavior, because every webapp has its own session (for security and containment). By setting the sharedSession config item to True (default is False), you can tell the server to use the global shared session. Every webapp that has this on True will not use its own session, but instead share a single global session, and so they share the session object, the session user information, and the session context. Because it may be dangerous, Snakelets prints a warning when starting up when it detects that a webapp uses the shared session. There are several things that are important:

  1. Because the session context is shared, all items that are placed on it are also shared. Check for conflicting items! Webapps will overwrite eachothers attributes if you don't use different names.
  2. The user object is also shared. If you're using only LoginUser objects, you're safe, but if you are using your own subclassed User object with custom methods or properties, you may be in trouble, unless every webapp that shares the global session knows about this and sets the correct user object. It's probably better to define a User class that is then also shared across all your webapps that have a shared session.
  3. Logging out in one webapp means that the user is logged out in all other webapps with shared session too (this is to be expected, because logging in does the same!).
  4. A shared session is only shared on a single virtual host. The same webapps but on a different vhost will not share the session from another vhost.
  5. Note: shared session does NOT mean that different users share a session. Every user ofcourse has her own private session!

The 'shared1' and 'shared2' example webapps show how you can use the shared session to achieve single signon.

Snakelets manual - Back to index