server package

Module contents

The server module implements an authentication server.

It is a WSGI app implemented in falcon and exposes an app variable that can be called from any WSGI server, like gunicorn.

A typical invocation is:

gunicorn -b 0.0.0.0:8005 server:create_app()

On import a sqlite database is initialized and logging is started.

For more information see the GitHub repo

The following attributes will be initialized to the values defined in the corresponding environment variables

server.DEBUGLEVEL

can be CRITICAL, ERROR, SUCCESS, INFO, DEBUG, TRACE. Defaults to DEBUG

server.DATABASE_FILE

path to database file, default to user.db

server.DATABASE_BACKOFF

number of seconds to wait between database connection retries, defaults to 1, doubles every retry.

server.DATABASE_RETRIES

number of times to retry a database connection. Defaults to 3.

server.create_app()[source]

An WSGI app factory.

Returns

falcon.API

When the app is created, a SQLAlchemy/sqlite database is initialized with server.get_sessionmaker().

The initialization will also create an admin user with server.add_superuser().

Submodules

server.server module

This module handles the http requests related to user and session management.

It also provides utility functions to initialize the database and admin user.

The attributes listed below configure the authserver and are initialized from environment variables with the same name.

Additional attributes are defined at the package level (in server) and in smtp .

Attributes:

The following environment variables define credentials but are not present in the module as attributes.

  • ADMIN_USER_FILE point to a file containing admin user name.

  • ADMIN_USER admin user name, overrides ADMIN_USER_FILE

  • ADMIN_PASSWORD_FILE point to a file containing admin password.

  • ADMIN_PASSWORD admin password, overrides ADMIN_PASSWORD_FILE

server.server.number(variable, default)[source]

Return the integer in the environment variable.

Parameters
  • variable (str) – the name of the environment variable

  • default (int) – the default value to return if variable is not defined

Returns

An integer value.

server.server.getvar(variable, default='<unknown>')[source]

Return the value of the environment variable.

Parameters
  • variable (str) – the name of the environment variable

  • default (str) – the default value to return if variable is not defined

Returns

A string.

server.server.getfile(variable, defaultfilename, default='Hi {name}, click {link}, Regards {website}')[source]

Return the contents of the file specified in the environment variable.

Parameters
  • variable (str) – the name of the environment variable

  • defaultfilename (str) – the filename to use if the variable is not defined

  • default (str) – the string to return if the file couldn’t be found

The default contains the following placeholders that can be used in the actual files as well

  • {name} the full name of the user

  • {link} a confirmation link to click

  • {website} the name of the application/website

Returns

A string.

server.server.DOMAIN = 'yourdomain.org'

Domain to be used in session cookie, e.g. yourdomain.org

server.server.APPLICATION = '/books'

Redirect locations for successful logon, e.g. /books

server.server.LOGINSCREEN = ''

Location of login screen, e.g. /books/login.html

server.server.CONFIRMREGISTRATION = 'https//server.yourdomain.org/auth/confirmregistration'

Base url in email registration confirmation, e.g. https://server.yourdomain.org/auth/confirmregistration

server.server.RESETPASSWORD = 'https://server.yourdomain.org/auth/resetpassword'

Base url in password reset request confirmation, e.g. https://server.yourdomain.org/auth/resetpassword

server.server.WEBSITE = '"Book Collection"'

The name of the app/website, e.g. Book Collection

server.server.SOFTTIMEOUT = 30

Session soft limit in minutes

server.server.HARDTIMEOUT = 480

Session hard limit in minutes

server.server.PWRESETTIMEOUT = 60

Password reset request confirmation limit in minutes

server.server.REGISTERTIMEOUT = 60

Registration confirmation limit in minutes

server.server.EMAILTEMPLATE_FORGOTPASSWORD = 'Hi {name}, click {link}, Regards {website}'

File containing email template for password reset confirmation email

See getfile() for allowed template variables inside the text.

server.server.EMAILTEMPLATE_REGISTER = 'Hi {name}, click {link}, Regards {website}'

File containing email template for registration confirmation email

See getfile() for allowed template variables inside the text.

