Просмотр исходного кода

Merge pull request #434 from justanr/ISS-431-interface-documentation

Iss 431 interface documentation
Peter Justin 7 лет назад
Родитель
Сommit
715f2d8978

+ 10 - 0
docs/accountmanagement.rst

@@ -0,0 +1,10 @@
+.. _accountmanagement:
+
+Account Management
+==================
+
+.. autoclass:: flaskbb.core.auth.password.ResetPasswordService
+    :members:
+
+.. autoclass:: flaskbb.core.auth.activation.AccountActivator
+    :members:

+ 5 - 0
docs/api.rst

@@ -6,4 +6,9 @@ API
 .. toctree::
    :maxdepth: 2
 
+   coreexceptions
    models
+   registration
+   authentication
+   accountmanagement
+   tokens

+ 80 - 0
docs/authentication.rst

@@ -0,0 +1,80 @@
+.. _authentication:
+
+Authentication
+==============
+
+FlaskBB exposes several interfaces and hooks to customize authentication and
+implementations of these. For details on the hooks see :ref:`hooks`
+
+Authentication Interfaces
+-------------------------
+
+.. module:: flaskbb.core.auth.authentication
+
+.. autoclass:: AuthenticationManager
+   :members:
+   :undoc-members:
+
+.. autoclass:: AuthenticationProvider
+   :members:
+   :undoc-members:
+
+.. autoclass:: PostAuthenticationHandler
+   :members:
+   :undoc-members:
+
+.. autoclass:: AuthenticationFailureHandler
+   :members:
+   :undoc-members:
+
+
+Authentication Provided Implementations
+---------------------------------------
+
+.. module:: flaskbb.auth.services.authentication
+.. autoclass:: DefaultFlaskBBAuthProvider
+.. autoclass:: MarkFailedLogin
+.. autoclass:: BlockUnactivatedUser
+.. autoclass:: ClearFailedLogins
+.. autoclass:: PluginAuthenticationManager
+
+
+Reauthentication Interfaces
+---------------------------
+
+.. module:: flaskbb.core.auth.authentication
+
+.. autoclass:: ReauthenticateManager
+   :members:
+   :undoc-members:
+
+.. autoclass:: ReauthenticateProvider
+   :members:
+   :undoc-members:
+
+.. autoclass:: PostReauthenticateHandler
+   :members:
+   :undoc-members:
+
+.. autoclass:: ReauthenticateFailureHandler
+   :members:
+   :undoc-members:
+
+
+Reauthentication Provided Implementations
+-----------------------------------------
+
+.. module:: flaskbb.auth.services.reauthentication
+.. autoclass:: DefaultFlaskBBReauthProvider
+.. autoclass:: ClearFailedLoginsOnReauth
+.. autoclass:: MarkFailedReauth
+.. autoclass:: PluginReauthenticationManager
+
+
+Exceptions
+----------
+
+.. module:: flaskbb.core.auth.authentication
+
+.. autoexception:: StopAuthentication
+.. autoexception:: ForceLogout

+ 13 - 0
docs/coreexceptions.rst

@@ -0,0 +1,13 @@
+.. _coreexceptions:
+
+.. module:: flaskbb.core.exceptions
+
+Core Exceptions
+===============
+
+These are exceptions that aren't specific to any one part of FlaskBB
+and are used ubiquitously.
+
+.. autoexception:: BaseFlaskBBError
+.. autoexception:: ValidationError
+.. autoexception:: StopValidation

+ 3 - 1
docs/hooks.rst

@@ -17,6 +17,9 @@ A hook needs a hook specification which are defined in
 :mod:`flaskbb.plugins.spec`. All hooks have to be prefixed with
 ``flaskbb_`` and template hooks with ``flaskbb_tpl_``.
 
+Be sure to also check out the :ref:`api` documentation for interfaces that
+interact with these plugins in interesting ways.
+
 
 Python Hooks
 ------------
@@ -77,7 +80,6 @@ FlaskBB Form Hooks
 
 .. autofunction:: flaskbb_form_new_post_save
 .. autofunction:: flaskbb_form_new_post
