Authentication

Authentication in EVE-SRP was designed from the start to allow for multiple different authentication systems and to make it easy to integrate it with an existing authentication system.

As an exercise in how to write your own authentication plugin, let’s write one that doesn’t rely on an external service. There are four classes to override for your authentication plugin, User, Group, AuthMethod and AuthForm.

Let’s start with subclassing User. This class is mapped to an SQL table using SQLAlchemy’s declarative extension (more specifically, the Flask-SQLAlchemy plugin to Flask). The parent class automatically sets up the table name and inheritance mapper arguments for you, so all you need to do is provide the id attribute that links your class with the parent class and an attribute to store the password hash. In the example, we’re using the pbkdf2 package to provide the password hashing. We also have a checking method to make life easier for us later.

from evesrp import db
from evesrp.auth import User
from pbkdf2 import pbkdf2_bin


class LocalUser(User):
    id = db.Column(db.Integer, db.ForeignKey('user.id', primary_key=True))
    password = db.Column(db.LargeBinary(256), nullable=False)
    salt = db.Column(db.LargeBinary(256), nullable=False)

    def __init__(self, username, password):
        self.name = username
        self.salt = None
        self.password = pbkdf2_bin(password.encode('utf-8'), self.salt,
                iterations=100000)

    def check_password(self, password):
        key = pbkdf2_bin(password.encode('utf-8'), self.salt,
                iterations=100000)
        matched = 0
        for a, b in zip(self.password, key):
            matched |= ord(a) ^ ord(b)
        return matched == 0

    @classmethod
    def authmethod(cls):
        return LocalAuth

In addition, we override User.authmethod() to tell which authentication method class to use for the actual login process.

AuthMethod subclasses have three and a half methods they can subclass to customize themselves. AuthMethod.form() returns the AuthForm subclass that represents the necessary fields. AuthMethod.login() performs the actual login process. As part of this, it is passed an instance of the class given by AuthMethod.form() with the submitted data via the form argument. Finally, some login methods need a secondary view, for example, OpenID needs a destination to redirect to and process the arguments passed to along with the redirect. The AuthMethod.view() method is an optional method AuthMethod subclasses can implement to process/present a secondary view. It can be accessed at /login/<AuthMethod.__name__.lower()> and accepts the GET and POST HTTP verbs.

from evesrp.auth import AuthForm, AuthMethod
from flask import redirect, url_for
from flask.ext.wtf import Form
from sqlalchemy.orm.exc import NoResultFound
from wtforms.fields import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, EqualTo


class LocalLoginForm(AuthForm):
    username = StringField('Username', validators=[InputRequired()])
    password = PasswordField('Password', validators=[InputRequired()])
    submit = SubmitField('Log In')


class LocalCreateUserForm(Form):
    username = StringField('Username', validators=[InputRequired()])
    password = PasswordField('Password', validators=[InputRequired(),
            EqualTo('password_repeat', message='Passwords must match')])
    password_repeat = PasswordField(
            'Repeat Password', validators=[InputRequired()])
    submit = SubmitField('Log In')


class LocalAuth(AuthMethod):
    def form(self):
        return LocalLoginForm()

    def login(self, form):
        # form has already been validated
        try:
            user = LocalUser.query.filter_by(name=form.username.data).one()
        except NoResultFound:
            flash("No user found with that username.", 'error')
            return redirect(url_for('login.login'))
        if user.check_password(form.password.data):
            self.login_user(user)
            redirect(request.args.get('next') or url_for('index'))
        else:
            flash("Incorrect password.", 'error')
            redirect(url_for('login.login'))

    def view(self):
        form = LocalCreateUserForm()
        if form.validate_on_submit():
            user = LocalUser(form.username.data, form.password.data)
            db.session.add(user)
            db.session.commit()
            self.login_user(user)
            return redirect(url_for('index'))
        return render_template('form.html', form=form)

API Documentation

class evesrp.auth.AuthMethod(admins=None, name='Base Authentication', **kwargs)[source]
form()[source]

Return a form class to login with.

login(form)[source]

Process a validated login form.

You must return a valid response object.

static login_user(user)[source]

Signal to the authentication systems that a new user has logged in.

Handles sending the flask.ext.principal.identity_changed signal and calling flask.ext.login.login_user() for you.

Parameters:user (User) – The user that has been authenticated and is logging in.
safe_name[source]