server.server.SESSIONID_pattern = regex.Regex('[01-9a-f]{32}', flags=regex.V0)

Legal pattern for a session id and confirmation ids

server.server.PASSWORD_lower = regex.Regex('[a-z]', flags=regex.V0)

These characters are considered lowercase in passwords

server.server.PASSWORD_upper = regex.Regex('[A-Z]', flags=regex.V0)

These characters are considered uppercase in passwords

server.server.PASSWORD_digit = regex.Regex('[01-9]', flags=regex.V0)

These characters are considered digits in passwords

server.server.PASSWORD_special = regex.Regex('[ !|@#$%^&*()\\-_.,<>?/\\\\{}\\[\\]]', flags=regex.V0)

These characters are considered special in passwords

server.server.newpassword(password)[source]

Return a cryptographic hash of password as a string of hex digits.

The password is salted with 16 random bytes. The salt is prepended as 32 hex digits to the returned hash.

Parameters

password (str) – the password to hash.

Returns

A string consisting of 32 + 64 hexadecimal characters.

server.server.checkpassword(password, reference)[source]

Compare a plaintext password to a hashed reference.

The reference is a string of hex digits, the first 32 being the salt.

server.server.allowed_password(s)[source]

Check if a password meets the complexity criteria:

  • between 8 and 64 characters,

  • contain at least 1 lowercase, 1 uppercase, 1 digit and 1 special character

  • it may not contain characters outside those classes

  • character classes _are_ unicode aware

class server.server.ParameterSet(specs={})[source]

Bases: object

Defines allowable input parameters for a POST or GET request.

__init__(specs={})[source]

Creates a ParameterSet instance based on a dictionary of parameters.

Parameters

specs (dict) –

specifies a mapping name –> (ex, maxlength) name is a case sensitive name of an allowed input parameter

ex is either a string, a regular expression or a callable that specifies the validity of a value.

maxlength is an integer that specifies the maximum length of a parameter value

If ex is a string it is converted to a regular expression.

If ex is a callable it should return a boolean indicating the validity of a value.

check(params)[source]

Return true if all params are all allowed.

Parameters

params (dict) –

is a mapping name –> value, where name and value are strings Each value should match the requirements specified for ‘name’.

If params contains extra parameters or it is missing items it is considered invalid.

server.server.alchemyencoder(obj)[source]

A JSON encoder for SQLAlchemy declarative_base() objects, date and Decimal objects.

Base objects are returned as dict object with a key for each column. A column with a name equal to password is _not_ included. If a Base object has a user column, an extra key email is added that contains user.email.

date objects are returned as an isoformat string.

Decimal objects are returned as a float.

Parameters

obj (object) – an object to decode

Returns

A dict, str, float or None.

Like all default encoders for json it returns objects which are then encoded to json strings by the main encoder.

an example:

import json