-
 .. autofunction:: flaskbb_form_new_topic
 .. autofunction:: flaskbb_form_new_topic_save
 

+ 32 - 0
docs/registration.rst

@@ -0,0 +1,32 @@
+.. _registration:
+
+Registration
+============
+
+These interfaces and implementations control the user registration flow in
+FlaskBB.
+
+Registration Interfaces
+-----------------------
+
+.. module:: flaskbb.core.auth.registration
+
+.. autoclass:: UserRegistrationInfo
+
+.. autoclass:: UserValidator
+    :members:
+
+.. autoclass:: UserRegistrationService
+    :members:
+
+
+Registration Provided Implementations
+-------------------------------------
+
+.. module:: flaskbb.auth.services.registration
+
+.. autoclass:: UsernameRequirements
+.. autoclass:: UsernameValidator
+.. autoclass:: UsernameUniquenessValidator
+.. autoclass:: EmailUniquenessValidator
+.. autoclass:: RegistrationService

+ 20 - 0
docs/tokens.rst

@@ -0,0 +1,20 @@
+.. _tokens:
+
+Tokens
+======
+
+.. module:: flaskbb.core.tokens
+
+.. autoclass:: TokenActions
+    :members:
+
+.. autoclass:: Token
+
+.. autoclass:: TokenSerializer
+    :members:
+
+.. autoclass:: TokenVerifier
+    :members:
+
+.. autoexception:: TokenError
+    :members:

+ 4 - 0
flaskbb/auth/services/activation.py

@@ -17,6 +17,10 @@ from ...email import send_activation_token
 
 
 class AccountActivator(_AccountActivator):
+    """
+    Default account activator for FlaskBB, handles the activation
+    process through email.
+    """
 
     def __init__(self, token_serializer, users):
         self.token_serializer = token_serializer

+ 36 - 1
flaskbb/auth/services/authentication.py

@@ -12,9 +12,10 @@
 import logging
 from datetime import datetime
 
+from pytz import UTC
+
 import attr
 from flask_babelplus import gettext as _
