FlaskBB Event Hooks¶
Post and Topic Events¶
-
flaskbb.plugins.spec.
flaskbb_event_post_save_before
(post)[source]¶ Hook for handling a post before it has been saved.
Parameters: post (flaskbb.forum.models.Post) – The post which triggered the event.
-
flaskbb.plugins.spec.
flaskbb_event_post_save_after
(post, is_new)[source]¶ Hook for handling a post after it has been saved.
Parameters: - post (flaskbb.forum.models.Post) – The post which triggered the event.
- is_new (bool) – True if the post is new, False if it is an edit.
-
flaskbb.plugins.spec.
flaskbb_event_topic_save_before
(topic)[source]¶ Hook for handling a topic before it has been saved.
Parameters: topic (flaskbb.forum.models.Topic) – The topic which triggered the event.
-
flaskbb.plugins.spec.
flaskbb_event_topic_save_after
(topic, is_new)[source]¶ Hook for handling a topic after it has been saved.
Parameters: - topic (flaskbb.forum.models.Topic) – The topic which triggered the event.
- is_new (bool) – True if the topic is new, False if it is an edit.
Registration Events¶
-
flaskbb.plugins.spec.
flaskbb_event_user_registered
(username)[source]¶ Hook for handling events after a user is registered
Warning
This hook is deprecated in favor of
flaskbb_registration_post_processor()
Parameters: username – The username of the newly registered user.
-
flaskbb.plugins.spec.
flaskbb_gather_registration_validators
()[source]¶ Hook for gathering user registration validators, implementers must return a callable that accepts a
UserRegistrationInfo
and raises aValidationError
if the registration is invalid orStopValidation
if validation of the registration should end immediatey.Example:
def cannot_be_named_fred(user_info): if user_info.username.lower() == 'fred': raise ValidationError(('username', 'Cannot name user fred')) @impl def flaskbb_gather_registration_validators(): return [cannot_be_named_fred]
Note
This is implemented as a hook that returns callables since the callables are designed to raise exceptions that are aggregated to form the failure message for the registration response.
See Also:
UserValidator
-
flaskbb.plugins.spec.
flaskbb_registration_post_processor
(user)[source]¶ Hook for handling actions after a user has successfully registered. This spec receives the user object after it has been successfully persisted to the database.
Example:
def greet_user(user): flash(_("Thanks for registering {}".format(user.username))) @impl def flaskbb_registration_post_processor(user): greet_user(user)
See Also:
RegistrationPostProcessor
-
flaskbb.plugins.spec.
flaskbb_registration_failure_handler
(user_info, failures)[source]¶ Hook for dealing with user registration failures, receives the info that user attempted to register with as well as the errors that failed the registration.
Example:
from .utils import fuzz_username def has_already_registered(failures): return any( attr = "username" and "already registered" in msg for (attr, msg) in failures ) def suggest_alternate_usernames(user_info, failures): if has_already_registered(failures): suggestions = fuzz_username(user_info.username) failures.append(("username", "Try: {}".format(suggestions))) @impl def flaskbb_registration_failure_handler(user_info, failures): suggest_alternate_usernames(user_info, failures)
See Also:
RegistrationFailureHandler
Authentication Events¶
-
flaskbb.plugins.spec.
flaskbb_authenticate
(identifier, secret)[source]¶ Hook for authenticating users in FlaskBB. This hook should return either an instance of
flaskbb.user.models.User
or None.If a hook decides that all attempts for authentication should end, it may raise a
flaskbb.core.exceptions.StopAuthentication
and include a reason why authentication was stopped.Only the first User result will used and the default FlaskBB authentication is tried last to give others an attempt to authenticate the user instead.
See also:
AuthenticationProvider
Example of alternative auth:
def ldap_auth(identifier, secret): "basic ldap example with imaginary ldap library" user_dn = "uid={},ou=flaskbb,dc=flaskbb,dc=org" try: ldap.bind(user_dn, secret) return User.query.join( UserLDAP ).filter( UserLDAP.dn==user_dn ).with_entities(User).one() except: return None @impl def flaskbb_authenticate(identifier, secret): return ldap_auth(identifier, secret)
Example of ending authentication:
def prevent_login_with_too_many_failed_attempts(identifier): user = User.query.filter( db.or_( User.username == identifier, User.email == identifier ) ).first() if user is not None: if has_too_many_failed_logins(user): raise StopAuthentication(_( "Your account is temporarily locked due to too many" " login attempts" )) @impl(tryfirst=True) def flaskbb_authenticate(user, identifier): prevent_login_with_too_many_failed_attempts(identifier)
-
flaskbb.plugins.spec.
flaskbb_post_authenticate
(user)[source]¶ Hook for handling actions that occur after a user is authenticated but before setting them as the current user.
This could be used to handle MFA. However, these calls will be blocking and should be taken into account.
Responses from this hook are not considered at all. If a hook should need to prevent the user from logging in, it should register itself as tryfirst and raise a
flaskbb.core.exceptions.StopAuthentication
and include why the login was prevented.See also:
PostAuthenticationHandler
Example:
def post_auth(user): today = utcnow() if is_anniversary(today, user.date_joined): flash(_("Happy registerversary!")) @impl def flaskbb_post_authenticate(user): post_auth(user)
-
flaskbb.plugins.spec.
flaskbb_authentication_failed
(identifier)[source]¶ Hook for handling authentication failure events. This hook will only be called when no authentication providers successfully return a user or a
flaskbb.core.exceptions.StopAuthentication
is raised during the login process.See also:
AuthenticationFailureHandler
Example:
def mark_failed_logins(identifier): user = User.query.filter( db.or_( User.username == identifier, User.email == identifier ) ).first() if user is not None: if user.login_attempts is None: user.login_attempts = 1 else: user.login_attempts += 1 user.last_failed_login = utcnow()
-
flaskbb.plugins.spec.
flaskbb_reauth_attempt
(user, secret)[source]¶ Hook for handling reauth in FlaskBB
These hooks receive the currently authenticated user and the entered secret. Only the first response from this hook is considered – similar to the authenticate hooks. A successful attempt should return True, otherwise None for an unsuccessful or untried reauth from an implementation. Reauth will be considered a failure if no implementation return True.
If a hook decides that a reauthenticate attempt should cease, it may raise StopAuthentication.
See also:
ReauthenticateProvider
Example of checking secret or passing to the next implementer:
@impl def flaskbb_reauth_attempt(user, secret): if check_password(user.password, secret): return True
Example of forcefully ending reauth:
@impl def flaskbb_reauth_attempt(user, secret): if user.login_attempts > 5: raise StopAuthentication( _("Too many failed authentication attempts") )
-
flaskbb.plugins.spec.
flaskbb_post_reauth
(user)[source]¶ Hook called after successfully reauthenticating.
These hooks are called a user has passed the flaskbb_reauth_attempt hooks but before their reauth is confirmed so a post reauth implementer may still force a reauth to fail by raising StopAuthentication.
Results from these hooks are not considered.
See also:
PostReauthenticateHandler
-
flaskbb.plugins.spec.
flaskbb_reauth_failed
(user)[source]¶ Hook called if a reauth fails.
These hooks will only be called if no implementation for flaskbb_reauth_attempt returns a True result or if an implementation raises StopAuthentication.
If an implementation raises ForceLogout it should register itself as trylast to give other reauth failed handlers an opprotunity to run first.
See also:
ReauthenticateFailureHandler
Profile Edit Events¶
-
flaskbb.plugins.spec.
flaskbb_gather_password_validators
(app)[source]¶ Hook for gathering
ChangeSetValidator
instances specialized for handlingPasswordUpdate
This hook should return an iterable:class NotLongEnough(ChangeSetValidator): def __init__(self, min_length): self._min_length = min_length def validate(self, model, changeset): if len(changeset.new_password) < self._min_length: raise ValidationError( "new_password", "Password must be at least {} characters ".format( self._min_length ) ) @impl def flaskbb_gather_password_validators(app): return [NotLongEnough(app.config['MIN_PASSWORD_LENGTH'])]
Parameters: app – The current application
-
flaskbb.plugins.spec.
flaskbb_password_updated
(user)[source]¶ Hook for responding to a user updating their password. This hook is called after the password change has been persisted:
@impl def flaskbb_password_updated(app, user): send_email( "Password changed", [user.email], text_body=..., html_body=... )
See also
ChangeSetPostProcessor
Parameters: user – The user that updated their password.
-
flaskbb.plugins.spec.
flaskbb_gather_email_validators
(app)[source]¶ Hook for gathering
ChangeSetValidator
instances specialized forEmailUpdate
. This hook should return an iterable:class BlackListedEmailProviders(ChangeSetValidator): def __init__(self, black_list): self._black_list = black_list def validate(self, model, changeset): provider = changeset.new_email.split('@')[1] if provider in self._black_list: raise ValidationError( "new_email", "{} is a black listed email provider".format(provider) ) @impl def flaskbb_gather_email_validators(app): return [BlackListedEmailProviders(app.config["EMAIL_PROVIDER_BLACK_LIST"])]
Parameters: app – The current application
-
flaskbb.plugins.spec.
flaskbb_email_updated
(user, email_update)[source]¶ Hook for responding to a user updating their email. This hook is called after the email change has been persisted:
@impl def flaskbb_email_updated(app): send_email( "Email changed", [email_change.old_email], text_body=..., html_body=... )
See also
ChangeSetPostProcessor
.Parameters: - user – The user whose email was updated.
- email_update – The change set applied to the user.
-
flaskbb.plugins.spec.
flaskbb_gather_details_update_validators
(app)[source]¶ Hook for gathering
ChangeSetValidator
instances specialized forUserDetailsChange
. This hook should return an iterable:class DontAllowImageSignatures(ChangeSetValidator): def __init__(self, renderer): self._renderer = renderer def validate(self, model, changeset): rendered = self._renderer.render(changeset.signature) if '<img' in rendered: raise ValidationError("signature", "No images allowed in signature") @impl def flaskbb_gather_details_update_validators(app): renderer = app.pluggy.hook.flaskbb_load_nonpost_markdown_class() return [DontAllowImageSignatures(renderer())]
Parameters: app – The current application
-
flaskbb.plugins.spec.
flaskbb_details_updated
(user, details_update)[source]¶ Hook for responding to a user updating their details. This hook is called after the details update has been persisted.
See also
ChangeSetPostProcessor
Parameters: - user – The user whose details have been updated.
- details_update – The details change set applied to the user.
-
flaskbb.plugins.spec.
flaskbb_settings_updated
(user, settings_update)[source]¶ Hook for responding to a user updating their settings. This hook is called after the settings change has been persisted.
See also
ChangeSetPostProcessor
Parameters: - user – The user whose settings have been updated.
- settings – The settings change set applied to the user.