jsonstring = json.dumps(someobject, default=alchemyencoder)
class server.server.User(**kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.Base

ORM representation of a User.

id

Primary key

email

user name (a valid email address)

password

hashed password

name

full name

superuser

role, true if superuser

created

timestamp

active

true if user account is enabled

attempts

number of failed login attempts

accessed

timestamp of last access

locked

timestamp of user lockout

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class server.server.Session(**kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.Base

ORM representation of a Session.

Any session will be deleted if the corresponding user is deleted.

id

primary key holds a guid

created

timestamp

softlimit

timestamp, session must show activity before this time to be renewed

hardlimit

timestamp, after this time the session will be removed regardless

userid

foreign key to User, on cascade delete is on

user

ORM link to User

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class server.server.PendingUser(**kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.Base

ORM representation of a PendingUser (a newly registered user awaiting confirmation).

id

primary key holds a guid

email

user name (a valid email address)

name

full name

password

hashed password

created

timestamp

expires

timestamp, after this time the session will be removed regardless

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

class server.server.PasswordReset(**kwargs)[source]

Bases: sqlalchemy.ext.declarative.api.Base

ORM representation of a PasswordReset event awaiting confirmation.

This PasswordReset will be deleted if the corresponding user is deleted.

__init__(**kwargs)

A simple constructor that allows initialization from kwargs.

Sets attributes on the constructed instance using the names and values in kwargs.

Only keys that are present as attributes of the instance’s class are allowed. These could be, for example, any mapped columns or relationships.

id

primary key holds a guid

created

timestamp

expires

timestamp, after this time the session will be removed regardless

userid

foreign key to User, on cascade delete is on

user

ORM link to User

server.server.max_body(limit)[source]

A falcon.before() hook to limit the size of request body.

Parameters

limit (int) – maximum size in bytes of the request body

Raises

falcon.HTTPPayloadTooLarge

Returns

a hook function

class server.server.LoginResource[source]

Bases: object

Routing endpoint that serves the action of a login form.

on_post(req, resp)[source]

Handle a logon POST request.

Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as www-formencoded parameters in the request body.

On success it will set the Location header to APPLICATION and return a session cookie.

On failure it will set the Location header to LOGINSCREEN.

It will always set the response status to falcon.HTTP_303.

Other Parameters
  • email – the username of the user (a valid email address)

  • password – the password

  • login – the literal text Login

Typically these parameters would correspond to input fields in an HTML form and a submit button with a name=Login attribute and send as input parameters in the body of the request.

class server.server.RegisterResource[source]

Bases: object

Routing endpoint that serves the action of a registration form.

on_post(req, resp)[source]

Handle a register POST request.

Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as www-formencoded parameters in the request body.

On success it will create a pending user request and send an email with a confirmation link.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN.

It will always set the response status to falcon.HTTP_303.

Other Parameters
  • email – the username of the user (a valid email address)

  • name – the full name of the user

  • password – the password ( 8 >= length <= 64, must contain at lease 1 lowercase, 1 uppercase, 1 digit and 1 special char.

  • password2 – must be identical to the password parameter

  • login – the literal text Register

Typically these parameters would correspond to input fields in an HTML form and a submit button with a name=Register attribute and send as input parameters in the body of the request.

class server.server.ForgotPasswordResource[source]

Bases: object

Routing endpoint that serves the action of a forgot password form.

on_post(req, resp)[source]

Handle a forgotpassword POST request.

Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as www-formencoded parameters in the request body.

On success it will create a password reset request and send an email with a confirmation link.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN.

It will always set the response status to ;attr:falcon.HTTP_303.

Other Parameters
  • email – the username of the user (a valid email address)

  • login – the literal text Forgot

Typically these parameters would correspond to input fields in an HTML form and a submit button with a name=Forgot attribute and send as input parameters in the body of the request.

class server.server.VerifySessionResource[source]

Bases: object

Routing endpoint that serves as the internal endpoint to verify the existence of a valid session.

on_post(req, resp)[source]

Handle a verifysession POST request.

Parameters
  • req – the request

  • resp – the response

Returns

  • email(str)

  • id(int)

  • name(str)

  • superuser(bool)

Return type

On success the response body will contain the following key=value pairs separated by newline characters

The method expects its input as www-formencoded parameters in the request body.

On success it will the session data and set the response status to falcon.HTTP_200.

On failure it will do nothing and return a response status of falcon.HTTP_404.

Other Parameters
  • email – the username of the user (a valid email address)

  • login – the literal text Forgot

This method should not be called from the browser. It is typically called by other backend servers to verify the validity of a session and get the email address, name and superuser status of that session.

It does not accept requests that have an X-Forwarded-Host header defined, so if the authserver and the backend server are positioned behind a reverse proxy that adds these headers (like traefik) all things will be fine.

session_active(session, sessionid)[source]

Check whether a sessionid represents an active session.

A session is active

If a session is found to be valid, its Session.softlimit is set to SOFTTIMEOUT minutes from now.

Parameters
  • session (DBSession) – SQLAlchemy session

  • sessionid (str) – a sessionid

Returns

a bytes object with a newline separated list of key=value pairs

On failure: None

Return type

On success

class server.server.LogoutResource[source]

Bases: object

Routing endpoint that serves the action of a logout form.

on_post(req, resp)[source]

Handle a logout POST request.

Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects no input parameters in the request body. A valid sessionid cookie should be present.

On success it will remove the session.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN.

It will always set the response status to ;attr:falcon.HTTP_303.

class server.server.ChoosePasswordResource[source]

Bases: object

Routing endpoint that serves the action of a choose new password form.

on_post(req, resp)[source]

Handle a choose password POST request.

Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as www-formencoded parameters in the request body. It also

On success it will change the password of the user.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN.

It will always set the response status to falcon.HTTP_303.

Other Parameters
  • resetid – an id that should be present in the PendingUser table

  • password – the password ( 8 >= length <= 64, must contain at lease 1 lowercase, 1 uppercase, 1 digit and 1 special char.

  • password2 – must be identical to the password parameter

  • choose – the literal text Choose

Typically these parameters would correspond to input fields in an HTML form and a submit button with a choose=Choose attribute and send as input parameters in the body of the request.

class server.server.StatsResource[source]

Bases: object

Routing endpoint that serves as the REST endpoint for user information overviews.

It can return information in JSON format on User, PendingUser, PasswordReset and Session.

Access is restricted to logged in users woth the superuser role.

on_post(req, resp, item)[source]

Handle a stats/{item} POST request, typically one initialed by an AJAX call.

Parameters
  • req – the request

  • resp – the response

  • item (str) – the kind of item for which a list should be returned

Returns

None

Even though this is a POST handler, it should have no input parameters in the body. The items that can be requested are:

  • users: to return a JSON encoded list of User objects

  • sessions: to return a JSON encoded list of Session objects

  • pendingusers: to return a JSON encoded list of PendingUser objects

  • passwordreset: to return a JSON encoded list of PasswordReset objects

On success the response body will contain a bytes object that is JSON data (in UTF-8 encoding). Note that any password attributes will be stripped form the output.

Example

JSON data is returned an object with a data attribute:

{
    "data": [
                {"id": 165675, "email": "abc@example.org", ...},
                {"id": 365625, "email": "def@example.org", ...},
                ...
            ]
}
class server.server.ConfirmRegistrationResource[source]

Bases: object

Routing endpoint that serves the registration confirmation link.

on_get(req, resp)[source]
Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as query parameters in the url.

On success it will create a User from the corresponding PendingUser.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN.

It will always set the response status to falcon.HTTP_303.

Other Parameters

confirmationid – an id that should be present in the PendingUser table

class server.server.ConfirmForgotPasswordResource[source]

Bases: object

Routing endpoint that serves the password reset confirmation link.

on_get(req, resp)[source]
Parameters
  • req – the request

  • resp – the response

Returns

None

The method expects its input as query parameters in the url.

On success it will create a User from the corresponding PendingUser.

On failure it will do nothing.

It will always set the Location header to LOGINSCREEN. If a pending request was found, ?choosepassword=id will be appended to the url. If not ?expired will be added.

It will always set the response status to falcon.HTTP_303.

Other Parameters

confirmationid – an id that should be present in the PasswordReset table

server.server.fetch_admin_params()[source]

Get admin variables from file or environment.

Enviroment variables overrule variables in files.

Returns

tuple(admin_user, admin_password)

Module level attributes referenced:

  • ADMIN_USER_FILE filename of file containing super user username (valid email address)

  • ADMIN_USER username (valid email address) of super user, will override ADMIN_USER_FILE

  • ADMIN_PASSWORD_FILE filename of file containing super user password in plaintext

  • ADMIN_PASSWORD super user password in plaintext, will override ADMIN_PASSWORD_FILE

server.server.add_superuser()[source]

Add superuser account to User table.

Will remove any user account with the same name along with any associated session.

server.server.set_sqlite_pragma(dbapi_connection, connection_record)[source]

Enable cascading constraint on the SQLAlchemy tables.

See: https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#foreign-key-support

server.server.get_sessionmaker(connection, timeout, retries)[source]

Create and initialize a global sqlalchemy.orm.sessionmaker.

Parameters
  • connection (str) – a sqlite connection string

  • timeout (int) – number of seconds to wait between connection retries. doubles with evert attempt.

  • retries (int) – number of times to retry a connection to the database.

Returns

bool

The sqlalchemy.orm.sessionmaker object is stored in the global DBSession.