User Guide ========== The objective of security is protection of information from theft or corruption, while allowing the information to remain accessible to its intended users. Ticket ------ Ticket is a short packet of bytes generated by a network server for a client, which can be delivered to itself as a means of authentication or proof of authorization, and cannot easily be forged. :py:class:`~wheezy.security.crypto.ticket.Ticket` has the following characteristics: * It is valid for certain period of time, in particular it has an explicitly set expiration time. * Its value is signed to prove its authenticity. * It is encrypted to protect sensitive information. * It has noise to harden forgery. :py:class:`~wheezy.security.crypto.ticket.Ticket` can be instantiated by passing the following arguments: * ``max_age`` - period of time (in seconds) this Ticket is considered valid. * ``salt`` - a random sequence that hardens against ticket forgery. It is prepended to the validation key and the encryption key. * ``digestmod`` - hash algorithm used with HMAC (Hash-based Message Authentication Code) to sign ticket. Defaults to SHA1. * ``cypher`` - cryptography algorithm. Defaults to AES128. * ``options`` - a dictionary that holds the following configuration values: ``CRYPTO_VALIDATION_KEY`` (used by signature) and ``CRYPTO_ENCRYPTION_KEY`` (used by encryption). Validation and Encryption Keys ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Keys used for validation and encryption are ensured to be at least of 320 bits length. The :py:meth:`~wheezy.security.crypto.ticket.ensure_strong_key` function appends HMAC signature to the key. If the cryptography library is not available you will see a warning message:: Ticket: cypher not available Although Ticket continues to function even cryptography library is not installed it strongly recommended to use cryptography in a production environment. Thread Safety ~~~~~~~~~~~~~ Ticket does not alter it state once initialized. It is guaranteed to be thread safe. Typical Use Case ~~~~~~~~~~~~~~~~ Here is typical use case when all possible configuration attributes are used:: from wheezy.security.crypto.comp import aes192 from wheezy.security.crypto.comp import sha1 from wheezy.security.crypto import Ticket options = { 'CRYPTO_VALIDATION_KEY': 'LkLlYR5WbTk54kaIgJOp', 'CRYPTO_ENCRYPTION_KEY': 'rH64daeXBZdgrR7WNawf' } ticket = Ticket( max_age=1200, salt='CzQnV0KazDKElBYiIC2w', digestmod=sha1, cypher=aes192, options=options) The ``ticket`` instance can be shared application wide. The ``encode`` / ``decode`` methods are used in the following way:: protected_value = ticket.encode('hello') assert 'hello' == ticket.decode(protected_value) In case the validity of a ticket cannot be confirmed, the ``decode`` method returns ``None``. Extensibility ~~~~~~~~~~~~~ Ticket ``cypher`` can be any callable that satisfies the following contract: * Initialization is called with encryption key. Returned object must be a factory for the actual algorithm instance. * Algorithm factory must return new algorithm via simple callable with no arguments. * Algorithm implementation must support two methods: ``encrypt(value)`` and ``decrypt(value)``. Principal --------- :py:class:`~wheezy.security.principal.Principal` is a container of user specific security information. It includes the following attributes: * ``id`` - user identity, e.g. number `755345`, UUID `f102a87b-ee36-4a2e-97de-8f803f470867` or whatever else is valid to look up a user quickly in your application. * ``roles`` - a list of authorized user roles, e.g. `user`, `manager`, etc. * ``alias`` - a user friendly name, display name, etc. This can be something like `John Smith`, etc. * ``extra`` - any string you would like to hold in security context. Here is a sample how to instantiate new Principal:: principal = Principal( id='125134788', roles=['user'], alias='John Smith') :py:class:`~wheezy.security.principal.Principal` supports the following methods: * ``dump`` - converts instance to a string. * ``load`` - reverse operation to ``dump``. You can use ``Ticket`` to securely pass ``Principal`` across network boundaries. Combining them both you can introduce an authentication/authorization cookie to your application. Authorization ------------- Authorization specifies access rights to resources and provides access control in particular to your application. You are able to request authorization by decorating your method with :py:meth:`~wheezy.security.authorization.authorized`. Here is a typical use case:: from wheezy.security import authorized class MyBusinessLogic(object): principal = None @authorized def cancel_transfer(self, id): return True @authorized(roles=('operator',)) def approve_transfer(self): return True Note that the :py:meth:`~wheezy.security.authorization.authorized` decorator requires the object to supply a ``principal`` attribute of type :py:class:`~wheezy.security.principal.Principal`. If a caller is not authorized to perform a requested operation, a :py:class:`~wheezy.security.errors.SecurityError` exception is raised. See :py:meth:`~wheezy.security.authorization.authorized` for more details.