-from pytz import UTC
 from werkzeug import check_password_hash
 
 from ...core.auth.authentication import (AuthenticationFailureHandler,
@@ -31,11 +32,20 @@ logger = logging.getLogger(__name__)
 
 @attr.s(frozen=True)
 class FailedLoginConfiguration(object):
+    """
+    Used to configure how many failed logins are accepted until an account
+    is temporarily locked out and how long to temporarily lock the account
+    out for.
+    """
     limit = attr.ib()
     lockout_window = attr.ib()
 
 
 class BlockTooManyFailedLogins(AuthenticationProvider):
+    """
+    Pre authentication check to block a login from an account that has too
+    many failed login attempts in place.
+    """
 
     def __init__(self, configuration):
         self.configuration = configuration
@@ -65,6 +75,15 @@ class BlockTooManyFailedLogins(AuthenticationProvider):
 
 
 class DefaultFlaskBBAuthProvider(AuthenticationProvider):
+    """
+    This is the default username/email and password authentication checker,
+    locates the user based on the identifer passed -- either username or email
+    -- and compares the supplied password to the hash connected to the matching
+    user (if any).
+
+    Offers protection against timing attacks that would rely on the difference
+    in response time from not matching a password hash.
+    """
 
     def authenticate(self, identifier, secret):
         user = User.query.filter(
@@ -81,6 +100,10 @@ class DefaultFlaskBBAuthProvider(AuthenticationProvider):
 
 
 class MarkFailedLogin(AuthenticationFailureHandler):
+    """
+    Failure handler that marks the login attempt on the user and sets the
+    last failed date when it happened.
+    """
 
     def handle_authentication_failure(self, identifier):
         user = User.query.filter(
@@ -93,6 +116,10 @@ class MarkFailedLogin(AuthenticationFailureHandler):
 
 
 class BlockUnactivatedUser(PostAuthenticationHandler):
+    """
+    Post auth handler that will block a user that has managed to pass the
+    authentication check but has not actually activated their account yet.
+    """
 
     def handle_post_auth(self, user):
         if not user.activated:  # pragma: no branch
@@ -106,12 +133,20 @@ class BlockUnactivatedUser(PostAuthenticationHandler):
 
 
 class ClearFailedLogins(PostAuthenticationHandler):
+    """
+    Post auth handler that clears all failed login attempts from a user's
+    account.
+    """
 
     def handle_post_auth(self, user):
         user.login_attempts = 0
 
 
 class PluginAuthenticationManager(AuthenticationManager):
+    """
+    Authentication manager relying on plugin hooks to manage the authentication
+    process. This is the default authentication manager for FlaskBB.
+    """
 
     def __init__(self, plugin_manager, session):
         self.plugin_manager = plugin_manager

+ 6 - 0
flaskbb/auth/services/password.py

@@ -17,6 +17,10 @@ from ...email import send_reset_token
 
 
 class ResetPasswordService(_ResetPasswordService):
+    """
+    Default password reset handler for FlaskBB, manages the process through
+    email.
+    """
 
     def __init__(self, token_serializer, users, token_verifiers):
         self.token_serializer = token_serializer
@@ -24,6 +28,7 @@ class ResetPasswordService(_ResetPasswordService):
         self.token_verifiers = token_verifiers
 
     def initiate_password_reset(self, email):
+
         user = self.users.query.filter_by(email=email).first()
 
         if user is None:
@@ -38,6 +43,7 @@ class ResetPasswordService(_ResetPasswordService):
         )
 
     def reset_password(self, token, email, new_password):
+
         token = self.token_serializer.loads(token)
         if token.operation != TokenActions.RESET_PASSWORD:
             raise TokenError.invalid()

+ 16 - 0
flaskbb/auth/services/reauthentication.py

@@ -24,6 +24,10 @@ logger = logging.getLogger(__name__)
 
 
 class DefaultFlaskBBReauthProvider(ReauthenticateProvider):
+    """
+    This is the default reauth provider in FlaskBB, it compares the provided
+    password against the current user's hashed password.
+    """
 
     def reauthenticate(self, user, secret):
         if check_password_hash(user.password, secret):  # pragma: no branch
@@ -31,12 +35,20 @@ class DefaultFlaskBBReauthProvider(ReauthenticateProvider):
 
 
 class ClearFailedLoginsOnReauth(PostReauthenticateHandler):
+    """
+    Handler that clears failed login attempts after a successful
+    reauthentication.
+    """
 
     def handle_post_reauth(self, user):
         user.login_attempts = 0
 
 
 class MarkFailedReauth(ReauthenticateFailureHandler):
+    """
+    Failure handler that marks the failed reauth attempt as a failed login
+    and when it occurred.
+    """
 
     def handle_reauth_failure(self, user):
         user.login_attempts += 1
@@ -44,6 +56,10 @@ class MarkFailedReauth(ReauthenticateFailureHandler):
 
 
 class PluginReauthenticationManager(ReauthenticateManager):
+    """
+    Default reauthentication manager for FlaskBB, it relies on plugin hooks
+    to manage the reauthentication flow.
+    """
 
     def __init__(self, plugin_manager, session):
         self.plugin_manager = plugin_manager

+ 25 - 0
flaskbb/auth/services/registration.py

@@ -24,12 +24,20 @@ __all__ = (
 
 @attr.s(hash=False, repr=True, frozen=True, cmp=False)
 class UsernameRequirements(object):
+    """
+    Configuration for username requirements, minimum and maximum length
+    and disallowed names.
+    """
     min = attr.ib()
     max = attr.ib()
     blacklist = attr.ib()
 
 
 class UsernameValidator(UserValidator):
+    """
+    Validates that the username for the registering user meets the minimum
+    requirements (appropriate length, not a forbidden name).
+    """
 
     def __init__(self, requirements):
         self._requirements = requirements
@@ -58,6 +66,9 @@ class UsernameValidator(UserValidator):
 
 
 class UsernameUniquenessValidator(UserValidator):
+    """
+    Validates that the provided username is unique in the application.
+    """
 
     def __init__(self, users):
         self.users = users
@@ -77,6 +88,9 @@ class UsernameUniquenessValidator(UserValidator):
 
 
 class EmailUniquenessValidator(UserValidator):
+    """
+    Validates that the provided email is unique in the application.
+    """
 
     def __init__(self, users):
         self.users = users
@@ -93,6 +107,17 @@ class EmailUniquenessValidator(UserValidator):
 
 
 class RegistrationService(UserRegistrationService):
+    """
+    Default registration service for FlaskBB, runs the registration information
+    against the provided validators and if it passes, creates the user.
+
+    If any of the provided
+    :class:`UserValidators<flaskbb.core.auth.registration.UserValidator>`
+    raise a :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+    then the register method will raise a
+    :class:`StopValidation<flaskbb.core.exceptions.StopValidation>` with all
+    reasons why the registration was prevented.
+    """
 
     def __init__(self, validators, user_repo):
         self.validators = validators

+ 33 - 0
flaskbb/core/auth/activation.py

@@ -15,10 +15,43 @@ from ..._compat import ABC
 
 
 class AccountActivator(ABC):
+    """
+    Interface for managing account activation in installations that require
+    a user to activate their account before using it.
+    """
+
     @abstractmethod
     def initiate_account_activation(self, user):
+        """
+        This method is abstract.
+
+        Used to extend an offer of activation to the user. This may take any
+        form, but is recommended to take the form of a permanent communication
+        such as email.
+
+        This method may raise :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+        to communicate a failure when creating the token for the user to
+        activate their account with (such as when a user has requested a token
+        be sent to an email that is not registered in the installation or
+        the account associated with that email has already been activated).
+
+        :param user: The user that the activation request applies to.
+        :type user: :class:`User<flaskbb.user.models.User>`
+        """
         pass
 
     @abstractmethod
     def activate_account(self, token):
+        """
+        This method is abstract.
+
+        Used to handle the actual activation of an account. The token
+        passed in is the serialized token communicated to the user to use
+        for activation. This method may raise
+        :class:`TokenError<flaskbb.core.tokens.TokenError>` or
+        :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+        to communicate failures when parsing or consuming the token.
+
+        :param str token: The raw serialized token sent to the user
+        """
         pass

+ 178 - 3
flaskbb/core/auth/authentication.py

@@ -16,6 +16,8 @@ class StopAuthentication(BaseFlaskBBError):
     """
     Used by Authentication providers to halt any further
     attempts to authenticate a user.
+
+    :param reason str: The reason why authentication was halted
     """
 
     def __init__(self, reason):
@@ -26,6 +28,8 @@ class StopAuthentication(BaseFlaskBBError):
 class ForceLogout(BaseFlaskBBError):
     """
     Used to forcefully log a user out.
+
+    :param reason str: The reason why the user was force logged out
     """
 
     def __init__(self, reason):
@@ -34,22 +38,69 @@ class ForceLogout(BaseFlaskBBError):
 
 
 class AuthenticationManager(ABC):
+    """
+    Used to handle the authentication process. A default is implemented,
+    however this interface is provided in case alternative flows are needed.
+
+    If a user successfully passes through the entire authentication process,
+    then it should be returned to the caller.
+    """
 
     @abstractmethod
     def authenticate(self, identifier, secret):
         """
-        Manages the entire authentication process in FlaskBB.
+        This method is abstract.
+
+        :param str identifier: An identifer for the user, typically this is
+            either a username or an email.
+        :param str secret: A secret to verify the user is who they say they are
+        :returns: A fully authenticated but not yet logged in user
+        :rtype: :class:`User<flaskbb.user.models.User>`
 
-        If a user is successfully authenticated, it is returned
-        from this method.
         """
         pass
 
 
 class AuthenticationProvider(ABC):
+    """
+    Used to provide an authentication service for FlaskBB.
+
+    For example, an implementer may choose to use LDAP as an authentication
+    source::
+
+        class LDAPAuthenticationProvider(AuthenticationProvider):
+            def __init__(self, ldap_client):
+                self.ldap_client = ldap_client
+
+            def authenticate(self, identifier, secret):
+                user_dn = "uid={},ou=flaskbb,ou=org".format(identifier)
+                try:
+                    self.ldap_client.bind_user(user_dn, secret)
+                    return User.query.join(
+                            UserLDAP
+                        ).filter(
+                            UserLDAP.dn==user_dn
+                        ).with_entities(User).one()
+                except Exception:
+                    return None
+
+    During an authentication process, a provider may raise a
+    :class:`StopAuthentication<flaskbb.core.auth.authentication.StopAuthentication>`
+    exception to completely, but safely halt the process. This is most useful
+    when multiple providers are being used.
+    """
 
     @abstractmethod
     def authenticate(self, identifier, secret):
+        """
+        This method is abstract.
+
+        :param str identifier: An identifer for the user, typically this is
+            either a username or an email.
+        :param str secret: A secret to verify the user is who they say they are
+        :returns: An authenticated user.
+        :rtype: :class:`User<flaskbb.user.models.User>`
+        """
         pass
 
     def __call__(self, identifier, secret):
@@ -57,9 +108,26 @@ class AuthenticationProvider(ABC):
 
 
 class AuthenticationFailureHandler(ABC):
+    """
+    Used to post process authentication failures, such as no provider returning
+    a user or a provider raising
+    :class:`StopAuthentication<flaskbb.core.auth.authentication.StopAuthentication>`.
+
+    Postprocessing may take many forms, such as incrementing the login attempts
+    locking an account if too many attempts are made, forcing a reauth if
+    the user is currently authenticated in a different session, etc.
+
+    Failure handlers should not return a value as it will not be considered.
+    """
 
     @abstractmethod
     def handle_authentication_failure(self, identifier):
+        """
+        This method is abstract.
+
+        :param str identifier: An identifer for the user, typically this is
+            either a username or an email.
+        """
         pass
 
     def __call__(self, identifier):
@@ -67,9 +135,35 @@ class AuthenticationFailureHandler(ABC):
 
 
 class PostAuthenticationHandler(ABC):
+    """
+    Used to post process authentication success. Post authentication handlers
+    recieve the user instance that was returned by the successful
+    authentication rather than the identifer.
+
+    Postprocessors may decide to preform actions such as flashing a message
+    to the user, clearing failed login attempts, etc.
+
+    Alternatively, a postprocessor can decide to fail the authentication
+    process anyways by raising
+    :class:`StopAuthentication<flaskbb.core.auth.authentication.StopAuthentication>`,
+    for example a user may successfully authenticate but has not yet activated
+    their account.
+
+    Cancelling a successful authentication will cause registered
+    :class:`AuthenticationFailureHandler<flaskbb.core.auth.authentication.AuthenticationFailureHandler>`
+    instances to be run.
+
+    Success handlers should not return a value as it will not be considered.
+    """
 
     @abstractmethod
     def handle_post_auth(self, user):
+        """
+        This method is abstact.
+
+        :param user: An authenticated but not yet logged in user
+        :type user: :class:`User<flaskbb.user.model.User>`
+        """
         pass
 
     def __call__(self, user):
@@ -77,9 +171,23 @@ class PostAuthenticationHandler(ABC):
 
 
 class ReauthenticateManager(ABC):
+    """
+    Used to handle the reauthentication process in FlaskBB. A default
+    implementation is provided, however this is interface exists in case
+    alternative flows are desired.
 
+    Unlike the AuthenticationManager, there is no need to return the user to
+    the caller.
+    """
     @abstractmethod
     def reauthenticate(self, user, secret):
+        """
+        This method is abstract.
+
+        :param user: The current user instance
+        :param str secret: The secret provided by the user
+        :type user: :class:`User<flaskbb.user.models.User>`
+        """
         pass
 
     def __call__(self, user, secret):
@@ -87,9 +195,50 @@ class ReauthenticateManager(ABC):
 
 
 class ReauthenticateProvider(ABC):
+    """
+    Used to reauthenticate a user that is already logged into the system,
+    for example when suspicious activity is detected in their session.
+
+    ReauthenticateProviders are similiar to
+    :class:`AuthenticationProvider<flaskbb.core.auth.authentication.AuthenticationProvider>`
+    except they receive a user instance rather than an identifer for a user.
+
+    A successful reauthentication should return True while failures should
+    return None in order to give other providers an attempt run.
+
+    If a ReauthenticateProvider determines that reauthentication should
+    immediately end, it may raise
+    :class:`StopAuthentication<flaskbb.core.auth.authentication.StopAuthentication>`
+    to safely end the process.
+
+
+    An example::
+
+        class LDAPReauthenticateProvider(ReauthenticateProvider):
+            def __init__(self, ldap_client):
+                self.ldap_client = ldap_client
+
+            def reauthenticate(self, user, secret):
+                user_dn = "uid={},ou=flaskbb,ou=org".format(user.username)
+                try:
+                    self.ldap_client.bind_user(user_dn, secret)
+                    return True
+                except Exception:
+                    return None
+
+    """
 
     @abstractmethod
     def reauthenticate(self, user, secret):
+        """
+        This method is abstract.
+
+        :param user: The current user instance
+        :param str secret: The secret provided by the user
+        :type user: :class:`User<flaskbb.user.models.User>`
+        :returns: True for a successful reauth, otherwise None
+        """
+
         pass
 
     def __call__(self, user, secret):
@@ -97,9 +246,21 @@ class ReauthenticateProvider(ABC):
 
 
 class ReauthenticateFailureHandler(ABC):
+    """
+    Used to manager reauthentication failures in FlaskBB.
 
+    ReauthenticateFailureHandlers are similiar to
+    :class:`AuthenticationFailureHandler<flaskbb.core.auth.authentication.AuthenticationFailureHandler>`
+    except they receive the user instance rather than an indentifier for a user
+    """
     @abstractmethod
     def handle_reauth_failure(self, user):
+        """
+        This method is abstract.
+
+        :param user: The current user instance that failed the reauth attempt
+        :type user: :class:`User<flaskbb.user.models.User>`
+        """
         pass
 
     def __call__(self, user):
@@ -107,9 +268,23 @@ class ReauthenticateFailureHandler(ABC):
 
 
 class PostReauthenticateHandler(ABC):
+    """
+    Used to post process successful reauthentication attempts.
+
+    PostAuthenticationHandlers are similar to
+    :class:`PostAuthenticationHandler<flaskbb.core.auth.authentication.PostAuthenticationHandler>`,
+    including their ability to cancel a successful attempt by raising
+    :class:`StopAuthentication<flaskbb.core.auth.authentication.StopAuthentication>`
+    """
 
     @abstractmethod
     def handle_post_reauth(self, user):
+        """
+        This method is abstract.
+
+        :param user: The current user instance that passed the reauth attempt
+        :type user: :class:`User<flaskbb.user.models.User>`
+        """
         pass
 
     def __call__(self, user):

+ 31 - 0
flaskbb/core/auth/password.py

@@ -15,11 +15,42 @@ from ..._compat import ABC
 
 
 class ResetPasswordService(ABC):
+    """
+    Interface for managing the password reset experience in FlaskBB.
+    """
 
     @abstractmethod
     def initiate_password_reset(self, email):
+        """
+        This method is abstract.
+
+        Used to send a password reset token to a user.
+
+        This method may raise a
+        :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+        when generating the token, such as when the user requests a reset token
+        be sent to an email that isn't registered in the application.
+
+        :param str email: The email to send the reset request to.
+        """
         pass
 
     @abstractmethod
     def reset_password(self, token, email, new_password):
+        """
+        This method is abstract.
+
+        Used to process a password reset token and handle resetting the user's
+        password to the newly desired one. The token passed to this message
+        is the raw, serialized token sent to the user.
+
+        This method may raise
+        :class:`TokenError<flaskbb.core.tokens.TokenError>` or
+        :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+        to communicate failures when parsing or consuming the token.
+
+        :param str token: The raw serialized token sent to the user
+        :param str email: The email entered by the user at password reset
+        :param str new_password: The new password to assign to the user
+        """
         pass

+ 26 - 4
flaskbb/core/auth/registration.py

@@ -19,6 +19,10 @@ from ..._compat import ABC
 
 @attr.s(hash=True, cmp=False, repr=True, frozen=True)
 class UserRegistrationInfo(object):
+    """
+    User registration object, contains all relevant information for validating
+    and creating a new user.
+    """
     username = attr.ib()
     password = attr.ib(repr=False)
     email = attr.ib()
@@ -27,20 +31,38 @@ class UserRegistrationInfo(object):
 
 
 class UserValidator(ABC):
+    """
+    Used to validate user registrations and stop the registration process
+    by raising a
+    :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`.
+    """
+
     @abstractmethod
     def validate(self, user_info):
         """
-        Used to check if a user should be allowed to register.
-        Should raise ValidationError if the user should not be
-        allowed to register.
+        This method is abstract.
+
+        :param user_info: The provided registration information.
+        :type user_info: :class:`UserRegistrationInfo<flaskbb.core.auth.registration.UserRegistrationInfo>`
         """
-        return True
 
     def __call__(self, user_info):
         return self.validate(user_info)
 
 
 class UserRegistrationService(ABC):
+    """
+    Used to manage the registration process. A default implementation is
+    provided however, this interface is provided in case alternative
+    flows are needed.
+    """
+
     @abstractmethod
     def register(self, user_info):
+        """
+        This method is abstract.
+
+        :param user_info: The provided user registration information.
+        :type user_info: :class:`UserRegistrationInfo<flaskbb.core.auth.registration.UserRegistrationInfo>`
+        """
         pass

+ 13 - 4
flaskbb/core/exceptions.py

@@ -11,14 +11,23 @@
     :license: BSD, see LICENSE for more details
 """
 
+
 class BaseFlaskBBError(Exception):
-    "Root exception for FlaskBB"
+    """
+    Root exception for FlaskBB.
+    """
 
 
 class ValidationError(BaseFlaskBBError):
     """
     Used to signal validation errors for things such as
     token verification, user registration, etc.
+
+    :param str attribute: The attribute the validation error applies to,
+        if the validation error applies to multiple attributes or to
+        the entire object, this should be set to None
+    :param str reason: Why the attribute, collection of attributes or object
+        is invalid.
     """
 
     def __init__(self, attribute, reason):
@@ -33,11 +42,11 @@ class StopValidation(BaseFlaskBBError):
     validation should end immediately and no further
     processing should be done.
 
-    The reasons passed should be an iterable of
-    tuples consisting of `(attribute, reason)`
-
     Can also be used to communicate all errors
     raised during a validation run.
+
+    :param reasons: A sequence of `(attribute, reason)` pairs explaining
+        why the object is invalid.
     """
 
     def __init__(self, reasons):

+ 53 - 7
flaskbb/core/tokens.py

@@ -23,6 +23,8 @@ class TokenError(BaseFlaskBBError):
     Raised when there is an issue with deserializing
     a token. Has helper classmethods to ensure
     consistent verbiage.
+
+    :param str reason: An explanation of why the token is invalid
     """
 
     def __init__(self, reason):
@@ -31,10 +33,19 @@ class TokenError(BaseFlaskBBError):
 
     @classmethod
     def invalid(cls):
+        """
+        Used to raise an exception about a token that is invalid
+        due to being signed incorrectly, has been tampered with,
+        is unparsable or contains an inappropriate action.
+        """
         return cls(_('Token is invalid'))
 
     @classmethod
     def expired(cls):
+        """
+        Used to raise an exception about a token that has expired and is
+        no longer usable.
+        """
         return cls(_('Token is expired'))
 
     # in theory this would never be raised
@@ -48,33 +59,60 @@ class TokenError(BaseFlaskBBError):
 # holder for token actions
 # not an enum so plugins can add to it
 class TokenActions:
+    """
+    Collection of token actions.
+
+    .. note::
+        This is just a class rather than an enum because enums cannot be
+        extended at runtime which would limit the number of token actions
+        to the ones implemented by FlaskBB itself and block extension of
+        tokens by plugins.
+    """
     RESET_PASSWORD = 'reset_password'
     ACTIVATE_ACCOUNT = 'activate_account'
 
 
 @attr.s(frozen=True, cmp=True, hash=True)
 class Token(object):
+    """
+    :param int user_id:
+    :param str operation: An operation taken from
+        :class:`TokenActions<flaskbb.core.tokens.TokenActions>`
+    """
     user_id = attr.ib()
     operation = attr.ib()
 
 
 class TokenSerializer(ABC):
     """
-    Interface for token serializers.
 
-    dumps must accept a Token instance and produce
-    a JWT
-
-    loads must accept a string representation of
-    a JWT and produce a token instance
     """
 
     @abstractmethod
     def dumps(self, token):
+        """
+        This method is abstract.
+
+        Used to transform a token into a string representation of it.
+
+        :param token:
+        :type token: :class:`Token<flaskbb.core.tokens.Token>`
+        :returns str:
+        """
         pass
 
     @abstractmethod
     def loads(self, raw_token):
+        """
+        This method is abstract
+
+        Used to transform a string representation of a token into an
+        actual :class:`Token<flaskbb.core.tokens.Token>` instance
+
+        :param str raw_token:
+        :returns token: The parsed token
+        :rtype: :class:`Token<flaskbb.core.tokens.Token`>
+        """
         pass
 
 
@@ -84,12 +122,20 @@ class TokenVerifier(ABC):
     deserialization, such as an email matching the
     user id in the provided token.
 
-    Should raise a flaskbb.core.exceptions.ValidationError
+    Should raise a
+    :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
     if verification fails.
     """
 
     @abstractmethod
     def verify_token(self, token, **kwargs):
+        """
+        This method is abstract.
+
+        :param token: The parsed token to verify
+        :param kwargs: Arbitrary context for validation of the token
+        :type token: :class:`Token<flaskbb.core.tokens.Token>`
+        """
         pass
 
     def __call__(self, token, **kwargs):

+ 18 - 0
flaskbb/plugins/spec.py

@@ -166,6 +166,9 @@ def flaskbb_authenticate(identifier, secret):
     authentication is tried last to give others an attempt to
     authenticate the user instead.
 
+    See also:
+    :class:`AuthenticationProvider<flaskbb.core.auth.AuthenticationProvider>`
+
     Example of alternative auth::
 
         def ldap_auth(identifier, secret):
@@ -222,6 +225,9 @@ def flaskbb_post_authenticate(user):
     :class:`flaskbb.core.exceptions.StopAuthentication`
     and include why the login was prevented.
 
+    See also:
+    :class:`PostAuthenticationHandler<flaskbb.core.auth.PostAuthenticationHandler>`
+
     Example::
 
         def post_auth(user):
@@ -243,6 +249,9 @@ def flaskbb_authentication_failed(identifier):
     :class:`flaskbb.core.exceptions.StopAuthentication`
     is raised during the login process.
 
+    See also:
+    :class:`AuthenticationFailureHandler<flaskbb.core.auth.AuthenticationFailureHandler>`
+
     Example::
 
         def mark_failed_logins(identifier):
@@ -277,6 +286,9 @@ def flaskbb_reauth_attempt(user, secret):
     If a hook decides that a reauthenticate attempt should
     cease, it may raise StopAuthentication.
 
+    See also:
+    :class:`ReauthenticateProvider<flaskbb.core.auth.ReauthenticateProvider>`
+
     Example of checking secret or passing to the next implementer::
 
         @impl
@@ -303,6 +315,9 @@ def flaskbb_post_reauth(user):
     may still force a reauth to fail by raising StopAuthentication.
 
     Results from these hooks are not considered.
+
+    See also:
+    :class:`PostReauthenticateHandler<flaskbb.core.auth.PostAuthenticationHandler>`
     """
 
 @spec
@@ -316,6 +331,9 @@ def flaskbb_reauth_failed(user):
     If an implementation raises ForceLogout it should register
     itself as trylast to give other reauth failed handlers an
     opprotunity to run first.
+
+    See also:
+    :class:`ReauthenticateFailureHandler<flaskbb.core.auth.ReauthenticateFailureHandler>`
     """
 
 # Form hooks