Normalizes a string to be a valid Python identifier (along with a few other things).

Specifically, all letters are lower cased, only ASCII characters, and whitespace replaced by underscores.

Returns:The normalized string.
Rtype str:
view()[source]

Optional method for providing secondary views.

evesrp.views.login.auth_method_login() is configured to allow both GET and POST requests, and will call this method as soon as it is known which auth method is meant to be called. The path for this view is /login/self.safe_name/, and can be generated with url_for('login.auth_method_login', auth_method=self.safe_name).

The default implementation redirects to the main login view.

class evesrp.auth.models.Entity(name, authmethod, **kwargs)[source]

Private class for shared functionality between User and Group.

This class defines a number of helper methods used indirectly by User and Group subclasses such as automatically defining the table name and mapper arguments.

You should not inherit fomr this class directly, and should instead inherit from either User or Group.

authmethod

The name of the AuthMethod for this entity.

entity_permissions

Permissions associated specifically with this entity.

has_permission(permissions, division_or_request=None)[source]

Returns if this entity has been granted a permission in a division.

If division is None, this method checks if this group has the given permission in any division.

Parameters:
  • permissions (iterable) – The series of permissions to check
  • division_or_request – The division to check. May also be None or an SRP request.
Rtype bool:
name

The name of the entity. Usually a nickname.

class evesrp.auth.models.User(name, authmethod, **kwargs)[source]

User base class.

Represents users who can submit, review and/or pay out requests. It also supplies a number of convenience methods for subclasses.

actions

Actions this user has performed on requests.

admin

If the user is an administrator. This allows the user to create and administer divisions.

get_id()[source]

Part of the interface for Flask-Login.

groups

Groups this user is a member of

is_active()[source]

Part of the interface for Flask-Login.

is_anonymous()[source]

Part of the interface for Flask-Login.

is_authenticated()[source]

Part of the interface for Flask-Login.

pilots

Pilots associated with this user.

requests

Requests this user has submitted.

submit_divisions()[source]

Get a list of the divisions this user is able to submit requests to.

Returns:A list of tuples. The tuples are in the form (division.id, division.name)
Return type:list
class evesrp.auth.models.Group(name, authmethod, **kwargs)[source]

Base class for a group of users.

Represents a group of users. Usable for granting permissions to submit, evaluate and pay.

permissions

Synonym for entity_permissions

users

User s that belong to this group.

class evesrp.auth.models.Permission(division, permission, entity)[source]
__init__(division, permission, entity)[source]

Create a Permission object granting an entity access to a division.

division

The division this permission is granting access to

entity

The Entity being granted access

permission

The permission being granted.

class evesrp.auth.models.Division(name)[source]

A reimbursement division.

A division has (possibly non-intersecting) groups of people that can submit requests, review requests, and pay out requests.

division_permissions

All Permissions associated with this division.

name

The name of this division.

permissions[source]

The permissions objects for this division, mapped via their permission names.

requests

Request s filed under this division.

transformers

A mapping of attribute names to Transformer instances.

class evesrp.auth.models.Pilot(user, name, id_)[source]

Represents an in-game character.

__init__(user, name, id_)[source]

Create a new Pilot instance.

Parameters:
  • user (User) – The user this character belpongs to.
  • name (str) – The name of this character.
  • id (int) – The CCP-given characterID number.
name

The name of the character

requests

The Requests filed with lossmails from this character.

user

The User this character belongs to.

class evesrp.auth.testauth.TestAuth(api_key=None, **kwargs)[source]
list_groups(user=None)[source]

Return a list of groups descriptors.

If user is None, return _all_ groups. Otherwise, return the groups a member is part of.

class evesrp.auth.testauth.TestUser(username, auth_id, authmethod, groups=None, **kwargs)[source]
class evesrp.auth.testauth.TestGroup(name, auth_id, authmethod)[source]
class evesrp.auth.bravecore.BraveCore(client_key, server_key, identifier, url='https://core.braveineve.com', **kwargs)[source]
class evesrp.auth.bravecore.CoreUser(name, authmethod, **kwargs)[source]
class evesrp.auth.bravecore.CoreGroup(name, authmethod, **kwargs)[source]

Table Of Contents

Previous topic

Developers Guide to EVE-SRP

Next topic

Killmail Handling

This Page