././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1611628843.595904 Flask-Security-Too-4.0.0/0000755000175100001640000000000000000000000015721 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/.editorconfig0000644000175100001640000000111100000000000020370 0ustar00runnerdocker00000000000000# -*- coding: utf-8 -*- root = true [*] indent_style = space end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 # Python files [*.py] indent_size = 4 # isort plugin configuration known_first_party = flask_security multi_line_output = 2 default_section = THIRDPARTY # RST files (used by sphinx) [*.rst] indent_size = 4 # CSS, HTML, JS, JSON, YML [*.{css,html,js,json,yml}] indent_size = 2 # Matches the exact files either package.json or .travis.yml [{package.json,.travis.yml}] indent_size = 2 # Dockerfile [Dockerfile] indent_size = 4 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/AUTHORS0000644000175100001640000000146500000000000016777 0ustar00runnerdocker00000000000000Flask-Security was written by Matt Wright and various contributors. Flask-Security-Too is an independently maintained repo: Development Lead ```````````````` - Chris Wagner Maintainer `````````` - Chris Wagner Patches and Suggestions ``````````````````````` Alexander Sukharev Alexey Poryadin Andrew J. Camenga Anthony Plunkett Artem Andreev Catherine Wise Chris Haines Christophe Simonis David Ignacio Eric Butler Eskil Heyn Olsen Iuri de Silvio Jay Goel Jiri Kuncar Joe Esposito Joe Hand Josh Purvis Kostyantyn Leschenko Luca Invernizzi Manuel Ebert Martin Maillard Paweł Krześniak Robert Clark Rodrigue Cloutier Rotem Yaari Srijan Choudhary Tristan Escalada Vadim Kotov Walt Askew John Paraskevopoulos Chris Wagner Eric Regnier Gal Stainfeld Ivan Piskunov Tyler Baur ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/CHANGES.rst0000644000175100001640000013327700000000000017540 0ustar00runnerdocker00000000000000Flask-Security Changelog ======================== Here you can see the full list of changes between each Flask-Security release. Version 4.0.0 ------------- Released January 26, 2021 **PLEASE READ CHANGE NOTES CAREFULLY - THERE ARE LIKELY REQUIRED CHANGES YOU WILL HAVE TO MAKE TO EVEN START YOUR APPLICATION WITH 4.0** Start Here +++++++++++ - Your UserModel must contain ``fs_uniquifier`` - Either uninstall Flask-BabelEx (if you don't need translations) or add either Flask-Babel (>=2.0) or Flask-BabelEx to your dependencies AND be sure to initialize it in your app. - Add Flask-Mail to your dependencies. - If you have unicode emails or passwords read change notes below. Version 4.0.0rc2 ---------------- Released January 18, 2021 Features & Cleanup +++++++++++++++++++ - Removal of python 2.7 and <3.6 support - Removal of token caching feature (a relatively new feature that had some systemic issues) - (:pr:`328`) Remove dependence on Flask-Mail and refactor. - (:pr:`335`) Remove two-factor `/tf-confirm` endpoint and use generic `freshness` mechanism. - (:pr:`336`) Remove ``SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN_INVALID(ATE)``. In addition to not making sense - the documentation has never been correct. - (:pr:`339`) Require ``fs_uniquifier`` in the UserModel and stop using/referencing the UserModel primary key. - (:pr:`349`) Change ``SECURITY_USER_IDENTITY_ATTRIBUTES`` configuration variable semantics. - Remove (all?) requirements around having an 'email' column in the UserModel. API change - JSON SPA redirects used to always include a query param 'email=xx'. While that is still sent (if and only if) the UserModel contains an 'email' columns, a new query param 'identity' is returned which returns the value of :meth:`.UserMixin.calc_username()`. - (:pr:`382`) Improvements and documentation for two-factor authentication. - (:pr:`394`) Add support for email validation and normalization (see :class:`.MailUtil`). - (:issue:`231`) Normalize unicode passwords (see :class:`.PasswordUtil`). - (:issue:`391`) Option to redirect to `/confirm` if user hits an endpoint that requires confirmation. New option :py:data:`SECURITY_REQUIRES_CONFIRMATION_ERROR_VIEW` which if set and the user hits the `/login`, `/reset`, or `/us-signin` endpoint, and they require confirmation the response will be a redirect. (SnaKyEyeS) - (:issue:`366`) Allow redirects on sub-domains. Please see :py:data:`SECURITY_REDIRECT_ALLOW_SUBDOMAINS`. (willcroft) - (:pr:`376`) Have POST redirects default to Flask's ``APPLICATION_ROOT``. Previously the default configuration was ``/``. Now it first looks at Flask's `APPLICATION_ROOT` configuration and uses that (which also by default is ``/``. (tysonholub) - (:pr:`401`) Add 2FA Validity Window so an application can configure how often the second factor has to be entered. (baurt) - (:pr:`403`) Add HTML5 Email input types to email fields. This has some backwards compatibility concerns outlined below. (drola) - (:pr:`413`) Add hy_AM translations. (rudolfamirjanyan) - (:pr:`410`) Add Basque and fix Spanish translations. (mmozos) - (:pr:`408`) Polish translations. (kamil559) - (:pr:`390`) Update ru_RU translations. (TitaniumHocker) Fixed +++++ - (:issue:`389`) Fixes for translations. First - email subjects were never being translated. Second, converted all templates to use _fsdomain(xx) rather than _(xx) so that they get translated regardless of the app's domain. - (:issue:`381`) Support Flask-Babel 2.0 which has backported Domain support. Flask-Security now supports Flask-Babel (>=2.00), Flask-BabelEx, as well as no translation support. Please see backwards compatibility notes below. - (:pr:`352`) Fix issue with adding/deleting permissions - all mutating methods must be at the datastore layer so that db.put() can be called. Added :meth:`.UserDatastore.add_permissions_to_role` and :meth:`.UserDatastore.remove_permissions_from_role`. The methods :meth:`.RoleMixin.add_permissions` and :meth:`.RoleMixin.remove_permissions` have been deprecated. - (:issue:`395`) Provide ability to change table names for User and Role tables in the fsqla model. - (:issue:`338`) All sessions are invalidated when a user changes or resets their password. This is accomplished by changing the user's `fs_uniquifier`. The user is automatically re-logged in (and a new session created) after a successful change operation. - (:issue:`418`) Two-factor (and to a lesser extent unified sign in) QRcode fetching wasn't protected via CSRF. The fix makes things secure and simpler (always good); however read below for compatibility concerns. In addition, the elements that make up the QRcode (key, username, issuer) area also made available to the form and returned as part of the JSON return value - this allows for manual or other ways to initialize the authenticator app. - (:issue:`421`) GET on `/login` and `/change` could return the callers authentication_token. This is a security concern since GETs don't have CSRF protection. This bug was introduced in 3.3.0. Backwards Compatibility Concerns +++++++++++++++++++++++++++++++++ - (:pr:`328`) Remove dependence on Flask-Mail and refactor. The ``send_mail_task`` and ``send_mail`` methods as part of Flask-Security initialization have been removed and replaced with a new :class:`.MailUtil` class. The utility method :func:`.send_mail` can still be used. If your application didn't use either of the deprecated methods, then the only change required is to add Flask-Mail to your package requirements (since Flask-Security no longer lists it). Please see the :ref:`emails_topic` for updated examples. - (:pr:`335`) Convert two-factor setup flow to use the freshness feature rather than its own verify password endpoint. This COMPLETELY removes the ``/tf-confirm`` endpoint and associated form: ``two_factor_verify_password_form``. Now, when /tf-setup is invoked, the :meth:`flask_security.check_and_update_authn_fresh` is invoked, and if the current session isn't 'fresh' the caller will be redirected to a verify endpoint (either :py:data:`SECURITY_VERIFY_URL` or :py:data:`SECURITY_US_VERIFY_URL`). The simplest change would be to call ``/verify`` everywhere the application used to call ``/tf-confirm``. - (:pr:`339`) Require ``fs_uniquifier``. In 3.3 the ``fs_uniquifier`` was added in the UserModel to fix the slow authentication token issue. In 3.4 the ``fs_uniquifier`` was used to implement Flask-Login's `Alternative Token` feature - thus decoupling the primary key (id) from any security context. All along, there have been a few issues with applications not wanting to use the name 'id' in their model, or wanting a different type for their primary key. With this change, Flask-Security no longer interprets or uses the UserModel primary key - just the ``fs_uniquifier`` field. See the changes section for 3.3 for information on how to do the schema and data upgrades required to add this field. There is also an API change - the JSON response (via UserModel.get_security_payload()) returned the ``user.id`` field. With this change the default is an empty directory - override :meth:`.UserMixin.get_security_payload()` to return any portion of the UserModel you need. - (:pr:`349`) :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES` has changed syntax and semantics. It now contains the combined information from the old ``SECURITY_USER_IDENTITY_ATTRIBUTES`` and the newly introduced in 3.4 :py:data:`SECURITY_USER_IDENTITY_MAPPINGS`. This enabled changing the underlying way we validate credentials in the login form and unified sign in form. In prior releases we simply tried to look up the form value as the PK of the UserModel - this often failed and then looped through the other ``SECURITY_USER_IDENTITY_ATTRIBUTES``. This had a history of issues, including many applications not wanting to have a standard PK for the user model. Now, using the mapping configuration, the UserModel attribute/column the input corresponds to is determined, then the UserModel is queried specifically for that *attribute:value* pair. If you application didn't change the variable, no modifications are required. - (:pr:`354`) The :class:`flask_security.PhoneUtil` is now initialized as part of Flask-Security initialization rather than ``@app.before_first_request`` (since that broke the CLI). Since it isn't called in an application context, the *app* being initialized is passed as an argument to *__init__*. - (:issue:`381`) When using Flask-Babel (>= 2.0) it is required that the application initialize Flask-Babel (e.g. Babel(app)). Flask-BabelEx would self-initialize so it didn't matter. Flask-Security will throw a run time error upon first request if Flask-Babel OR FLask-BabelEx is installed, but not initialized. Also, Flask-Security no longer has a dependency on either Flask-Babel or Flask-BabelEx - if neither are installed, it falls back to a dummy translation. *If your application expects translation services, it must specify the appropriate* *dependency AND initialize it.* - (:pr:`394`) Email input is now normalized prior to being stored in the DB. Previously, it was validated, but the raw input was stored. Normalization and validation rely on the `email_validator `_ package. The :class:`.MailUtil` class provides the interface for normalization and validation - allowing all this to be customized. If you have unicode local or domain parts - existing users may have difficulties logging in. Administratively you need to read each user record, normalize the email (see :class:`.MailUtil`), and write it back. - (:issue:`381`) Passwords are now, by default, normalized using Python's unicodedata.normalize() method. The :py:data:`SECURITY_PASSWORD_NORMALIZE_FORM` defaults to "NKFD". This brings Flask-Security in line with the NIST recommendations outlined in `Memorized Secret Verifiers `_ If your users have unicode passwords they may have difficulty authenticating. You can turn off this normalization or have your users reset their passwords. Password normalization and validation has been encapsulated in a new :class:`.PasswordUtil` class. This replaces the method ``password_validator`` introduced in 3.4.0. - (:pr:`403`) By default all forms that have an email as input now use the wtforms html5 ``EmailField``. For most applications this will make the user experience slightly nicer - especially for mobile devices. Some applications use the email form field for other identity attributes (such as username). If your application does this you will probably need to subclass ``LoginForm`` and change the email type back to StringField. - (:issue:`338`) By default, both passwords and authentication tokens use the same attribute ``fs_uniquifier`` to uniquely identify the user. This means that if the user changes or resets their password, all authentication tokens also become invalid. This could be viewed as a feature or a bug. If this behavior isn't desired, add another uniquifier: ``fs_token_uniquifier`` to your UserModel and that will be used to generate authentication tokens. - (:issue:`418`) Fix CSRF vulnerability w.r.t. getting QRcodes. Both two-factor and unified-signup had a separate GET endpoint to fetch the QRcode when setting up an authenticator app. GETS don't have any CSRF protection. Both of those endpoints have been completely removed, and the QRcode is embedded in a successful POST of the setup form. The changes to the templates are minimal and of course if you didn't override the template - there is no compatibility concern. - (:issue:`421`) Fix CSRF vulnerability on `/login` and `/change` that could return the callers authentication token. Now, callers can only get the authentication token on successful POST calls. Version 3.4.5 -------------- Released January 8, 2021 Security Vulnerability Fix. Two CSRF vulnerabilities were reported: `qrcode`_ and `login`_. This release fixes the more severe of the 2 - the `/login` vulnerability. The QRcode issue has a much smaller risk profile since a) it is only for two-factor authentication using an authenticator app b) the qrcode is only available during the time the user is first setting up their authentication app. The QRcode issue has been fixed in 4.0. .. _qrcode: https://github.com/Flask-Middleware/flask-security/issues/418 .. _login: https://github.com/Flask-Middleware/flask-security/issues/421 Fixed +++++ - (:issue:`421`) GET on `/login` and `/change` could return the callers authentication_token. This is a security concern since GETs don't have CSRF protection. This bug was introduced in 3.3.0. Backwards Compatibility Concerns ++++++++++++++++++++++++++++++++ - (:issue:`421`) Fix CSRF vulnerability on `/login` and `/change` that could return the callers authentication token. Now, callers can only get the authentication token on successful POST calls. Version 3.4.4 -------------- Released July 27, 2020 Bug/regression fixes. Fixed +++++ - (:issue:`359`) Basic Auth broken. When the unauthenticated handler was changed to provide a more uniform/consistent response - it broke using Basic Auth from a browser, since it always redirected rather than returning 401. Now, if the response headers contain ``WWW-Authenticate`` (which is set if ``basic`` @auth_required method is used), a 401 is returned. See below for backwards compatibility concerns. - (:pr:`362`) As part of figuring out issue 359 - a redirect loop was found. In release 3.3.0 code was put in to redirect to :py:data:`SECURITY_POST_LOGIN_VIEW` when GET or POST was called and the caller was already authenticated. The method used would honor the request ``next`` query parameter. This could cause redirect loops. The pre-3.3.0 behavior of redirecting to :py:data:`SECURITY_POST_LOGIN_VIEW` and ignoring the ``next`` parameter has been restored. - (:issue:`347`) Fix peewee. Turns out - due to lack of unit tests - peewee hasn't worked since 'permissions' were added in 3.3. Furthermore, changes in 3.4 around get_id and alternative tokens also didn't work since peewee defines its own `get_id` method. Compatibility Concerns ++++++++++++++++++++++ In 3.3.0, :meth:`flask_security.auth_required` was changed to add a default argument if none was given. The default include all current methods - ``session``, ``token``, and ``basic``. However ``basic`` really isn't like the others and requires that we send back a ``WWW-Authenticate`` header if authentication fails (and return a 401 and not redirect). ``basic`` has been removed from the default set and must once again be explicitly requested. Version 3.4.3 ------------- Released June 12, 2020 Minor fixes for a regression and a couple other minor changes Fixed +++++ - (:issue:`340`) Fix regression where tf_phone_number was required, even if SMS wasn't configured. - (:pr:`342`) Pick up some small documentation fixes from 4.0.0. Version 3.4.2 ------------- Released May 2, 2020 Only change is to move repo to the Flask-Middleware github organization. Version 3.4.1 -------------- Released April 22, 2020 Fix a bunch of bugs in new unified sign in along with a couple other major issues. Fixed +++++ - (:issue:`298`) Alternative ID feature ran afoul of postgres/psycopg2 finickiness. - (:issue:`300`) JSON 401 responses had WWW-Authenticate Header attached - that caused browsers to pop up their own login/password form. Not what applications want. - (:issue:`280`) Allow admin/api to setup TFA (and unified sign in) out of band. Please see :meth:`.UserDatastore.tf_set`, :meth:`.UserDatastore.tf_reset`, :meth:`.UserDatastore.us_set`, :meth:`.UserDatastore.us_reset` and :meth:`.UserDatastore.reset_user_access`. - (:pr:`305`) We used form._errors which wasn't very pythonic, and it was removed in WTForms 2.3.0. - (:pr:`310`) WTForms 2.3.0 made email_validator optional - we need it. Version 3.4.0 ------------- Released March 31, 2020 Features ++++++++ - (:pr:`257`) Support a unified sign in feature. Please see :ref:`unified-sign-in`. - (:pr:`265`) Add phone number validation class. This is used in both unified sign in as well as two-factor when using ``sms``. - (:pr:`274`) Add support for 'freshness' of caller's authentication. This permits endpoints to be additionally protected by ensuring a recent authentication. - (:issue:`99`, :issue:`195`) Support pluggable password validators. Provide a default validator that offers complexity and breached support. - (:issue:`266`) Provide interface to two-factor send_token so that applications can provide error mitigation. Defaults to returning errors if can't send the verification code. - (:pr:`247`) Updated all-inclusive data models (fsqlaV2). Add fields necessary for the new unified sign in feature and changed 'username' to be unique (but not required). - (:pr:`245`) Use fs_uniquifier as the default Flask-Login 'alternative token'. Basically this means that changing the fs_uniquifier will cause outstanding auth tokens, session and remember me cookies to be invalidated. So if an account gets compromised, an admin can easily stop access. Prior to this cookies were storing the 'id' which is the user's primary key - difficult to change! (kishi85) Fixed +++++ - (:issue:`273`) Don't allow reset password for accounts that are disabled. - (:issue:`282`) Add configuration that disallows GET for logout. Allowing GET can cause some denial of service issues. The default still allows GET for backwards compatibility. (kantorii) - (:issue:`258`) Reset password wasn't integrated into the two-factor feature and therefore two-factor auth could be bypassed. - (:issue:`254`) Allow lists and sets as underlying permissions. (pffs) - (:issue:`251`) Allow a registration form to have additional fields that aren't part of the user model that are just passed to the user_registered.send signal, where the application can perform arbitrary additional actions required during registration. (kuba-lilz) - (:issue:`249`) Add configuration to disable the 'role-joining' optimization for SQLAlchemy. (pffs) - (:issue:`238`) Fix more issues with atomically setting the new TOTP secret when setting up two-factor. (kishi85) - (:pr:`240`) Fix Quart Compatibility. (ristellise) - (:issue:`232`) CSRF Cookie not being set when using 'Remember Me' cookie to re-sign in. (kishi85) - (:issue:`229`) Two-factor enabled accounts didn't work with the Remember Me feature. (kishi85) As part of adding unified sign in, there were many similarities with two-factor. Some refactoring was done to unify naming, configuration variables etc. It should all be backwards compatible. - In TWO_FACTOR_ENABLED_METHODS "mail" was changed to "email". "mail" will still be honored if already stored in DB. Also "google_authenticator" is now just "authenticator". - TWO_FACTOR_SECRET, TWO_FACTOR_URI_SERVICE_NAME, TWO_FACTOR_SMS_SERVICE, and TWO_FACTOR_SMS_SERVICE_CONFIG have all been deprecated in favor of names that are the same for two-factor and unified sign in. Other changes with possible backwards compatibility issues: - ``/tf-setup`` never did any phone number validation. Now it does. - ``two_factor_setup.html`` template - the chosen_method check was changed to ``email``. If you have your own custom template - be sure make that change. Version 3.3.3 ------------- Released February 11, 2020 Minor changes required to work with latest released Werkzeug and Flask-Login. Version 3.3.2 ------------- Released December 7, 2019 - (:issue:`215`) Fixed 2FA totp secret regeneration bug (kishi85) - (:issue:`172`) Fixed 'next' redirect error in login view - (:issue:`221`) Fixed regressions in login view when already authenticated user again does a GET or POST. - (:issue:`219`) Added example code for unit testing FS protected routes. - (:issue:`223`) Integrated two-factor auth into registration and confirmation. Thanks to kuba-lilz and kishi85 for finding and providing detailed issue reports. In Flask-Security 3.3.0 the login view was changed to allow already authenticated users to access the view. Prior to 3.3.0, the login view was protected with @anonymous_user_required - so any access (via GET or POST) would simply redirect the user to the ``POST_LOGIN_VIEW``. With the 3.3.0 changes, both GET and POST behaved oddly. GET simply returned the login template, and POST attempted to log out the current user, and log in the new user. This was problematic since this couldn't possibly work with CSRF. The old behavior has been restored, with the subtle change that older Flask-Security releases did not look at "next" in the form or request for the redirect, and now, all redirects from the login view will honor "next". Version 3.3.1 ------------- Released November 16, 2019 - (:pr:`197`) Add `Quart `_ compatibility (Ristellise) - (:pr:`194`) Add Python 3.8 support into CI (jdevera) - (:pr:`196`) Improve docs around Single Page Applications and React (acidjunk) - (:issue:`201`) fsqla model was added to __init__.py making Sqlalchemy a required package. That is wrong and has been removed. Applications must now explicitly import from ``flask_security.models`` - (:pr:`204`) Fix/improve examples and quickstart to show one MUST call hash_password() when creating users programmatically. Also show real SECRET_KEYs and PASSWORD_SALTs and how to generate them. - (:pr:`209`) Add argon2 as an allowable password hash. - (:pr:`210`) Improve integration with Flask-Admin. Actually - this PR improves localization support by adding a method ``_fsdomain`` to jinja2's global environment. Added documentation around localization. Version 3.3.0 ------------- Released September 26, 2019 **There are several default behavior changes that might break existing applications. Most have configuration variables that restore prior behavior**. **If you use Authentication Tokens (rather than session cookies) you MUST make a (small) change. Please see below for details.** - (:pr:`120`) Native support for Permissions as part of Roles. Endpoints can be protected via permissions that are evaluated based on role(s) that the user has. - (:issue:`126`, :issue:`93`, :issue:`96`) Revamp entire CSRF handling. This adds support for Single Page Applications and having CSRF protection for browser(session) authentication but ignored for token based authentication. Add extensive documentation about all the options. - (:issue:`156`) Token authentication is slow. Please see below for details on how to enable a new, fast implementation. - (:issue:`130`) Enable applications to provide their own :meth:`.render_json` method so that they can create unified API responses. - (:issue:`121`) Unauthorized callback not quite right. Split into 2 different callbacks - one for unauthorized and one for unauthenticated. Made default unauthenticated handler use Flask-Login's unauthenticated method to make everything uniform. Extensive documentation added. `.Security.unauthorized_callback` has been deprecated. - (:pr:`120`) Add complete User and Role model mixins that support all features. Modify tests and Quickstart documentation to show how to use these. Please see :ref:`responsetopic` for details. - Improve documentation for :meth:`.UserDatastore.create_user` to make clear that hashed password should be passed in. - Improve documentation for :class:`.UserDatastore` and :func:`.verify_and_update_password` to make clear that caller must commit changes to DB if using a session based datastore. - (:issue:`122`) Clarify when to use ``confirm_register_form`` rather than ``register_form``. - Fix bug in 2FA that didn't commit DB after using `verify_and_update_password`. - Fix bug(s) in UserDatastore where changes to user ``active`` flag weren't being added to DB. - (:issue:`127`) JSON response was failing due to LazyStrings in error response. - (:issue:`117`) Making a user inactive should stop all access immediately. - (:issue:`134`) Confirmation token can no longer be reused. Added *SECURITY_AUTO_LOGIN_AFTER_CONFIRM* option for applications that don't want the user to be automatically logged in after confirmation (defaults to True - existing behavior). - (:issue:`159`) The ``/register`` endpoint returned the Authentication Token even though confirmation was required. This was a huge security hole - it has been fixed. - (:issue:`160`) The 2FA totp_secret would be regenerated upon submission, making QRCode not work. (malware-watch) - (:issue:`166`) `default_render_json` uses ``flask.make_response`` and forces the Content-Type to JSON for generating the response (koekie) - (:issue:`166`) *SECURITY_MSG_UNAUTHENTICATED* added to the configuration. - (:pr:`168`) When using the @auth_required or @auth_token_required decorators, the token would be verified twice, and the DB would be queried twice for the user. Given how slow token verification is - this was a significant issue. That has been fixed. - (:issue:`84`) The :func:`.anonymous_user_required` was not JSON friendly - always performing a redirect. Now, if the request 'wants' a JSON response - it will receive a 400 with an error message defined by *SECURITY_MSG_ANONYMOUS_USER_REQUIRED*. - (:pr:`145`) Improve 2FA templates to that they can be localized. (taavie) - (:issue:`173`) *SECURITY_UNAUTHORIZED_VIEW* didn't accept a url (just an endpoint). All other view configurations did. That has been fixed. Possible compatibility issues +++++++++++++++++++++++++++++ - (:pr:`164`) In prior releases, the Authentication Token was returned as part of the JSON response to each successful call to `/login`, `/change`, or `/reset/{token}` API call. This is not a great idea since for browser-based UIs that used JSON request/response, and used session based authentication - they would be sent this token - even though it was likely ignored. Since these tokens by default have no expiration time this exposed a needless security hole. The new default behavior is to ONLY return the Authentication Token from those APIs if the query param ``include_auth_token`` is added to the request. Prior behavior can be restored by setting the *SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN* configuration variable. - (:pr:`120`) :class:`.RoleMixin` now has a method :meth:`.get_permissions` which is called as part each request to add Permissions to the authenticated user. It checks if the RoleModel has a property ``permissions`` and assumes it is a comma separated string of permissions. If your model already has such a property this will likely fail. You need to override :meth:`.get_permissions` and simply return an emtpy set. - (:issue:`121`) Changes the default (failure) behavior for views protected with @auth_required, @token_auth_required, or @http_auth_required. Before, a 401 was returned with some stock html. Now, Flask-Login.unauthorized() is called (the same as @login_required does) - which by default redirects to a login page/view. If you had provided your own `.Security.unauthorized_callback` there are no changes - that will still be called first. The old default behavior can be restored by setting *SECURITY_BACKWARDS_COMPAT_UNAUTHN* to True. Please see :ref:`responsetopic` for details. - (:issue:`127`) Fix for LazyStrings in json error response. The fix for this has Flask-Security registering its own JsonEncoder on its blueprint. If you registered your own JsonEncoder for your app - it will no longer be called when serializing responses to Flask-Security endpoints. You can register your JsonEncoder on Flask-Security's blueprint by sending it as `json_encoder_cls` as part of initialization. Be aware that your JsonEncoder needs to handle LazyStrings (see speaklater). - (:issue:`84`) Prior to this fix - anytime the decorator :func:`.anonymous_user_required` failed, it caused a redirect to the post_login_view. Now, if the caller wanted a JSON response, it will return a 400. - (:issue:`156`) Faster Authentication Token introduced the following non-backwards compatible behavior change: * Since the old Authentication Token algorithm used the (hashed) user's password, those tokens would be invalidated whenever the user changed their password. This is not likely to be what most users expect. Since the new Authentication Token algorithm doesn't refer to the user's password, changing the user's password won't invalidate outstanding Authentication Tokens. The method :meth:`.UserDatastore.set_uniquifier` can be used by an administrator to change a user's ``fs_uniquifier`` - but nothing the user themselves can do to invalidate their Authentication Tokens. Setting the *SECURITY_BACKWARDS_COMPAT_AUTH_TOKEN_INVALIDATE* configuration variable will cause the user's ``fs_uniquifier`` to be changed when they change their password, thus restoring prior behavior. New fast authentication token implementation ++++++++++++++++++++++++++++++++++++++++++++ Current auth tokens are slow because they use the user's password (hashed) as a uniquifier (the user id isn't really enough since it might be reused). This requires checking the (hashed) password against what is in the token on EVERY request - however hashing is (on purpose) slow. So this can add almost a whole second to every request. To solve this, a new attribute in the User model was added - ``fs_uniquifier``. If this is present in your User model, then it will be used instead of the password for ensuring the token corresponds to the correct user. This is very fast. If that attribute is NOT present - then the behavior falls back to the existing (slow) method. DB Migration ~~~~~~~~~~~~ To use the new UserModel mixins or to add the column ``user.fs_uniquifier`` to speed up token authentication, a schema AND data migration needs to happen. If you are using Alembic the schema migration is easy - but you need to add ``fs_uniquifier`` values to all your existing data. You can add code like this to your migrations::update method:: # be sure to MODIFY this line to make nullable=True: op.add_column('user', sa.Column('fs_uniquifier', sa.String(length=64), nullable=True)) # update existing rows with unique fs_uniquifier import uuid user_table = sa.Table('user', sa.MetaData(), sa.Column('id', sa.Integer, primary_key=True), sa.Column('fs_uniquifier', sa.String)) conn = op.get_bind() for row in conn.execute(sa.select([user_table.c.id])): conn.execute(user_table.update().values(fs_uniquifier=uuid.uuid4().hex).where(user_table.c.id == row['id'])) # finally - set nullable to false op.alter_column('user', 'fs_uniquifier', nullable=False) Version 3.2.0 ------------- Released June 26th 2019 - (:pr:`80`) Support caching of authentication token (eregnier `opr #839 `_). This adds a new configuration variable *SECURITY_USE_VERIFY_PASSWORD_CACHE* which enables a cache (with configurable TTL) for authentication tokens. This is a big performance boost for those accessing Flask-Security via token as opposed to session. - (:pr:`81`) Support for JSON/Single-Page-Application. This completes support for non-form based access to Flask-Security. See PR for details. (jwag956) - (:pr:`79` Add POST logout to enhance JSON usage (jwag956). - (:pr:`73`) Fix get_user for various DBs (jwag956). This is a more complete fix than in opr #633. - (:pr:`78`, :pr:`103`) Add formal openapi API spec (jwag956). - (:pr:`86`, :pr:`94`, :pr:`98`, :pr:`101`, :pr:`104`) Add Two-factor authentication (opr #842) (baurt, jwag956). - (:issue:`108`) Fix form field label translations (jwag956) - (:issue:`115`) Fix form error message translations (upstream #801) (jwag956) - (:issue:`87`) Convert entire repo to Black (baurt) Version 3.1.0 ------------- Released never - (:pr:`53`) Use Security.render_template in mails too (noirbizarre `opr #487 `_) - (:pr:`56`) Optimize DB accesses by using an SQL JOIN when retrieving a user. (nfvs `opr #679 `_) - (:pr:`57`) Add base template to security templates (grihabor `opr #697 `_) - (:pr:`73`) datastore: get user by numeric identity attribute (jirikuncar `opr #633 `_) - (:pr:`58`) bugfix: support application factory pattern (briancappello `opr #703 `_) - (:pr:`60`) Make SECURITY_PASSWORD_SINGLE_HASH a list of scheme ignoring double hash (noirbizarre `opr #714 `_) - (:pr:`61`) Allow custom login_manager to be passed in to Flask-Security (jaza `opr #717 `_) - (:pr:`62`) Docs for OAauth2-based custom login manager (jaza `opr #727 `_) - (:pr:`63`) core: make the User model check the password (mklassen `opr #779 `_) - (:pr:`64`) Customizable send_mail (abulte `opr #730 `_) - (:pr:`68`) core: fix default for UNAUTHORIZED_VIEW (jirijunkar `opr #726 `_) These should all be backwards compatible. Possible compatibility issues: - #487 - prior to this, render_template() was overridable for views, but not emails. If anyone actually relied on this behavior, this has changed. - #703 - get factory pattern working again. There was a very complex dance between Security() instantiation and init_app regarding kwargs. This has been rationalized (hopefully). - #679 - SqlAlchemy SQL improvement. It is possible you will get the following error:: Got exception during processing: - 'User.roles' does not support object population - eager loading cannot be applied. This is likely solvable by removing ``lazy='dynamic'`` from your Role definition. Performance improvements: - #679 - for sqlalchemy, for each request, there would be 2 DB accesses - now there is one. Testing: For datastores operations, Sqlalchemy, peewee, pony were all tested against sqlite, postgres, and mysql real databases. Version 3.0.2 ------------- Released April 30th 2019 - (opr #439) HTTP Auth respects SECURITY_USER_IDENTITY_ATTRIBUTES (pnpnpn) - (opr #660) csrf_enabled` deprecation fix (abulte) - (opr #671) Fix referrer loop in _get_unauthorized_view(). (nfvs) - (opr #675) Fix AttributeError in _request_loader (sbagan) - (opr #676) Fix timing attack on login form (cript0nauta) - (opr #683) Close db connection after running tests (reambus) - (opr #691) docs: add password salt to SQLAlchemy app example (KshitijKarthick) - (opr #692) utils: fix incorrect email sender type (switowski) - (opr #696) Fixed broken Click link (williamhatcher) - (opr #722) Fix password recovery confirmation on deleted user (kesara) - (opr #747) Update login_user.html (rickwest) - (opr #748) i18n: configurable the dirname domain (escudero) - (opr #835) adds relevant user to reset password form for validation purposes (fuhrysteve) These are bug fixes and a couple very small additions. No change in behavior and no new functionality. 'opr#' is the original pull request from https://github.com/mattupstate/flask-security Version 3.0.1 -------------- Released April 28th 2019 - Support 3.7 as part of CI - Rebrand to this forked repo - (#15) Build docs and translations as part of CI - (#17) Move to msgcheck from pytest-translations - (opr #669) Fix for Read the Docs (jirikuncar) - (opr #710) Spanish translation (maukoquiroga) - (opr #712) i18n: improvements of German translations (eseifert) - (opr #713) i18n: add Portuguese (Brazilian) translation (dinorox) - (opr #719) docs: fix anchor links and typos (kesara) - (opr #751) i18n: fix missing space (abulte) - (opr #762) docs: fixed proxy import (lsmith) - (opr #767) Update customizing.rst (allanice001) - (opr #776) i18n: add Portuguese (Portugal) translation (micael-grilo) - (opr #791) Fix documentation for mattupstate#781 (fmerges) - (opr #796) Chinese translations (Steinkuo) - (opr #808) Clarify that a commit is needed after login_user (christophertull) - (opr #823) Add Turkish translation (Admicos) - (opr #831) Catalan translation (miceno) These are all documentation and i18n changes - NO code changes. All except the last 3 were accepted and reviewed by the original Flask-Security team. Thanks as always to all the contributors. Version 3.0.0 ------------- Released May 29th 2017 - Fixed a bug when user clicking confirmation link after confirmation and expiration causes confirmation email to resend. (see #556) - Added support for I18N. - Added options `SECURITY_EMAIL_PLAINTEXT` and `SECURITY_EMAIL_HTML` for sending respectively plaintext and HTML version of email. - Fixed validation when missing login information. - Fixed condition for token extraction from JSON body. - Better support for universal bdist wheel. - Added port of CLI using Click configurable using options `SECURITY_CLI_USERS_NAME` and `SECURITY_CLI_ROLES_NAME`. - Added new configuration option `SECURITY_DATETIME_FACTORY` which can be used to force default timezone for newly created datetimes. (see mattupstate/flask-security#466) - Better IP tracking if using Flask 0.12. - Renamed deprecated Flask-WFT base form class. - Added tests for custom forms configured using app config. - Added validation and tests for next argument in logout endpoint. (see #499) - Bumped minimal required versions of several packages. - Extended test matric on Travis CI for minimal and released package versions. - Added of .editorconfig and forced tests for code style. - Fixed a security bug when validating a confirmation token, also checks if the email that the token was created with matches the user's current email. - Replaced token loader with request loader. - Changed trackable behavior of `login_user` when IP can not be detected from a request from 'untrackable' to `None` value. - Use ProxyFix instead of inspecting X-Forwarded-For header. - Fix identical problem with app as with datastore. - Removed always-failing assertion. - Fixed failure of init_app to set self.datastore. - Changed to new style flask imports. - Added proper error code when returning JSON response. - Changed obsolete Required validator from WTForms to DataRequired. Bumped Flask-WTF to 0.13. - Fixed missing `SECURITY_SUBDOMAIN` in config docs. - Added cascade delete in PeeweeDatastore. - Added notes to docs about `SECURITY_USER_IDENTITY_ATTRIBUTES`. - Inspect value of `SECURITY_UNAUTHORIZED_VIEW`. - Send password reset instructions if an attempt has expired. - Added "Forgot password?" link to LoginForm description. - Upgraded passlib, and removed bcrypt version restriction. - Removed a duplicate line ('retype_password': 'Retype Password') in forms.py. - Various documentation improvement. Version 1.7.5 ------------- Released December 2nd 2015 - Added `SECURITY_TOKEN_MAX_AGE` configuration setting - Fixed calls to `SQLAlchemyUserDatastore.get_user(None)` (this now returns `False` instead of raising a `TypeError` - Fixed URL generation adding extra slashes in some cases (see GitHub #343) - Fixed handling of trackable IP addresses when the `X-Forwarded-For` header contains multiple values - Include WWW-Authenticate headers in `@auth_required` authentication checks - Fixed error when `check_token` function is used with a json list - Added support for custom `AnonymousUser` classes - Restricted `forgot_password` endpoint to anonymous users - Allowed unauthorized callback to be overridden - Fixed issue where passwords cannot be reset if currently set to `None` - Ensured that password reset tokens are invalidated after use - Updated `is_authenticated` and `is_active` functions to support Flask-Login changes - Various documentation improvements Version 1.7.4 ------------- Released October 13th 2014 - Fixed a bug related to changing existing passwords from plaintext to hashed - Fixed a bug in form validation that did not enforce case insensitivity - Fixed a bug with validating redirects Version 1.7.3 ------------- Released June 10th 2014 - Fixed a bug where redirection to `SECURITY_POST_LOGIN_VIEW` was not respected - Fixed string encoding in various places to be friendly to unicode - Now using `werkzeug.security.safe_str_cmp` to check tokens - Removed user information from JSON output on `/reset` responses - Added Python 3.4 support Version 1.7.2 ------------- Released May 6th 2014 - Updated IP tracking to check for `X-Forwarded-For` header - Fixed a bug regarding the re-hashing of passwords with a new algorithm - Fixed a bug regarding the `password_changed` signal. Version 1.7.1 ------------- Released January 14th 2014 - Fixed a bug where passwords would fail to verify when specifying a password hash algorithm Version 1.7.0 ------------- Released January 10th 2014 - Python 3.3 support! - Dependency updates - Fixed a bug when `SECURITY_LOGIN_WITHOUT_CONFIRMATION = True` did not allow users to log in - Added `SECURITY_SEND_PASSWORD_RESET_NOTICE_EMAIL` configuration option to optionally send password reset notice emails - Add documentation for `@security.send_mail_task` - Move to `request.get_json` as `request.json` is now deprecated in Flask - Fixed a bug when using AJAX to change a user's password - Added documentation for select functions in the `flask_security.utils` module - Fixed a bug in `flask_security.forms.NextFormMixin` - Added `CHANGE_PASSWORD_TEMPLATE` configuration option to optionally specify a different change password template - Added the ability to specify addtional fields on the user model to be used for identifying the user via the `USER_IDENTITY_ATTRIBUTES` configuration option - An error is now shown if a user tries to change their password and the password is the same as before. The message can be customed with the `SECURITY_MSG_PASSWORD_IS_SAME` configuration option - Fixed a bug in `MongoEngineUserDatastore` where user model would not be updated when using the `add_role_to_user` method - Added `SECURITY_SEND_PASSWORD_CHANGE_EMAIL` configuration option to optionally disable password change email from being sent - Fixed a bug in the `find_or_create_role` method of the PeeWee datastore - Removed pypy tests - Fixed some tests - Include CHANGES and LICENSE in MANIFEST.in - A bit of documentation cleanup - A bit of code cleanup including removal of unnecessary utcnow call and simplification of get_max_age method Version 1.6.9 ------------- Released August 20th 2013 - Fix bug in SQLAlchemy datastore's `get_user` function - Fix bug in PeeWee datastore's `remove_role_from_user` function - Fixed import error caused by new Flask-WTF release Version 1.6.8 ------------- Released August 1st 2013 - Fixed bug with case sensitivity of email address during login - Code cleanup regarding token_callback - Ignore validation errors in find_user function for MongoEngineUserDatastore Version 1.6.7 ------------- Released July 11th 2013 - Made password length form error message configurable - Fixed email confirmation bug that prevented logged in users from confirming their email Version 1.6.6 ------------- Released June 28th 2013 - Fixed dependency versions Version 1.6.5 ------------- Released June 20th 2013 - Fixed bug in `flask.ext.security.confirmable.generate_confirmation_link` Version 1.6.4 ------------- Released June 18th 2013 - Added `SECURITY_DEFAULT_REMEMBER_ME` configuration value to unify behavior between endpoints - Fixed Flask-Login dependency problem - Added optional `next` parameter to registration endpoint, similar to that of login Version 1.6.3 ------------- Released May 8th 2013 - Fixed bug in regards to imports with latest version of MongoEngine Version 1.6.2 ------------- Released April 4th 2013 - Fixed bug with http basic auth Version 1.6.1 ------------- Released April 3rd 2013 - Fixed bug with signals Version 1.6.0 ------------- Released March 13th 2013 - Added Flask-Pewee support - Password hashing is now more flexible and can be changed to a different type at will - Flask-Login messages are configurable - AJAX requests must now send a CSRF token for security reasons - Form messages are now configurable - Forms can now be extended with more fields - Added change password endpoint - Added the user to the request context when successfully authenticated via http basic and token auth - The Flask-Security blueprint subdomain is now configurable - Redirects to other domains are now not allowed during requests that may redirect - Template paths can be configured - The welcome/register email can now optionally be sent to the user - Passwords can now contain non-latin characters - Fixed a bug when confirming an account but the account has been deleted Version 1.5.4 ------------- Released January 6th 2013 - Fix bug in forms with `csrf_enabled` parameter not accounting attempts to login using JSON data Version 1.5.3 ------------- Released December 23rd 2012 - Change dependency requirement Version 1.5.2 ------------- Released December 11th 2012 - Fix a small bug in `flask_security.utils.login_user` method Version 1.5.1 ------------- Released November 26th 2012 - Fixed bug with `next` form variable - Added better documentation regarding Flask-Mail configuration - Added ability to configure email subjects Version 1.5.0 ------------- Released October 11th 2012 - Major release. Upgrading from previous versions will require a bit of work to accommodate API changes. See documentation for a list of new features and for help on how to upgrade. Version 1.2.3 ------------- Released June 12th 2012 - Fixed a bug in the RoleMixin eq/ne functions Version 1.2.2 ------------- Released April 27th 2012 - Fixed bug where `roles_required` and `roles_accepted` did not pass the next argument to the login view Version 1.2.1 ------------- Released March 28th 2012 - Added optional user model mixin parameter for datastores - Added CreateRoleCommand to available Flask-Script commands Version 1.2.0 ------------- Released March 12th 2012 - Added configuration option `SECURITY_FLASH_MESSAGES` which can be set to a boolean value to specify if Flask-Security should flash messages or not. Version 1.1.0 ------------- Initial release ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/CONTRIBUTING.rst0000644000175100001640000001167200000000000020371 0ustar00runnerdocker00000000000000.. _contributing: =========================== Contributing =========================== .. highlight:: console Contributions are welcome. If you would like add features or fix bugs, please review the information below. One source of history or ideas are the `bug reports`_. There you can find ideas for requested features, or the remains of rejected ideas. If you have a 'big idea' - please file an issue first so it can be discussed prior to you spending a lot of time developing. New features need to be generally useful - if your feature has limited applicability, consider making a small change that ENABLES your feature, rather than trying to get the entire feature into Flask-Security. .. _bug reports: https://github.com/Flask-Middleware/flask-security/issues Checklist --------- * All new code and bug fixes need unit tests * If you change/add to the external API be sure to update docs/openapi.yaml * Additions to configuration variables and/or messages must be documented * Make sure any new public API methods have good docstrings, are picked up by the api.rst document, and are exposed in __init__.py if appropriate. * Add appropriate info to CHANGES.rst Getting the code ---------------- The code is hosted on a GitHub repo at https://github.com/Flask-Middleware/flask-security. To get a working environment, follow these steps: #. (Optional, but recommended) Create a Python 3.6 (or greater) virtualenv to work in, and activate it. #. Fork the repo `Flask-Security `_ (look for the "Fork" button). #. Clone your fork locally:: $ git clone https://github.com//flask-security #. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature #. Change directory to flask_security:: $ cd flask_security #. Install the requirements:: $ pip install -r requirements/tests.txt #. Install pre-commit hooks:: $ pre-commit install #. Develop the Feature/Bug Fix and edit #. Write Tests for your code in:: tests/ #. When done, verify unit tests, syntax etc. all pass:: $ pytest tests $ pre-commit run --all-files $ python setup.py build_sphinx compile_catalog #. Use tox:: $ tox # run everything CI does $ tox -e py38-low # make sure works with older dependencies $ tox -e style # run pre-commit/style checks #. When the tests are successful, commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature #. Submit a pull request through the GitHub website. #. Be sure that the CI tests and coverage checks pass. Updating the Swagger API document ---------------------------------- When making changes to the external API, you need to update the openapi.yaml formal specification. To do this - install the swagger editor locally:: $ npm -g install swagger-editor-dist http-server Then in a browser navigate to:: file:///usr/local/lib/node_modules/swagger-editor-dist/index.html# Edit - it is a WYSIWYG editor and will show you errors. Once you save (as yaml) you need to look at what it will render as:: $ python setup.py build_sphinx $ http-server -p 8081 Then in your browser navigate to:: http://localhost:8081/docs/_build/html/index.html or http://localhost:8081/docs/_build/html/_static/openapi_view.html Please note that changing ``openapi.yaml`` won't re-trigger a docs build - so you might have to manually delete ``docs/_build``. Updating Translations --------------------- If you change any translatable strings (such as new messages, modified forms, etc.) you need to re-generate the translations:: $ python setup.py extract_messages $ python setup.py update_catalog $ python setup.py compile_catalog Testing ------- Unit tests are critical since Flask-Security is a piece of middleware. They also help other contributors understand any subtleties in the code and edge conditions that need to be handled. Datastore +++++++++ By default the unit tests use an in-memory sqlite DB to test datastores (except for MongoDatastore which uses mongomock). While this is sufficient for most changes, changes to the datastore layer require testing against a real DB (the CI tests test against postgres). It is easy to run the unit tests against a real DB instance. First of course install the DB locally then:: # For postgres pytest --realdburl postgres://@localhost/ # For mysql pytest --realdburl "mysql+pymysql://root:@localhost/" Views +++++ Much of Flask-Security is concerned with form-based views. These can be difficult to test especially translations etc. In the tests directory is a stand-alone Flask application ``view_scaffold.py`` that can be run and you can point your browser to it and walk through the various views. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611628843.5759041 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/0000755000175100001640000000000000000000000023163 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628843.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/PKG-INFO0000644000175100001640000001254300000000000024265 0ustar00runnerdocker00000000000000Metadata-Version: 1.2 Name: Flask-Security-Too Version: 4.0.0 Summary: Simple security for Flask apps. Home-page: https://github.com/Flask-Middleware/flask-security Author: Matt Wright & Chris Wagner Author-email: jwag.wagner+github@gmail.com License: MIT Project-URL: Documentation, https://flask-security-too.readthedocs.io Project-URL: Releases, https://pypi.org/project/Flask-Security-Too/ Project-URL: Code, https://github.com/Flask-Middleware/flask-security Project-URL: Issue tracker, https://github.com/Flask-Middleware/flask-security/issues Description: Flask-Security =================== .. image:: https://github.com/Flask-Middleware/flask-security/workflows/tests/badge.svg?branch=master&event=push :target: https://github.com/Flask-Middleware/flask-security .. image:: https://codecov.io/gh/Flask-Middleware/flask-security/branch/master/graph/badge.svg?token=U02MUQJ7BM :target: https://codecov.io/gh/Flask-Middleware/flask-security :alt: Coverage! .. image:: https://img.shields.io/github/tag/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/releases .. image:: https://img.shields.io/pypi/dm/flask-security-too.svg :target: https://pypi.python.org/pypi/flask-security-too :alt: Downloads .. image:: https://img.shields.io/github/license/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/blob/master/LICENSE :alt: License .. image:: https://readthedocs.org/projects/flask-security-too/badge/?version=latest :target: https://flask-security-too.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/python/black .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white :target: https://github.com/pre-commit/pre-commit :alt: pre-commit Quickly add security features to your Flask application. Notes on this repo ------------------ This is a independently maintained version of Flask-Security based on the 3.0.0 version of the `Original `_ Goals +++++ * Regain momentum for this critical piece of the Flask eco-system. To that end the the plan is to put out small, frequent releases starting with pulling the simplest and most obvious changes that have already been vetted in the upstream version, as well as other pull requests. This was completed with the June 29 2019 3.2.0 release. * Continue work to get Flask-Security to be usable from Single Page Applications, such as those built with Vue and Angular, that have no html forms. This is true as of the 3.3.0 release. * Use `OWASP `_ to guide best practice and default configurations. * Be more opinionated and 'batteries' included by reducing reliance on abandoned projects and bundling in support for common use cases. * Follow the `Pallets `_ lead on supported versions, documentation standards and any other guidelines for extensions that they come up with. * Any other great ideas. Contributing ++++++++++++ Issues and pull requests are welcome. Other maintainers are also welcome. Unlike the original Flask-Security - issue pull requests against the *master* branch. Please consult these `contributing`_ guidelines. .. _contributing: https://github.com/Flask-Middleware/flask-security/blob/master/CONTRIBUTING.rst Installing ---------- Install and update using `pip `_: :: pip install -U Flask-Security-Too Resources --------- - `Documentation `_ - `Releases `_ - `Issue Tracker `_ - `Code `_ Keywords: flask security Platform: any Classifier: Environment :: Web Environment Classifier: Framework :: Flask Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Development Status :: 4 - Beta Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628843.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/SOURCES.txt0000644000175100001640000001546000000000000025055 0ustar00runnerdocker00000000000000.editorconfig AUTHORS CHANGES.rst CONTRIBUTING.rst LICENSE MANIFEST.in README.rst babel.ini pytest.ini setup.cfg setup.py tox.ini Flask_Security_Too.egg-info/PKG-INFO Flask_Security_Too.egg-info/SOURCES.txt Flask_Security_Too.egg-info/dependency_links.txt Flask_Security_Too.egg-info/not-zip-safe Flask_Security_Too.egg-info/requires.txt Flask_Security_Too.egg-info/top_level.txt docs/.gitignore docs/Makefile docs/api.rst docs/authors.rst docs/changelog.rst docs/conf.py docs/configuration.rst docs/contributing.rst docs/customizing.rst docs/features.rst docs/index.rst docs/models.rst docs/openapi.yaml docs/patterns.rst docs/quickstart.rst docs/requirements.txt docs/spa.rst docs/two_factor_configurations.rst docs/_static/logo-owl-105.png docs/_static/logo-owl-68.png docs/_static/logo-owl-full-240.png docs/_static/logo-owl-full.png docs/_static/openapi_view.html flask_security/__init__.py flask_security/babel.py flask_security/changeable.py flask_security/cli.py flask_security/confirmable.py flask_security/core.py flask_security/datastore.py flask_security/decorators.py flask_security/forms.py flask_security/mail_util.py flask_security/password_util.py flask_security/passwordless.py flask_security/phone_util.py flask_security/quart_compat.py flask_security/recoverable.py flask_security/registerable.py flask_security/signals.py flask_security/totp.py flask_security/twofactor.py flask_security/unified_signin.py flask_security/utils.py flask_security/views.py flask_security/models/__init__.py flask_security/models/fsqla.py flask_security/models/fsqla_v2.py flask_security/templates/security/_macros.html flask_security/templates/security/_menu.html flask_security/templates/security/_messages.html flask_security/templates/security/base.html flask_security/templates/security/change_password.html flask_security/templates/security/forgot_password.html flask_security/templates/security/login_user.html flask_security/templates/security/register_user.html flask_security/templates/security/reset_password.html flask_security/templates/security/send_confirmation.html flask_security/templates/security/send_login.html flask_security/templates/security/two_factor_setup.html flask_security/templates/security/two_factor_verify_code.html flask_security/templates/security/us_setup.html flask_security/templates/security/us_signin.html flask_security/templates/security/us_verify.html flask_security/templates/security/verify.html flask_security/templates/security/email/change_notice.html flask_security/templates/security/email/change_notice.txt flask_security/templates/security/email/confirmation_instructions.html flask_security/templates/security/email/confirmation_instructions.txt flask_security/templates/security/email/login_instructions.html flask_security/templates/security/email/login_instructions.txt flask_security/templates/security/email/reset_instructions.html flask_security/templates/security/email/reset_instructions.txt flask_security/templates/security/email/reset_notice.html flask_security/templates/security/email/reset_notice.txt flask_security/templates/security/email/two_factor_instructions.html flask_security/templates/security/email/two_factor_instructions.txt flask_security/templates/security/email/two_factor_rescue.html flask_security/templates/security/email/two_factor_rescue.txt flask_security/templates/security/email/us_instructions.html flask_security/templates/security/email/us_instructions.txt flask_security/templates/security/email/welcome.html flask_security/templates/security/email/welcome.txt flask_security/translations/flask_security.pot flask_security/translations/pwl.txt flask_security/translations/ca_ES/LC_MESSAGES/flask_security.mo flask_security/translations/ca_ES/LC_MESSAGES/flask_security.po flask_security/translations/da_DK/LC_MESSAGES/flask_security.mo flask_security/translations/da_DK/LC_MESSAGES/flask_security.po flask_security/translations/de_DE/LC_MESSAGES/flask_security.mo flask_security/translations/de_DE/LC_MESSAGES/flask_security.po flask_security/translations/es_ES/LC_MESSAGES/flask_security.mo flask_security/translations/es_ES/LC_MESSAGES/flask_security.po flask_security/translations/eu_ES/LC_MESSAGES/flask_security.mo flask_security/translations/eu_ES/LC_MESSAGES/flask_security.po flask_security/translations/fr_FR/LC_MESSAGES/flask_security.mo flask_security/translations/fr_FR/LC_MESSAGES/flask_security.po flask_security/translations/hy_AM/LC_MESSAGES/flask_security.mo flask_security/translations/hy_AM/LC_MESSAGES/flask_security.po flask_security/translations/ja_JP/LC_MESSAGES/flask_security.mo flask_security/translations/ja_JP/LC_MESSAGES/flask_security.po flask_security/translations/nl_NL/LC_MESSAGES/flask_security.mo flask_security/translations/nl_NL/LC_MESSAGES/flask_security.po flask_security/translations/pl_PL/LC_MESSAGES/flask_security.mo flask_security/translations/pl_PL/LC_MESSAGES/flask_security.po flask_security/translations/pt_BR/LC_MESSAGES/flask_security.mo flask_security/translations/pt_BR/LC_MESSAGES/flask_security.po flask_security/translations/pt_PT/LC_MESSAGES/flask_security.mo flask_security/translations/pt_PT/LC_MESSAGES/flask_security.po flask_security/translations/ru_RU/LC_MESSAGES/flask_security.mo flask_security/translations/ru_RU/LC_MESSAGES/flask_security.po flask_security/translations/tr_TR/LC_MESSAGES/flask_security.mo flask_security/translations/tr_TR/LC_MESSAGES/flask_security.po flask_security/translations/zh_Hans_CN/LC_MESSAGES/flask_security.mo flask_security/translations/zh_Hans_CN/LC_MESSAGES/flask_security.po requirements/dev.txt requirements/docs.txt requirements/tests.txt tests/__init__.py tests/conftest.py tests/test_changeable.py tests/test_cli.py tests/test_common.py tests/test_configuration.py tests/test_confirmable.py tests/test_context_processors.py tests/test_csrf.py tests/test_datastore.py tests/test_entities.py tests/test_hashing.py tests/test_misc.py tests/test_passwordless.py tests/test_recoverable.py tests/test_registerable.py tests/test_response.py tests/test_trackable.py tests/test_two_factor.py tests/test_unified_signin.py tests/test_utils.py tests/view_scaffold.py tests/templates/_messages.html tests/templates/_nav.html tests/templates/index.html tests/templates/register.html tests/templates/custom_security/change_password.html tests/templates/custom_security/forgot_password.html tests/templates/custom_security/login_user.html tests/templates/custom_security/register_user.html tests/templates/custom_security/reset_password.html tests/templates/custom_security/send_confirmation.html tests/templates/custom_security/send_login.html tests/templates/custom_security/tf_setup.html tests/templates/custom_security/tf_verify.html tests/templates/custom_security/us_setup.html tests/templates/custom_security/us_signin.html tests/templates/custom_security/us_verify.html tests/templates/custom_security/verify.html tests/templates/security/email/reset_instructions.html././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628843.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/dependency_links.txt0000644000175100001640000000000100000000000027231 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628811.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/not-zip-safe0000644000175100001640000000000100000000000025411 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628843.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/requires.txt0000644000175100001640000000020300000000000025556 0ustar00runnerdocker00000000000000Flask>=1.1.1 Flask-Login>=0.4.1 Flask-Principal>=0.4.0 Flask-WTF>=0.14.3 email-validator>=1.1.1 itsdangerous>=1.1.0 passlib>=1.7.2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628843.0 Flask-Security-Too-4.0.0/Flask_Security_Too.egg-info/top_level.txt0000644000175100001640000000002500000000000025712 0ustar00runnerdocker00000000000000flask_security tests ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/LICENSE0000644000175100001640000000213700000000000016731 0ustar00runnerdocker00000000000000MIT License Copyright (C) 2012-2019 by Matthew Wright Copyright (C) 2019-2019 by Chris Wagner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/MANIFEST.in0000644000175100001640000000073400000000000017463 0ustar00runnerdocker00000000000000include .editorconfig include AUTHORS include CHANGES.rst include CONTRIBUTING.rst include LICENSE include README.rst include babel.ini include pytest.ini include tox.ini include requirements/*.txt graft docs graft flask_security/templates graft flask_security/translations graft tests prune tests/.pytest_cache prune tests/.DS_Store recursive-exclude tests/.pytest_cache * exclude .coverage tests/.coverage prune docs/_build prune scripts prune examples global-exclude *.pyc ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1611628843.595904 Flask-Security-Too-4.0.0/PKG-INFO0000644000175100001640000001254300000000000017023 0ustar00runnerdocker00000000000000Metadata-Version: 1.2 Name: Flask-Security-Too Version: 4.0.0 Summary: Simple security for Flask apps. Home-page: https://github.com/Flask-Middleware/flask-security Author: Matt Wright & Chris Wagner Author-email: jwag.wagner+github@gmail.com License: MIT Project-URL: Documentation, https://flask-security-too.readthedocs.io Project-URL: Releases, https://pypi.org/project/Flask-Security-Too/ Project-URL: Code, https://github.com/Flask-Middleware/flask-security Project-URL: Issue tracker, https://github.com/Flask-Middleware/flask-security/issues Description: Flask-Security =================== .. image:: https://github.com/Flask-Middleware/flask-security/workflows/tests/badge.svg?branch=master&event=push :target: https://github.com/Flask-Middleware/flask-security .. image:: https://codecov.io/gh/Flask-Middleware/flask-security/branch/master/graph/badge.svg?token=U02MUQJ7BM :target: https://codecov.io/gh/Flask-Middleware/flask-security :alt: Coverage! .. image:: https://img.shields.io/github/tag/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/releases .. image:: https://img.shields.io/pypi/dm/flask-security-too.svg :target: https://pypi.python.org/pypi/flask-security-too :alt: Downloads .. image:: https://img.shields.io/github/license/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/blob/master/LICENSE :alt: License .. image:: https://readthedocs.org/projects/flask-security-too/badge/?version=latest :target: https://flask-security-too.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/python/black .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white :target: https://github.com/pre-commit/pre-commit :alt: pre-commit Quickly add security features to your Flask application. Notes on this repo ------------------ This is a independently maintained version of Flask-Security based on the 3.0.0 version of the `Original `_ Goals +++++ * Regain momentum for this critical piece of the Flask eco-system. To that end the the plan is to put out small, frequent releases starting with pulling the simplest and most obvious changes that have already been vetted in the upstream version, as well as other pull requests. This was completed with the June 29 2019 3.2.0 release. * Continue work to get Flask-Security to be usable from Single Page Applications, such as those built with Vue and Angular, that have no html forms. This is true as of the 3.3.0 release. * Use `OWASP `_ to guide best practice and default configurations. * Be more opinionated and 'batteries' included by reducing reliance on abandoned projects and bundling in support for common use cases. * Follow the `Pallets `_ lead on supported versions, documentation standards and any other guidelines for extensions that they come up with. * Any other great ideas. Contributing ++++++++++++ Issues and pull requests are welcome. Other maintainers are also welcome. Unlike the original Flask-Security - issue pull requests against the *master* branch. Please consult these `contributing`_ guidelines. .. _contributing: https://github.com/Flask-Middleware/flask-security/blob/master/CONTRIBUTING.rst Installing ---------- Install and update using `pip `_: :: pip install -U Flask-Security-Too Resources --------- - `Documentation `_ - `Releases `_ - `Issue Tracker `_ - `Code `_ Keywords: flask security Platform: any Classifier: Environment :: Web Environment Classifier: Framework :: Flask Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Development Status :: 4 - Beta Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/README.rst0000644000175100001640000000650000000000000017411 0ustar00runnerdocker00000000000000Flask-Security =================== .. image:: https://github.com/Flask-Middleware/flask-security/workflows/tests/badge.svg?branch=master&event=push :target: https://github.com/Flask-Middleware/flask-security .. image:: https://codecov.io/gh/Flask-Middleware/flask-security/branch/master/graph/badge.svg?token=U02MUQJ7BM :target: https://codecov.io/gh/Flask-Middleware/flask-security :alt: Coverage! .. image:: https://img.shields.io/github/tag/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/releases .. image:: https://img.shields.io/pypi/dm/flask-security-too.svg :target: https://pypi.python.org/pypi/flask-security-too :alt: Downloads .. image:: https://img.shields.io/github/license/Flask-Middleware/flask-security.svg :target: https://github.com/Flask-Middleware/flask-security/blob/master/LICENSE :alt: License .. image:: https://readthedocs.org/projects/flask-security-too/badge/?version=latest :target: https://flask-security-too.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/python/black .. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white :target: https://github.com/pre-commit/pre-commit :alt: pre-commit Quickly add security features to your Flask application. Notes on this repo ------------------ This is a independently maintained version of Flask-Security based on the 3.0.0 version of the `Original `_ Goals +++++ * Regain momentum for this critical piece of the Flask eco-system. To that end the the plan is to put out small, frequent releases starting with pulling the simplest and most obvious changes that have already been vetted in the upstream version, as well as other pull requests. This was completed with the June 29 2019 3.2.0 release. * Continue work to get Flask-Security to be usable from Single Page Applications, such as those built with Vue and Angular, that have no html forms. This is true as of the 3.3.0 release. * Use `OWASP `_ to guide best practice and default configurations. * Be more opinionated and 'batteries' included by reducing reliance on abandoned projects and bundling in support for common use cases. * Follow the `Pallets `_ lead on supported versions, documentation standards and any other guidelines for extensions that they come up with. * Any other great ideas. Contributing ++++++++++++ Issues and pull requests are welcome. Other maintainers are also welcome. Unlike the original Flask-Security - issue pull requests against the *master* branch. Please consult these `contributing`_ guidelines. .. _contributing: https://github.com/Flask-Middleware/flask-security/blob/master/CONTRIBUTING.rst Installing ---------- Install and update using `pip `_: :: pip install -U Flask-Security-Too Resources --------- - `Documentation `_ - `Releases `_ - `Issue Tracker `_ - `Code `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/babel.ini0000644000175100001640000000041700000000000017471 0ustar00runnerdocker00000000000000# Extraction from Python source files [python: **.py] encoding = utf-8 # Extraction from Jinja2 templates [jinja2: **/templates/**.html] encoding = utf-8 extensions = jinja2.ext.autoescape, jinja2.ext.with_ [jinja2: **/templates/**.txt] extensions = jinja2.ext.with_ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611628843.5759041 Flask-Security-Too-4.0.0/docs/0000755000175100001640000000000000000000000016651 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/.gitignore0000644000175100001640000000000700000000000020636 0ustar00runnerdocker00000000000000_build ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/Makefile0000644000175100001640000001273400000000000020320 0ustar00runnerdocker00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-Security.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-Security.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-Security" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-Security" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1611628843.5759041 Flask-Security-Too-4.0.0/docs/_static/0000755000175100001640000000000000000000000020277 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/_static/logo-owl-105.png0000644000175100001640000004765700000000000023072 0ustar00runnerdocker00000000000000PNG  IHDRiuS IDATx UYp񆢀W]üyHE -RIMP) Nh8[hay-Vj,x-ED԰|7g}>}c;{y|榭[n .6m4??N׻֭[K/tڼy^7׼. Z;j\JWoW%\2}ӟ^:w|;ߙNַ^`-Mo:n~s{NW|3e{x~3#9眡??<}k_ wmM4ӝtӵ}:s b6ug _tE7W0wWUvP80 Gw}:\tQ]wuidG㽵.ϱ"}w!y< o``b?ϐ6 ﬧ3y<} _̠ p=ơ׺ֵ.CEE^jWPU!N1FG_6tk^GG#w /L(=S0~f5 ͷh?72i[lM7эpMnri[az|EјΩmo{Ԟ{9bz!q GEV//CIezIȈ4ou[M?C?4 W煂_}ip u TBZ+|CZ郟~{3pc=37g/r:c}\;*v mi떷XgvZ$Fnq[ '9-t_`vѷpwyGw >tZe> BN;m~eu^/}i GяN/x >_ q!!¨y!p79]zi׿0CLKKA=$k̴ʯzdz_]`({Խk02W?׿A fM,O!_WSw]qLSe'>qenI.ch_4B;8}c:M0'$+c0`3`F' Ea#Pz=aJQߘDf!3঑hG0l$\h\ 8  L@8AIƐfc$E$ozիF'bXVS_x;4ka,<{p i$g>󙣭o}! L)6t"F`o La64?/p !|S..mZIiӻa:h@JC)H$U=ҍ(&k$][,68$c"M\O/N%la0{)/#ʤ%4$P ADt6_yBD]`\O|xn#'eo!(/yg}:C}'$υp|x6G~k_;Qkf",ff0{|&9#8# "Vf5Mm C0]!*G?z Cidѵcق!@PE;`yB`!d!,m2qGh@Hi &!HDgU7C&ӵ׋Ifjh{#@@# D1 4 `Aɟl;i\q֗Ei4l&zPsI_jڡ~A_G%x%A@}Ң__\wrk% l * xQ̂8'9D"5l;qfx b#6))!8CJ\8S-fų$큿3BvwG3 "Wh9Ap-/~@VMozӀwgG HiL O1Ii/W'fbiE(dB2L hc"Ch_BA%$i"՞>f SOf4VDoza  )6Ly4(vK"2ip ?` [IIAE˄ )d=.mf#H&!#CaZ9l "    Äinp/gnԏߘ6D􁸞Gl00F`!dM[}ce7{}G;޷wgusHB )@#:˜)0dR901i@љOΘ$"}0#:dZBI:b3H~)g&1y41xoB~@XLD0yYwG2p g`<28d1EÌdb01$H/~GhR 6FB:)f19GDO̔f)3g^H>)#8Fi pQ|͒dZ$hb/RLy0&A|B"!1R!Ary ,bΒ2$i6Lxh$?: ' gPda! L)_,UP,A#Lk>ɬm\kcsy&M>!$ b$t2 \ `N8aS8I@hD1UK1i`@ ̩1,Db6I >|o^0sL>|sq~ K1b#8B 3̧@ڃN0:ąw[Gj-&`o݊\(fkr1Ci% Usdgf\&֐@mnK4Ѿ(Ϝ|+!2">4^A@0~3@aI]gy-h—RpDP(!fkÚTgafH?I9kq `8|.0;C@> 4 0Ϣ];m5Pm$܌A- "HxM{~'pgs`nZ™B(aI{NjfD4KBp54RAIT(Y2[K{0%vi)׮O3" n[_L0xhfMǍ‡ɧņ, ڙk؈ڐln#2fR ,q c[ c 08}ߙ3L&^k=GE_1MK1qh( 8G+amv&8 $Q`!˹99,g,XDvԦ$avua`+`ew3տmfsnǚtBI H?D1)@M _ihE|g-Dg3ge!'e!.m}۾ܚ(34E;f#O~! $ BL/!诠nG.#ί.ͤ^ 'toEw ,$O=UΗfH449H!6 xKCtoY7%,|и2+} [-My5#D 1 ahT~Ȏbm /&1q 8jml=hoDsiEJ1i DpoD04#4[߈뻠gY YNITAAJ.'Da|FB"ܥg8dA4g0'*nF{MhԒ2#2݃pLiP`D..) =}]0#\D ?A]D|π6wIS6^ZIiLâ|ƲcSK:RH -n>m#MCo %DXK-V1ͱaMLTLc(3P`̩}@h h |Z>X?F2lҘ9i"|jc۠vo-JR! [>˦]i{ P3I"x(Y!B(, aZdjl(NhRIV3_3_ݙkLx9.'ڹHf]-fKФKU"#]Z],@Rr!i"`85x≣>pLp?O^ 0YVt ,z< h7?V:)2>ĴIҚ@!bl@+?G ryoN{{L䅯"&H7vy`A\1$Xl RO0h%s_ TLnice%a< nl$$> &pE||FN!47V3iO_Z/”(I#"bߚ"`~aDLge*i-&X`Tѫ}sx"XZjp"5 Y+kΟ)pΚF٦%\2œr0 AI$4a TcZmk%8)0IƔvJǘfK|Tej Okh zLzgT+$On>ְ) 1"z/<""ⵀfF`BK$Txl6k`" GP0ՆM;i9#8B"IC}osNrV.)Q>:F 9I䠙0A Ɇ|A p"* D& I0!޳T*B8M3[m7-Ч6I;t!(";07LEj+a (|U;pRZ~a,Cѣ^[]Y]}m>c˙A 6gV"le̗1DҎ)#$_AskSpLKH< ht#P'6Jop#Yj|%4䠕4^Sdv]b L8; LMzޘ"-КTF H>MrhL$̏.s䂋YJ[Jh_aBݣ!4zehc@s3AKnW#& ?iӊ)&-A㤔f9q_œHH#Lm(tE, X&0 .&Aͺ3@4,ĔBCn@ (_gu1U[ɜhP^eOR:f2ݯ >tA E ; b<7i꾉HApvӘJw&S6 KOᴅ,.3_VUdbT P'SOhC -uLil%"3P7dp=Cn&(m(Dܦ]eW b!/ꪢFb3X44hfQjb if1NBeũM(@YvPg:@e_ü4Sylh29as\1'm.M a@lOxI[u^|k)sWSBhQ:mq!{$=D pH[4hPkhb h)3[UH [أm~kOR ވ-6R h0CK赮ё2Yvt,@ K)O`C;41bF*݊Kho ʹYZdXYfjEE+V)sxpL[#ߙ4ʮ\Jug2YLrHe%=9p҇Jj1 M:>jB x]f 6ઔ@4!cb?F ү0\#ӕ3f6{NXa;ي.whu)hI6SGR&JC bya x)V!Lf)گV~iZɊ\P?04h4`o)ӶsuTJ-W9# w1`>-H_ZSZCZMY;e#`.HY{/HhU;vUG*YLE!B YȖHa3\ ҠAs`16y~v{ ˜4MYʒ'KIN!X:AOV흘Ly)0_{s6rͮƁ4L[L-ڪd 1d1f@Z&m[*f"74Z&XȤM>b*+nw\Acj\%8g6YA (hh6 `i! hNa|[5;F'=7͊&dp,B ^A __fU߂x.U֠0 }PX|&36i}Eቱ םfRfuB-&dK+HoVړ/6 i4'^!@s2[^kDfedFi`ggv#bg; Dx `ӄc=I,B h.mAF їI\MUsAd !W>RJsFGUWeB>9F;\@JJ`vh^LGr!꿰&~ W?m販Zml)Xkڧ׽u+e 1lMa2aĀ Bb]m߾+34\a lg#ɒ 5 "k%UF8"Uw~bъ8Uo k4(r4K >xL]8͜pg~(& ,SN U5cWlkw=f*lRgLhp b9gRBYS)c>\gH`8im 2Jxʲ}%tpymN$ ~)qԑs-Fa8ʂ0\OL7<* ZFf*Zhvfu;&5 lC:04SeHB3GXD`;8~#vmQE380v݀VjrMɭ),49m}#%@ju,4<-}AЎ6yZUxpGBsG;H u hG%$ؙ $ WV!+Μh/2G:M$|U4;&V։a+,ttVRFc؂ǿ=Y KFl.Ȯ g Q2}uvMD`'kSbwm)!zL{T+G| !" )$h!XwsE kzgk7SKk_ͦy+R&-Dʴyp__aV`Ɨ"»38Hv~8'$1X-,@T7;Pl=6(CImWUR]8ˆ*T 蘉i eКhkw2`kiY1cA/Dl+cչZ2 .s]&pșVOjTf1K:$-ޑJ7l [)L{$7Ү@L_[c*闞#I/턐UJ`njV3NaҞUɜڔ\,2_p=VfXq!L`ZBS\η$x ;I1 H=! n涥~Zߕ}6P DUwm!qo-3dy>:䃛zFѰ~8вQ4HY) 5MywjL3HQ}d+BH?ݸŇ T4# u &l.^s=Vy$7߇af4C]ΐmДIEx7Y2?c̘Ӝ$+!W-{]F 6Fr6Y0ϑ,A5N$Y#qX 6<褼RaiMfּT15n Z" <; L^AY.Ï7Q̧v{ާerQEeTX4ʪt =F]ǜ& L MkU$qĈH[D􁘴)AU-a3%]4`j%lOJfxDvU(рU犃`Ў sv Ao[5wtwEhG 1FH<_Οhʽ#4p}cRy"yB B,7f2mEVlK "‹od>9W83Gi8/B„BxJZ_,VpjڙMSde& !Evb])\n)}h:VK֒ }Gr֗iBE;Rrmpg˟ɴAM0jGH0xtЇnhS'eU0rTq& +/zq[v5PnZ5 ,!p䨅%-c07@|K^ܖC _ w/= qN&F髂dgÉ(:6}l1YУ 3b#xpST9H~9'R^~9vi֬a+o!jYKJeCK,_ a:+Nn?DkM}OsD~}T-{w&$ҭ &2feao3$W"-*jG FZ8L"$SI;6MM3BNQLM idz #\tEFamvwU̼D.Xܩeǂ]3.-p0;*\ga0n!H&"F:P1XBLJ҉1Nlʟ|//ˆIK[8Uq'J>PVc'}#zw48!KK.M3ѐP3.Znb`sE|VFPŝbw964309k)IKpo*0"̀ ]uclfɴA5YL'gV}  ߩْDGF``,>CA1  f k|DDwG ŒuT-B#L<+2U73Of&Z\qG`P̈/YpCs~Z1mVn$2L:;̖WQiP[B$$X!Ɉymrvf@#{~3Zs-x3`ƘTa-6><6̤jb]]8f;st5 8Y&A018 #|qWW6>GJI.MiKsI+2`3f; 1VS<$88C Y MJȝ7X`@;:l7:<-L*5 ~1 ~2ڑ,>ed@]~`! q !&˜h&So=D3夝N0Y%<1Ҟgp?O4O%HƼûw#ڷa;d4ۨ[U'rQG珗&$BX 1ׇ[1bHWj m$}r ISB` )E{\s8)Bp&d f&@2$ĬrML#m ָT9w0)hK:HV5R1;D23/;䓇+͘">a錉2h[@֮JdhMߩaüv) >3 hЀ1jA+hZ(pq /KT2d+SЮ:Ȕ'K9ӦB#&UL6v>C0N@AMzhHeXgLECFQs+_,8;S LE飉b[u&m<*nZI^nSXVWĄsHe 62K9bc4mWg1L8Ṥ>39=GӠl҉4-[kN@>3"𳛑YC pmJaJU@&FS,y/s$UT=]A=R cn4[O#fU?`aF$su"mY4}] BiiK`ht}Y IA0HVS/seHs|bSYN&w1I1=)qi1m#>;bUhy#K&tvti)@U9 %AgM@cTƀ&~'}@a< {Qt9&R3PDx%I`j!&bFmW9ܠ0(Z&>Wa&0T|du4-?t]psTl=Vf,Sl40{8QRaH-ƔB|m1CPeXbH,B{oL]QZW=_'K{h*qe2:_IT3s9AlBRpfv꾑D:餓ȻQDHN'5H#$VTD4. >N ߙC=a,?n6$̨j tr~7af޳F)p1zi?&5Ԇ9aʷ[֋]z.lZ:T^IDATiàbLga96]iJ4gGaFId(}&Fز]Mt0οWb1!D"g,I>̿WfKOk}jk%NiBTTsdrDbk>ۘ3y .06xfF}g3%Dx0  Z`m}"KiP >@Lx,4tK+h訽nE]h yMR"Yߘ@vJBDT-ftރpL ) hae0$Byd!9c^IY2WD녏U>36L%.}3sU[<;;DTH5 Ddt.ۍM U#R[H‚"00De,YExp ?pfR c}fO3n@ϛE;`6:lǤ t46c+,[t)$TH%Bè ݲfDiBe;a Fģq`6 HE*4gJ1 ?8!(\ny~lɴ-da#ۮ-64P'퓦U[#ITyh_;9}(0HGjue0`1FtPbu N*J3)" ˃HcF/Ǡ$5&wrGCLk*ۖ66Z/PרrϫZr駟fDb;ON@3e>/5s Gh)"BՄIÔr%t.{#0%ekNSO]9 c!àڛiU9η =EտT^O9-FEg5a3,+F37-5T`y-IL eAڔ{%4닶*ihB1#Dt &%xM*tr1I?J+mS@vi{%O i6GIk}#MhiQgUGh>I;X3\ >_juvg>wL# bن(p4YK絙o#J0:G6DHuAݶ((ϻdle.%\Ɣc-t衇n5frzHe!$n$}ħ53]x@ii*W#rkҦD6.ah !c^u_h_zt`eЉp1p@|LhUY%﨟JI` >Lٷ,K Xtol#V٪Ɉ$CfRmch@H87YEh8jI+ I1e;0fSc8h!7 ̹Ɗm%``p!hXQ谬,>mѧ5MaC6ŏtB"))NCȊyAa63Lf S0i/D1t#j[:^LhK*~6 ` &#b 0Pj0a 1c]Y {U 1""1AJt"Յ~`CG ͤ"E=G vdf1wypgi:@;0AH8":@PxaWG]Tmꦹ: ~&M[b Ry{n!Y&J$F;MY'FHhЀU'VʟByn #3t[i2ݩhj!"" 0<߈ha@4  ɑO0!R { BXg&&M`-C˜mv L,`ßp<#"BX/!* 5ZNZJг]u+c!h|"*k?bZxLJ:OI[!D}̄3l! !%pXK$hw;& ѱ 3ZpQ*L"p2뿲SФA\UH1;\i W.L"R{0[>'ɶT]fP0iMDi]WiG ؘ1@hR(:/r5hԞys-C Ѥ-fN3,!-VG)~Ψ,x! /hX`; LhS;My rD?͎9<@kH-MĨT4,-f>a  XLgh~wB}l8u ^/QwSm~ы^;5H340#4h[~v'zיI+! fNsy4ӌ\g; @4LJ|j? 0!YTDG >8IhЪ7]Aw= 0|%?[مڃo! $JBvnC9pU(C&L&;)#!&{:.uzw !̝ݻi!XS,uyD+̄SW:ш# ¿ ڤ8mWnsh t8ngFB$$ \kZ%b~~4Wh A t ؠ /twՇ0S:"CŽ;&Hq[h:>vxۭz೭8 MH $;b=R.h%Qd̄aTGYL$,IY'汍VQTii3)VimO%˃q# )\B}W]^4Ҵ3%0{$c2FP2SHCTGLFyNKjh!BeivM*Hrp cZ VQ)BIENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/_static/logo-owl-68.png0000644000175100001640000002234500000000000023005 0ustar00runnerdocker00000000000000PNG  IHDRDd? IDATxgmUu UPTR{+jKbCDMl1j ^AT`CPE/7o=gss*sg9c /ܶnM/=sMn2?vii۶maÆA?C}g˦]veڼ뮻N_.h?m޼y:tW 7mڴ |ߟ}kO׺ֵ+r .>я@_‹Xֻ #w_;<]|b ƽ{˧O~iƍKנWY|ez8.ڈ.st;aFǢ_tm#_g׻_%/yɴ~Mtsw}CFW^y6׿5N;mo{tao[nAg uyu^km ]g>vV^SONOre|X`[LJEn}[O~;Y?i:ǽJ~g>^t[rԧ>5 O! ÅY:0.7O~r|~4/`u{@-fv\:י߬MGoi9ίykơI>w=e(#o~< i@}݇EQ]>7]z? =zSNN Ϟ}0x1CBΚ^W gyk{0_tvt?a$ŌXzֳ&I0Yl"ȝ|%*խF#}K_wO=\P/qh7~Oӗ`g>HW >׾v j18IG9C=tC9d:sF޻ np$}hoP2Nb"*YB.W=7 1\]~6|chdK9́8苎w=,G= Lcx)O1>M1?\\?hL:l ؊0tZga֭θCBY:2&4a(#HYsαA( nܴi}ݶC0mh%# ~E0f~0 6}s2lzի^Ec&$^(c7t`4 SoS~($j櫂;OA,9o_t#fEFtp縖Ї>tȼ p7,R)Oy@[twyַ5|HGGd o>SEA7a~_ apס{հ \ӆŵ҃ie=bю3W $$A()vfFv UVJ,lùXƵ}lGwj +\׆~H[8Cq]y#tҺec>X?|tȂMBP 7p7?! % .J>0R80#F8cg5p7r6q~G`dΏXX&Xrׁ<PI3'?>Ϗx|[^7,^@w< ~8^%v&fNw*2I4 +o#t JRu#F:nâL\> nY%-'b=g~.i}!c~,ajkCa Wz0b3,$pO|G=8 =F.#{S3s! Fi1kpYp_W7^ ,~:0p\ +W %[k~w(1w%JIva802|;G>1cj.ψ;\ mjLzZː6'脂,̯]z!9 PG LI`P9yck&7S6:O#;~k_ˑYx@L,a@c)ڣkFsAr$o{b2^H_ @;nQCJR-cȺP\hB!5RDT6,UJ +c)2rp5J{ֽX կ~uĪOYqaX=#Ȋ)U3<0F?1g:c R\PC^nd nуGyFY aeRkuQ#&=$cJ+(ʥu0$ ]k!@yc4R,-je@L(RX0XCy`o֎~xmN26V:zp S[ȴkOjXkȥH+jM??GYm*B,sw?"%hQo( ;oիcҹT MR [\ gGXW5l;!»_[0`K\p和drg< KR,Qc`t]XqM5/{F<KLrh܋/~GЇF@w y(6EF܊;c®QGX},e# -tMTۤ gYgMќK2n@/x _v[|[ĐvM%ekR&&&USs.`6MYi -)pD  ^S^7&Qb]Ė0-oG̶!aǴ:ڤ׹⃿YiZ ʢ:@ gm\۴ p).V G!6{Ѓ4'E9&HRPPQ m1G V)B:6bj孤D2br݌H Fd{ʐ gtBH9XC(6wSb]`Qu5 љŞ'~@ʙB7q{evHi<9CcZC<Ģ:сmRya ڳ m[QR!4˹W$kGRf6j [!fp9aX\ 。Jޱie7Mł#Jf;*lӇdE2=qyP\o}: 9yZ=sbzǚZ yPJ@QsZJ+.X%vwzԣD)av7 nj?|Ӟ6D:0䳟1tkAACQ`hj:η e8vT=Zdv/WY{LwYb[p:6HhLw @1ݬNK! v-x$3c A(ien4BMмY!oѮ3"Q0e=ĐJJ}.QkYv k팕U$U ֕7aLcՋH ɪw5 x촲cw0L;`L`BW8VsG`b2$Y(3_~5?^ސfEEAjEeecX{ a*v(Ip.))YL?9Xd!`qrs%y&W]z?91 zRXd9KZ7?i0Y[ 4ХK51 3: rͬŶ Y,t āEt.m?& f)r(%fױa ',Z68\X;Kww~lse*Fu n䇕9Php)9.SXqPS x3XbԢsk!PtKi6)#Tm؆y>0@,랯]V=iet zVB&8D$ ū;~D,>cay= ` 7p;_ӂccҖ_7AqIJ Pb%\B!"1=(Q¸̸ƺ<=Y#Q y X&Tb,;0@`CXPHhL,H 4C- B|Rk,<10d Msn%ΑEn2_ blaT8XQoǞJX2*$(&r_oˁ9 &>u)tNi۴R `1ReՐ z |䕮k B6b1 ]=ډ߬`ЖQoǽSd,4'0y-5=h>cgAE+v{8=#3@eqdƐ( f] р[X>vX0:X\ 2߮{;R)W)<c,KXXb8? 0J n'-XS.B@ mT`u`vðq.ܶfrmvmdL7C+kb0mZ )] mڗ,[ .6PĪ-lZyCߔAm` 팡0o4һ^*'qL"T l憻Ǵ+<lmLofX@-XK+S|0 9h}3!2՗]:o3zhVӗ41͝ά*2ꊢwRYu2@qdzMC!{j`b zb /UH%Ӊ'86ԏ@>,.&Fg[@50"1;&wPE=It5Lh&,HPXr0O맗6 / 4:*@5脹ȹ蘹 !Y%:2Phd5E?H.9":\y⳶ ^\zp,xÿ}I]39I&XekF J `Aj\a hXsj5K^ORbhhG6W}Lv/r?e{^'2]2[lzˠ4diQ/C&m\]uj(,L;(|wi˘[9ږ$'`(@˺ѹiB7,b QyF2-6e,7VHA;>`(_1ɤ@Zsf%O- d ` pYq7UJ%@lj1bHm͂ejKpXA T@l-뙯4ƴ`D6w]9 c@ΊF 1E[&\X_UQYy֢cTъJLj vxPuN\xo= ۴#NQkԃyEcJYev0Ú~5Us` phA^Ed(%(qG4Rj\Q 9<}U}*&<a,T`<|k+ˎ1fe;me( LA5!{PeQ ۂk\ 3z}Xba!F0٘g*%+f~FTb p%ò#E(^55}o;wܣڝV.x{;\2f6+&4u}uRM`EZn{uTk\7Zu0V[OR\46e}oE/1B_]eսECxl؀1$+A; a΅Oq8LƸ^umn߅@js!ka/mGo=c (ksY\4T| jQ #g->O+;te-Xޤ싚2SJJ%(K ĉkMNF!oE1}kZckl^=9̼_Eʦm A}督XkiQ߈R]qfՓw6ʋb+_k%r+/ՋA`^0^68쳷%Ck@2)Aؾd5|ɖcRf2g8@9oҗ&DkgukfL6'mv-mfJԷdq +1 =Q}>㾧>G[ez[Kۄ XF2)Kz#eIDATKXYXɭ<2TY)y8'6ً\~GQ#5Zu{ݣm sjCV+g߬PxSХ)>ZȽ=+`vG +'R s6h8 ((:+t< }ݮgb`0rܭO_7& 򥾶IJ>e< n5XOy,:ꨱH8T V*mh "@n2D?IXa-oH@e݇)^V W맍5WهZmh}{ 0oiúObՆo?e4%#_pNH P ؊PڂyKglMxi?,4,G h؇7"Q_M8-%[hJH` p[s@ԓ`,.eP>jsI93Y9}4BmyUprUqx9^=÷ K_حT64jBg+?0ڰ2Vh.ȱp_:($J%#C׮$})P\RޚG+j\12}VmU`1(}9LykF?\ 0F0rK_B_է\ی\:Y窌т{W儱n+ZS(훢h]^UG˾U9& l2urk|`m[nlL…Pd4!{'1 F֏ݖHqmA?zĥRT*Jbv2/{Vַio~ϚZͫ_gmV[mfW_OJ* ^y>裏6?-rdo_y+ZT*JRT*W+u[?A,ꫯ;#vkם NX*JަSO='Og';/~1dE}0ZuU#8!4wC4Ϣ^6/?o2eJ}. lp_7M%JRT*J,Xw|l5o~'\zO~bz^dA[\~& 뿚^;6V䕿}kp}4ǏoG~//wK_،0RT*JRԦx+??i; _B|=$]Z: o.0zN^+$o|csI'5_}sE5~x䑟qoq}/ 68JRT*J@(B e 7lUzUs8_כO|q]ii0a3;$Y^w}QTOUcf֬YW\檉U~`:.JRT*Jmi~??o|Rt]w57{y\JU !0iҤpt'LK13?|8۠XJϙ͎;\yRT*JRiyhPne pQ~o^wqF o_5U4. ܲEҪԿc86mqiYGX`"ӦMk?fu׍fUĿ/ ~v_XBRT*JR44xY`$owk8"ol旿ep~8t+ @H?i(9?K /,m-NqؗŵߝOyV!^ ϙ3' =3pr0 6Ql wI*JRT*Jeʋ aNvmnvQ!D?SsyȪDq j:n _2 ~ V V 5{.@xsh+lc.ekʗt_~jE3ꯚUWi kqG[uC(uш#3f!ڳyՠgxZ9JRT*J@jnPZt+_:aa]y[N`,_X4,6 ɀ˙ U )-n.M/A۩ڌ7.-A :{ E)y+_'ٽKeq C֧?H~{6݀NsAѿy[Smt JRT*JҊJou r3[X կ~uT"OXFqUV#mleZ߾;ޱx \kssO~xtB ۼ$H~)Sl{́ŹҢ8WU3>wޙ\_/Zl߸Ayf/>={v˃a٧ؗFkT*JRT*-tO~a?q8|_-"rC/첀/ᵋQ;ݙ3g6뭷^馛F~^3<Z:s_/D /lvy.]t>^r ;Cow8q󒔮qs#ؚP)5~&rnsl*0>*d_oz꩸7=zt\*JRT*JA _q!'$%Ÿuv0@/QI|\mVop ę[`LujyL0 ~}Os(S‡A;GSAo}[*4N_^bv_k]]vigʐᜣ}[8(l>Ũn栃j.f}]X{rWN 79r~9#wuW9vvMU_JRT*8l;Z"g)@%ĕsox7Mz+ߕz뮋i8%Q0Zn9L26.}\82W~󺧟~9Cv)VTc5q~2p?"HN0/  `-Q, 0+v 6p”ᄏKbY}Yk'ͅ+wxs}NgGUpvN/PiG=˺~F{Fm;+.JRT* we'ܙ /n b,RqxjpyAЬY!??V[DƊYqg:.dnjׁGNO@~s GX^{ XΫVQiر1u\qof]wN8!̜Mr~0p46 6̟=yKk  68m|C X@؄o͑GPז[lokf]l(|*w;eW"9`|gr\_6 Lhi#1Hu9` Ÿ=G).JRT*Bht[.T]|^[omM@0Q ٳMa,`;gN1@ZqcU\d`̭qS-k`rc5 XKŵlP?y7jA6fgk#׍78+$fwp*c5F~Ek:cT˚RV-6 lhGʍj㞧n .rˍ JRT*Jmik X-SIQ9 s A]s%Hr )>*~mk<M] 2*G@+'ap- @׎sNԂIs|Ñl\Ʃc֖Rn*`n:dh6( s48֎1caMPjN\ul7x˗hۜ6+ljd1O P9c\dO?x~SNT*JRT*nKKMׂ*,q8 Uts$0rlgqFɂ*ׁRǡG?pAG6Ob!R-`kl`N986(U @,Gcf k l~7[u[8hp4Ph:@>7C?z- Xk:}f}#D_>0ƀUW])wfsLۄ^5օ m@j[.Z66FNh AĺG"Qf r=4Xy\jΧw@V{8Uhsm,W@cR *fy;͏~pa¾A=Sd6sNc <[طsc{/7-x6h5ns< |p<͠ s}n=̹6ygBJRT*JDvZ= @+0DUME`+;hp1Ŝ|s/T9`N. 庺V{*š`NS6Gܲ10 :ӵ򠅂Jp (9Zo\+g@rto\kmkl U k۳º9sm]+6:Yd<Ɓ0l ZsqsAgծRpT*JRi-u0#aT*(Ž@`ş@F G@?axqVamH ivV/j{mm+)X/vmítl6/ WN+0Y-7k<#5 \#X@9ꗣ ֛yvؚ`St U, q9;ٸ<kjW|hqY㢖V]*JRT*Ut[Z IJ Oq)N-(iSCrw \H0Y1? )7X ]: Bx393tT6p:_aƢ}߫%X0-pl=n Q Ɲ>cbׂDїQUf e- Lj_@[s B@\N/Y}ЮWw1&s6)d++\銠 #7QT*JRT*(d„ ߇S-p䳂Q PfL@ lB9\ +@N" D9@@@O|Zu_@X匪0 8Q9c\RpνxDG2.Þ܌%,}k3C5].B̹&oZ cǎ 痻mDpm_z饱66 8d„԰6@k3=wm"woʣrKRT*J]z{9-XVYA*Ա8B^e-xgZLĉ#O3cN1VPg.*XTl F` ¸Te|&_a!`+ @;՞c;7fp'0Ni k zbaE {_xV6/<8GmYzfW>f̘/Z]tQ=k?vp/jsU ldr6)qG*.JRT* w+#w[+ʑ[3iҤMФ(SFJ4s= ;"hԩS2'@Zq ? m e6xG;U(ԗ8-\<‰l Xl%bZ6[_8ri<;g؜l"̜93/DG Z3@Ƥ a>}W>cc†3է1k 6mZ3/N/\5-ܺ-JRT*JEVZicf3@ BrAS V 4U8-y u?]!r9s07@ LF.${*9©EbGb)l{l>'T?Ƣ_b1N k] Z#/n]i(ܽ3{` T9Ɓ5;-0Os-ϱ_ =<02|0 Z-L]a1!܅W;ʳ5թndUpT*JRivkh %ȁԬ/iYMsT$̅JUWq A. ` VUyF̹q jR-tX 5F AUUIa&(6cj>`7n Nsjls3Q ̹@{Ø<${&9Y#lV_ca|l6kݝݶ KRT*J]Wn:Ŝ Nq=Vw, /9kX``'āUR , K  9[DȮ~kaƀ[;k p-j.\Zo `9\V V ]k9`X;PGֈC52fk/\[?ɷv[ eqany'xb^{cAEmm-=ׇ?p\BmDa52RT*JvH^\^]πQ8bJ_PT_luu׍yv0g@R5\Jp*טfI8X^y rB?[q)Dž@g|P0g+_RPd TrO-ظzיq\P*1OS1/ g^Wkr6V-ZEuhMmx.y56WkOYm~Z߁TpT*JRi-V \Jn({\Tp',A.+g8^8sh$WPa0Q:-WwqYJA1K/H&^c7 *ϸb ih4v 9^`W7/kxꩧF^.?8@ ˖ ʍ# =Y}ښ[o[_Y0mLʑu]z ?_܆IU\*JRT*nG`] &'Z/q'וsvgʱW@ IDATy{sOjZ..?k\;zK;ꨣb<`i})~7&x s RgH#sI(όsk<+>(j\\˅gsy'O"U>]}l-8v9T'> 9žZ6 8\h}6 ^N*P(μ_./` qu=ll?xrX|UR6gg s[%v1vsz1~s̙_trqż!x>7@u&9B}α6F+bkƥHX:;J%iko.͡7' Y;7>HJRT* Rpnow#N&>w3N!g}n9W0׿!ׇmpߞ*w9̺;klSfrͳYNJRT* JV} >^ p=3z9*s%\E.ϴ|]6Î)(=́)SdsOW rmN-g@=g@3YN/ :( ;U{o5V69\8sնgb,6mZ3ǓN:)ap>c\ŴmLgͅD}ޞr֏+Zb>=+X/.JRT*Jgܹ/4M@$En-Xorir'9 PʫT?e.pepK mnoBkryA<vm ʿ|y`6^ ¦rV 8;2%g0`h@:?a` g5M6ݤYoagY5`[T[Y\p~@-7̵6X[@X9+\Y;X{P^y8gmhoe#$s=;c!t .JRT* wUt;Pکžs+"U@ k[ ya@AmVCi>uݗGC:`3ό+sЧ/ՏB}Ƒ}'46 .-UY豂T 9t1[#ٮfl~ϡ6+7f5HGݚZNa6`)ӧO3,T{7n\<3|<\pT*JRTp[ZfzR q\_ƑfO-R >^RO>Hjì(g`g)[*?hnP`;aيk cR:E05?s򙯾c=6͐j9_\k6·6}o7rl}F;ƨ]g5_cr|;8&9r<@噾69\~^ns+JRT*J n:Bl X0/r˝y >r;GsQ 8]ǵ@ ݷ; vC`}W>AǏb!BAo.Pg td::6UXօ Uiγ1^7^(ll1?cQJ> y28[Bɦ_mσlm9??}:ЪRT*JpW@`.5\P)xi'Lz,X ^xM.agp#^"׀Uޮ{.X kτ6ˣut@*6gPkrr9\UkƣP-V؊e;i"d9ûkkֆlZǍ78\igb9\fW=7W㳞365upT*JR4*nG 0믿~Q'-EU{tsL0(&dZ&s/ sM\X0W+טc+Uȳj vgQ]+܀y o5v>k lk h"sG`m4AiUs vQn[,efΰM}Fa/Gלt߄ 8fCө.-}6 r󽾀r:JRT*J4 0Iˆ,pq€'h L>*U [cV$YCS%e 2}/.,zO1FF Hl\Pg K@\+뮻. A|Gfſ&#U06.Z8#8 zDH|4X稃P{cV+(i߻y/Nr|;n vb2߾z~l9RT*Jarр0SrUGp SO=E丂dŰXril)rʀNH3 O6&-O'^ |rD+qZ -vYgsr 6pˏ|Z >]ig}v(k|.0 dY`G6]'ڵ`4{_{$Z-d惱[_.Ms6W쫂^rmL4)z[KL۽?oRT*Jav:' Ą;9{W8k@O\Nz)ˑCm*g> 3srM .\n?W-gS\b'tsR[_Pn57M9Ԁ 3yA6(VXJA^jm4xB>uxj'D[Wd>yCb-m@?t >9ĀT{r'j'X_`W%es)S"=+Z'!êNo: *g/h d40!i`{ 6uv[0fJX3#_`po|7_ƅq w6(*gVJRT*JmUsiA"8㞂:PL/zq:K|ZᾮTR N=Ԁ6߃# K(>r33 j0][n-x@c f9 ܜ6W. Rs7tS+5 W F9sp˅uԂqT06暀Spƭ_}YAgptkeKZM dnn֦ͅj{.6 gx_׆JRT*Jv ,6 Kq~37[xAUOpe~`y'K ls:{+\ x*$*d\bPUA$wT>@A\^s\ka@ ~GA89jK5l X|)DM68m 7(Mj]nk0xw}:ڵv qp Ce=9YصmPt_\*JRT*nG}:*†#\Oar7 8P vkGS'p Dٳfj(`j Zgn98Bp` %4IQPѠp`sΉ}n= [XWX,a͠Tj}Q@n}l’A+W7q;Fe-U;6%8\6&۟s[FJRT*JR7g&`U8)Lؙ/t 4Y asyT> \X@)X HQ*NW(. j r}y\P ƜaǡնXeaB C6}8(GZ1p/5fts@gOέq _\c `w=Fn3]"Ax_RN+JRT*JTCNw Uio9昀Ƌ.(Li%pؒpZ űw\‰9H:#v^, 0qBH!Yev\\0my @ٽN8ɠ@)M`k 8܍Ѽ`bG}|աrPGq/a@Yko`=4Ƃ8pֲ뭱Mcz?m9VZs7/c V6* /<*TtT*JRiتBQ`Bg"#r&L9`K,t0YI*&̕ \q;BaBYUo`\s$z_. w {;a`>9s}*e,*GOmnz;0 Y4w;m5GxU'LZCsnr֏ v3٘Q ajmbjm{qOHl*lӦMކlXtYT*JRT*WrtDQS[udPZ` af o H1)7|ssqC}E@ɽQ,TH.®* vF>0c.H`'׼G[\W@ +ok:E;P + HB҆j3¼A63s@+"p/ø ? |f&V/.;0@ Zsy침ml(ZڐfεMӔ\*JRT*P*d€ORPfX0 2, YϾJ Q@w]yҗ"SLȅF>@sͭ6 |`J%V ]6WrQE~n-XCx0[$?iZ[/Ʀ@ [/jK@3s6ŝ,H]*JRT*nG}`Gy$*/"p ,l4*"rtEL>Ͼ,J O?|*{9 Ha 4Ȃ5!b/15t9@[ YlZ}/Qn6?sA5jӳ[UT*JRTjS(@V$`QY!(9΍yw}w8B0`G%%q#jt6|`~H[[y¬`8S/g >#' ,ԘJ h1⚂ec7X0\{cndO{j[Z)3FNsA6F0=7}[ RYy,lBYk X&mβoȳ6uQT*JRTZw,(<.9*O<9餓G}4c`.x ` ͞=; `}wT0vU? K6+6 5yc=6X? Q:G>ZA!Lo2XzsqY69ւ{jK0O `lbS@^9xO`F=gYxm. tm~okfn9ZC}. |KRT*JҊPt^n[D!Ǡ [3n IDAT 8ih?ؑ q()KqlXYPA{*֮_ks 3K6VY?JtpV` 6!nh?.-JRT*JU}+ `HGRVT3nӥrzX\@ 4  VAX֧{U%M`pѣG7'pBsQG:~蠃 G|rՙpdi֬Y0^ .]qV=-">~N6 9 Л1o6Y?ڬ- L߳=࠙{f~1NW} رc카`tkhUE!X+^ask,CqK{\_E oɭ9 >w|@pg_-`[_>a \K:cns &68\#7SHye!pM/P ]knncm炿mT*JR4U܎GxPՋ0\Q@ rTCs4 NA sA/`f*p^y g榺; !]LkE2 \ 6G^h_;bxZpNo )Ffjǚ'm `ߘE"jڶ]P@[c8Ɓ !s@ j-uܶ KRT*J]_EHd)T uF.R:yUa^\򸮠܂j 9Bk N/Dɹn4sW7g`,؁j J]v%ں; o/Ps.}@">X]w5@]7 70*4Wef v?u1XMe1cqHm`pu:3̳u|˹V\*JRT*nG=s}iU5a<D&0EPPA^aX+R uvlX.Qa'X§9bΣ~|~m!ZXm접nC E5y@:CCl2B}F0Q!T.Zl=͑{_9_m)e\d| XDZ7nmpzA,r׺X[o&C(\\۞>GXOOz }X*JRT*  4gpFQ[aɀd!u `DrDT: 6dTX]0}&u@89 ^A < Yic=L0i^ v~epWGήq]0sl}(H4VyN2%®[ɉuq0|ӸО K }n~:pyМt{6eqsK`.JRT* w܎Vk+ q<9TA7ϜC峂=3ԵWZ  Z~y&NSNnO6-R+nzsט1c"?xɑ'-$ڼΦHT*JRT* V+G`pۣ:*, d)>9 y.L /0̂0`lq29hkx[ Z"cil\<lbu?忪L7<Pe/ 66.\9Wc pl]NT$ ̓ugZ{dCY{gtM7< M YaƍSX6uq9 WBiJ!q x2L`N4_yBWJR"W'C8H͘1#򂽶n3vl19FՋ40vl Y\ vjG ;Ԏ8 Sf֦v8 xhye:@f!lMQs}pDs*B՚k} qO z=\g3 bMy>#QyF0hX{ל8B9k,닫=pgiZ胡g͛2̹Pd<4i1-i>лh,J3]S@\*hb{oJu9tqxP;.cʡ}q-Nspl`mE!@X6aJgY(gWێ4r#DZmԅ)a. Ї>!ŊFqS g!"l.^WB?1Zm x6qIs9+U XyOq3a(0fw}ccc RTjCu7j9fĎK x8ߦcot|TRā‚4 X~VYXAdg`p^Pf,Vn,D b!>W V{ЧrJ3 53?@Ԯ¶ ^3Z8ٳޮN3G{}|ϥf` #[™mTpmXטm}<˂RTꗀȕ jT':)Jwwn)- MW\TP*T.iłU<0 c! \ZZU q1|` F^\[o5 UqnnR~+`|GPls{ν=P+ryU l|_c6m\؆k'L8F2j{`c 0ևvgM*߅tKRLZ󟾇Fv3}OJR=&O #){p`@=$ 7 @̜I0 z9)kAEh#Ş'XR,/ڂ9 Zkp[ha@\uf! W4lAn617Ųvm(e:sS )EoUXɍ^B-,?0 θ`*Xei,*`ac˙}tKI9]9`V5 rd@[ lO>pk&x4chsu; vk%$ Z78@:Wm~_@ou37m<l템krg!Yȉ6,|Ok[RTZF*#:}_4/iT*-^Aʀ|Y9YE;3p^=*)|fnqx l9LrB9p̍qr%Z8̙3h0b`{m8;8sds=wٷ ˡ5vU|rݫ,*&7];vls饗ί u4/`z?yϼ9rjVhZ-d+oL>k`ئGTst}& x=0g;>BoT*J#m#*'T*-uB o&y./71YM, A_N&^3p `ֽ3h6`L@dNuhYgq E]+hyQD <}|asW9y>[=ccWAE ؀vt~*01r p"[}́}znpUȎzү9ɜ_?m[*JKWCQy6zԥRi1y տJ/b t \;Й"$K(P+x*Ϙ1#>h?.1OrKnjw}fԩfѣGX:nܸ栃 @-VN1QƠ0 湱a[I;̥3j<܍6(a W!Мc=6ZSή62G޳'}*qwmNX? M$ye5RT*-R҈ȗJR43wYJPحU8sTX rX)Hέ{r9"夂S(BɅ6giiLkCY\Ϡ |39.nAd*Ze{'pB\zzzJy歽zzzzuNR9-)F5_^6 Y1T*-W GJV=S.)O,+s/AyA'gk H9~T-Hz@ O2%" &p Z/r8$ȩi v^ Ρզ95.ruy:+WZH嬂T)/.첀eq_~EqmXOm< K+|ZhgT.pmȊuqEòMn66|Gw5\k=O+Z0cΞ5);]`kϺU\\Z.*^>~4MB:m^T*`s+Ty?xr Q#z74; 6\b F9 _?ʅ @2-X'pof r죏>:ЂKc@@<\%˹W\L]68Z,TZeEkXkJsok˜5~9䐀s,lZ6̀3'Ys'xWlƦ2nqmn`i6_{vpG>%m*JL#[,3iw;CWT*VE̙]Bf`35NBX@X1w)V4VS 4 g#h<̀Ij& 7 fBt4+ l|a ,&hmévkr^ƒ]'>G 9n 9˜[iUKG٦u6>{ ']}xh`n@_[Z8`YnYɓ's~\\C 4xy]RTZt^꺂[<7M.JRi@ei;n5Tev-S+Ǘk8 2W\b ; n\~oh#84nk}]Ьm\P04,syfp p>/Bsb7?Sʹi"Xhs1i0 b 떏 I$S h|6{QGźeQ*anɠS+?˙̙3c}㾾2fkv1xs=m?k,}zX_@ . {{4l9})3gx~M:ry'ZZJ+ yHsNǓXa 1q+z]~>4b.#QgxPϵT]+?9WNi*F[#~d۶g7Եnz({3[_|MdsZm/N#X'?rsroX_ 6sFKpKb1P@)s,hOX'Pv,`VOiڒwXЬiLsc6M8.Îd[Ŝ8x #*ڸ\ @ӜTނU\7W^-' 9ƐׁW` Ayʻi\~k!lXzv!`m{_h2,9.ڳvY} ن\slC{=CXUUkAX/w+â|zVi(l0 kt`Py1,/`yX Vm? 1"]j Fg3m435VbE-Xl^CvPR ĀFp +ÜWqB1s=Q +rRsOq$Xs!,[ @R,\c W{.Hռ*Z Yh$?eQpJ,y OkckqV L1r|hιYOZ/ӟ1_=^}Z?>ڕs_3f̈gUݞ(yUH)y?PtC>XAl\KnA蜌k߭[`36;nSB!ut8~ՁidN2jdYC]Z("dwc6@?B;̓묳Nc|-ۋ/x~X0U.>r 2 V R䶂:n-=p +Ui:c?- + H}+$>\aN2Jvc2Xw4suvV[mVGO]BBrTzn;sIAX\N8qqE@ tWcBH8c;yZ&1p]fS `ކ7|rvAa+v-pk`-6uYhE@:hlҁk\as ["T.[kn悍 @yc$d{kka,-k1N@3(RsE(+F 0ݡ9+>Rez*ȃ┛zm8lmV(m|A>hr{ל~/_BT3ܾZ )`JrTUx=]y (rM YHrX}ƽB|$wT~0'̉U5Zo9@+<[ 'ka>j̠X+վlg ~p ͵f 8 +měpo0mF9Xt;I2/ eV{6(圛gSl~6!k}Y/{- n3yQZc )8!}!v6`rjFvUAy]KC~S[X/JgƵGWEӾ߆[Vq938VvBl sJZ'aMy:9Ir.9 `5>I.1u|P֨ oL( Y(U,j$%+tV.]`KwuW/2IK킀| K tgff T`!7\C.+yVKFQ;|RJ( +ho` 6ڥxkOߎ2 )8m\]*v77l\ZPX H_L %\PyM;U =ހi #g1"üцZP8.'=徴,6ptZsM b=펦~.5m5ۜլhQg\۶LYF ƥg `+wϞ={-I[B c;*ǮK.MyQZj 3)WB VV ;@.RZmwj^a nO1PSj  ɮ]~,Yݞ 6^̭gb,]ny|o5Zm"iʆsͫS!~i|qn3lh=oLS)2sߋ!|mCo>?+}iQVBk,'`P%/ س>[J5TՙZ+{-X>VT+rPk,`L!'b4Oʧ O618 6  qvNQŚZʨT]j,t{ @)`BflU{aMl_Aׄh>gTtˢRmx<6}P*dZn}Խ^?b>@!V+[ǼxJ D_^ xZ%UnhRK5bR< (vA-uYJŵ-T;]@'RBٌbUӮ|XJ75Q/ FUbja}BrxbeLKZB@^ *4?x)ryC cG T__+}^f/ k!{y~Tbc49g[UwچZ_ sOsc/Vz߰izUQaMh\-*f5;Zζ kl~~bVѫun}p { U=%o nF(DUt伂Qƀ*QI+F^T{ª3_+ɨs= DL}Sm@Ow K֏~ҧRڵJ j*S ؼQgISCō=j~Sgyfb<2(`G}F0rc3/G)|{n*NS ۶`}6ܛJ𺷣ۋx/~)4iKuԮx{skN˗^ƭ-2BRS7NFoicNHm|Y. ?l}+ D0m%DEF +,Zn>O"Oh߾}` Pms:`HI qVJi{wZQY.o,.r -8p^YޫlOb 2ƭo,0/Rc{%RE{[>F!|^ǁ4'ߴm)lq,x]jE3OZx:zm]mިBsPʂ^ro_Om1wֽ--tQǗhU1p&DV3CmXv<~Ĉ^رivFŸV_kۘ!kYC`a3q4V3'my7ͿSL)@ ^ Q6SKm5$Q))a!H4;!rr; Ap^ؾG_ `?({\hj &[ Fˊ}Chs±#Y/µ;T.ƨ/Tc׃{*~9啂J PRSoU߁43o sym(&W@؁-R7!R-BjF7޽{ sarA2*d;[mաҚ?_i}NP|rlX-6|mx-o#9ITMj0f0S^h/]}P~ 1۫ZK> -8{j)|VB=6Uئ rw,jX(Áy hA_.1l0s߄ߴ!XrZp7vl g[8w@Ɩ9f>cBax տֿ~^?=l55ެq,#Fc5Z#(?m,,z0g` R-@ J/%[ڀ!Ҷa0[`lzK%0`N" n+| BZ7 ƣ-@^PD27UJPacю_= -67>I- >ҷ{A?_(|Y @u*P~,TXTko,=^ =WK- ̋푌g#7mnS>VA'!Dms3O6FepX7v gMxsI>ƛ*ư\ըu;j+|wJ`:j”Z>@w g /8_*Y,RVV,4 @QX4fP ب /ЮO[? r2r}rpC9vpi`?r 1i5w+E5B]5X|F؜K2aRمD{>rPls!9OdSu-^> R`ZYda'x ۜ*e э·ڗiToV;6m*ŐXϤvBYB*P[Po)+tj-ceQ@%pBxҶ+R dg _JfJ*U( #(Խ=4:{`^q;ﺨ JGg]PiACT+V?rG ӵ`Ӧ`s/=4:t(OQWZX6Hh-XPЎE,!v(r޽<7EZM@{u /8qbӐ_; '`w^mk^O6ZLF,l[KjVjp܋%o p ,VZ.6U'ն3޺BjG>}_VRD҆mˁȴXzƝn#XoO#tYA*o ^/f{# \g  @^m oYIU"y ťLR2%EWR,6/:= }C4c!Hu]8:ƭ%kp `mQTރiJ‹|s}O} ՝rA`_x5g ȆBbAq3 tr͇ pO7 cLދ-6@1Y8t7Նfx񠐹, z s^Ͳ@dشpx潆 n[p K|D^6;b]jrϘ058j/5IȂaU~,~lPG#5C򫈕m|8Q@.䒒 *VH~RmD)~G RB)B|"h gaKyrXA|Y`/wwߩX!@&[Ũ6\C&N7 Y?wy3,Q0 $l ' -lAB΃^Tg?3e( 2=ْ ܃ ʔ)ys3Ȏ |M[63Ee}տcmss$g[/_NxN;RЯTlP0փXki~`v k ]@tFh]BA(EB ar^J[ lgD=sK/|D=Wn1pTŘRzpph`}ERUEG '5B)MVZ \39vjjų@ ~?6&Ǩk.A^>Wvc~cղZ0&F!2cQk_Z~8+%czK;3f}F,!ĔhJ''.TJ|(*Z ͛8>L5N&\`cuI`!=-B ֗ 1˜G ?p@37|׮]E6. QZ۞yO[V(BQ/!Iv||Jum[j Sfk0p x  ++(a &St~*S D[-F (@bmw]MJuf¤51vjCLF^ @],p BSH?=P5'V&z] Wf _4Da+ʽEcsg^\UkG_}GK=OmO_<_*D ?q!*Si+v/Tk n̞+ٔbl٫Z}ws j)@ u^$f֠6^Z.Ti RL_X W\̙k.zԈ饍|m;Q{n'ڦEtKA޿cU p@dt`PH@J )`XF1׎ j-EY^/ĺĪL1 @6 1@^ Wphj.UXOP(y>QjϠ9F= ~Rq<D(xo}DnqzK*WS^H{c?bs?w‚1Yy7'-$X<%v/ӣ\_ ~j3S?U `s W} qߖUx $wk5{^}^Ow,T\941w(/zi=ښL+=x0SUK£Œ@@L!Q T1![%a@\yjO34*@IHB{I<` q]TS+t&P'VjB%Saɮb^.c^k7VIո̳cbYx"|3;H@v)pmca5cm(%,=r]Z#ȑ{r_,AFZ۪¤۠!v+f?]C՝m׺XRlcU.' JfVzS{/mStXGo;F %,[ShmUxsQ+ g, %K]-z@š{5Xi}?PY{EAko;%p*|}_WB|+(zE7T;F : LA Ҩ/W20R\IEP޽ rJQdq0hʳ3 ֬/ T깸OP>!Z8՞c ӞeQHܩmk, _!-9ӧiboa_8n'`qmoQNe<$V!xRAI?[CXغB[+jaa`h:^:i÷ouXVrvl !im"0:~}qR@u-϶BqL:0Fj:͂fU*Sp=pAݠ?r~ǜ7ƶK7Y֮6Ey@ۂga.!ƠM[o'ߚ>{sg(~B;L޻s䟐lyS\˹~Pkpyv~Z^כ/nˁmyQ{ƑY?ŭx\EɞmSV-8l@4.;~ݪerdUwQ;U(kTmE(zRWA4BS0DQ;b{ 4*dE "kK*A}AE +P j')X[`mphiP:0W7uĹ V3EuX44g &n۶m+ۼLO`s=uy]rh*E!և"Y xb@[@˿,wZ"6V ᛯWb:el5c~r1r6 kXmxv b_G*|04ɵlgc>6]Rn K#;lHӂ5%H.s?aHA'(ݾ}{截rT r L_o^PI= DIaeB;JV{|-tV!`6'P*ZwH || '_~[׮~̝}q/b!\Zp0~Λ]9#333E-WY15„3gqET-N{=lC|Uǭp<X[H[֖ͦK{2*oQcBX)Rj8ǘ9ȓg5^j.~Ns U@0@TG DQBZFeǡQ$唂-j(dQ!u<4 x`hE(/wG> J(im;V-^UZK/p"AlZ1SRA9 Ui6&` 8 4mɑO=Ti@OOPfZwE ?5mS͝c3ϔy|,dX0ijwntcS6LIiJ(dh1oOƤl-Pj;WkWp̗[Ć $0i`I$ O(L*TS/Q99 _mwWpF>CE/ܲeKSe-@R@ε 8P_ Ek>(Qt ?}G ^C!}QyWc̱enMsk7( T!HťZTs'\Oφ+XB͡v^jCsXe|F+EeN[6 5 ItfiX/?GPQںcO R|f/I*UM5E_*o ^&u!`̗*e6-^;nXb0[*P /,`#7q1ж8}Zy Yv)̩< .`sMKLuUb@7*dlODl~jS` 2OyPJrTn* V/y`-0m>9Q ܫrzmڻUY8 Ak zu]?>_B 'G̨<+6(<{fE mF14snA/9goL!x=۱!|)@KK$*9Gߦ 0Ti)Bpј uky3i) &wm~Kw867"諩kKT'V`GĂ$"V~lI H(OeXXN+ RLJ)pH±Ցb@l+k@ h[)bE9jŒ ,x#_As1;X̃vwPl2Wuwh. SS\HK)K R_F+ b?\U+vvRs4pTP8-̙:J!R't "5Q)`P{V#`yXZ*P't=S%Gް&S-[PLawyն+WW|ݖGh}{f ,\\|Ewkc+R\|^z{キ|6T6Ǯň??-mqȡ}}RcvKœv6>KZjaWOH{̞6Bd9[lL~nhP;|IS9!KmSc2Xǿi^ۨ6ȩ+[M0`G ^Ga%`C@r(vR0`RI$J%L AM*B W pZP ,oU+[`ׂPoX@x:Wj{"`1V)BlV5@@jH;oՖ~Au6| P3O|q+ȷ2PP)> 8 IDAT`P|-`PU~WPiJoܛcaTvσ6 ڵ^i\pɅ6(Xukn_$75?E6;cL2L=wR"'-Sۢi 3YC^ 8owE S9m)u> kKmj/2U[?Os-W p"= U kv \> UE2 @&`w[zWx ʮڢ"_x3,[|c+0B}(D :W :GKU4~ V@u º?5`SH25L*Ռ ImvEPfnזY8d5B@q }K/g{LP>^=++_F |ĉ ņ'N4!(;ֶ϶Ske.}uuiBu|홎1J4@wNHNB~}ſvPi_uȫ0,{C;<ǿ~wDα\{jLߪI-mm]Faw|&oXbO 7oKG,Q)``)~ 5UoE IP)ď`}:*<%t|6 kMhRj 3vU7@r`]2dNsH2Y@g4F~۵ḺU~瀾>ͩkc"ҟk=!WB^`GhmDN.Я (Jmj34,X[^{=E꼾-(r-%Z%o,! z7 u~&}iղ;a6-}'ƕizT'qwkZƨ`X?*DH *8 (rDK9 U/VZ-b_atT9p8`/-KrvQkk !d|m_ kKվasܽlj-ֆBTX. P-Lta͞zOvTx~' }>>ñv? U;|~~+r?@$=@(֟q~ ʴk(K "y5ǔ`urmOb.dcs(D[1*Ů<X`>Asm1 ~w}o$ߍOAҦjՐȽ'VݥQ{QIX}gw@)k)}M 8zQav? EP X}, $Vt?x z̽Bl)@XFh Jx5& &F UW7_7xrd{ 0qo?~2^-V8ZMa2{/[U Ҫ}9rt ߹sg99Z0T@ 0Pެ̓s~zA7W,-VgC5pU)ٶ%Zֵ@[V{)ؑ KO!/R"耏T߯~%L6`)R%)_E@@1`S\AcG)+bj ){oD@Q, V=*Vzyrx#(k/c(؞܁Ds#_,~h! /g-"+> 5./_uUXL}Ryͭ<_\X5o86oq |S-LhGq3bel*qgV+޿j+-*c-TNA R4k}K_*`/ru6U3•U{ uWh1Ӗ^ ]g ]N】ϠGhTd/PQA0k@tM xD{ p͟^‘=㴅`j4[D_Rn ,x>쓀o۷(ͷ~{kǎ`[Fy&Z28------mlZ?mZ50&jT?\_z#?\g*5\$8@((Q؍h&B Y*I;G_MaBsOV<4~#)J+Y1wAu<<wR|> vA:X S J5F`q8saw\q(^ lE,e:N 4@h`,8O9U].-,XDo1Tfs}J-(\i-/dk[9h;fsNS7JKKKKKKKKKŶC[T7v.z96k \{L) > "k+ QF` %!@L)g#a=P WG,<F'*Y;QRA*8l dR??Xk a,DJaTX"?0k pZ@R4֮1vlhsEAuK #@w*[(i`WqkGvvTc֧*^F>[DZqF!'43 6Op )Tks$4 A6OEsM\x@ ^XX(aœeS̓q5^2pu OA- Xy@?y 7(Fm13(+j OKKKKKKKKK sɶS`8pVO?t"J[L$E`EʩB ]_|q%0't۶m"\˷ I=-P j 5@<˓]~o%`&HLvm_*ڤZ֮)8RZymK x(`:B5G+ OlPo^]#̚n9Ʃ@YlYe.%߸,, y2??.mRy[Pc0orUPЯe,C֩mm_@ΦVڄڊ fb_ x㍭BpԿoV(ñc) < T)D< v^(VpBH(`_J( :*~TQf|e*,5( ^xA'`Oʽ?Z,p?LmBAv-ƒǀy3g˜A8`֘({>*5.4|{n-OOPMhaD SJޯ%֩m#EV뿟r4mhՕW^Y5ڵkW e<Q,R-A T\[Ta@ׂ$@cŰ`rg`hX J,t: `6EN)V=A }WTO: 4 1'؎s|{"<0qOʱ'2A$57擺 05V3^}R`)TXs'$9|`*pi`+ϊXne,t8T+W Ll)~ Z2_}˸빻VS\----------m6,|5vJ,Z B n<-ݻwo{\'Wu0y Hr? Րb(4՞?O Ȕ]bC(5M`/*MBsR2 恤TՌ| V)Ѡ=dRgA-hʽ5 /*05.Z #gX&yO3؂7-Q6 ̞5]i*cq@x3_,X~jk286 _Qla\?UÕ w H7tS,DdoZZZZZZZZڈmy MoBTWlDW[ ѵ+5P^c^y B)Tm߾8gbDA!`F-oVdR5[ ,x jT/8Py9EcM0l-)>g~ Pў0_`c+njؘ@pka.\D%jO6Kn{R{OBykǎ'5kyU`Qm}s " etZZZZZZ:G06%68j5Q) !(>z 9֎Pa `)ZG +X_@ZK-a?sXnTQPw /Bo駾n>!pQ :&ڵ4;.6΁ajk@''(Q9w>|pQ)\^{enu^;rνc .( mǶT=͓,py}{τ~e 6F*6"g&І-T~N n7J P0U0w)B׾ A8ҠW I~@Z „ j7*.ԆZꤽ:TeתP@ 0) D9 {^Q]CI{XA{l(T(JEu(j~R]ـX7ZbЪf,ق>J Fz}KsHǀVReO7Zɑg<ZYp3үEJf;_~>я6"gr=p s} _(KtUh.HJ+EmK.w - r zAy|<͉@+:7q*? A*67RUT6F0 R*ߘ2w-<{93ce 7&P4&`kKeNKսOyX<{`y?pz#[ko6m:scMKKKKKKKK۸eت 0lH P*R 0'0KJ(LiJLڗ7 мdRA b;#)$ K TA6(E-U: 2W[ha?Tmݧ}9`,{-0Ȧa* @ h♘{nG2wΛn36P=/ϱnZZZZZZZZZZڸmU 7H-> p^E9''r]7 JѾk kE/>pL8:MQ@1u`Iu|i/X%%4ٵ@*M귶U y" [(Gڦw`Mi :n< T| šꮺ7,DJiF z tZZZZZZZZFܽ` 8RA (s)FbrU <Vyڒ 蜧 <:9Brp;J/|:W-]vص0ZΫBS| lڡ:BhuTި @2 i_q)hP 5T.{Q/2ǯ1R l [3?AyfpT =B='c\>OzlmDiAϼ݂@O+ [[pZZZZZZZZF:b4`y9rBAP0@0%5}ub:)`Q2P}(pc) 8 R Uob^_*;СRL)ThDŽ5p2'x/5Z.-FK|f9QL\W°@^u]Wjgh E6ɦRsg^P6)+8SsͿ缱g}{-hB-׭]ЬIS yFv}kN o$3ʟp[!R %d{rQ.pǵYA6 .U e- b`GT9}R* cOa>!ԀԁlHKAv]tN1ERb C)v*A1k+d۸x(?[hH`>̥fAA幚?Pl B>Mɾ[Zn³-:Tvqۚ ^RL5bš)OIFK5PBc) 8j+_JiGnBnU : c2OR?TT3P۽{wPT)@OEk G0A0h*R^ 'OzpH9<]@mפ- Rjc TY3 ˘o(@9PtF5B-.xQwG1;̡bUAO /ujon4,gpmF(6Zm9iiiiiiiiiiiibk RCR(?tGBw)`H*E *H{gJ#c~Tx@яy8K. 0c1.`yY+----------m\S4S.](e^(@S*0弐Y F81`t*`iX]PTjGW1F@.DJ ү6TC2xlg39rlO*>&d:q  m6戊K|‚1_c# i>m@ Sw}wTf o~9<`)?/{nb[?)|F ܧ_Sa7AS+C6et: (rpE*@Jx/jhUb$ qw^E󁺨maKmvϞ=EM/ {Oq ^ 8pa*;5N) h|w Z\15>p=s(A3ؽ˵`LKKJ6esDW!ڱm93ڳ =Tl!e#?AOg}6I|Ɂ6/FV : aXgqqYU8l] ɀ=(@: ´9;1k $Ȥ@QШϝ;w"WC[peI0M=DR. *[|t Pr_%r}G T^~،W!z O*ԀQ) yRm}*Jq*0`,ZTXJ,ܚn26!Q+9nUvXO>gk8˹~Ҧ̹Ms٘KYoZZZZZZZZZZ8k=p( [vւ=(R8) )¤-> 6*,`lT``"< 4͒? nwUB| Pc\a~<,WW!-&kbA'?R͇>eWm,T p R.]!ߔ_ 0Ԏ\h@OWJ@< cO7Y.𫯾Z*=*y+?EUX~90>k6b+[uD %#9-----------mR"XށFb#P!<*%h_*"x(S?AUly/ Uu@TS?K >ޭ[XRMȝ]y^O p)@S>-=cLvM+xMX~* ?B PyʯWŽ=_ս #]G B5֯mqCQ-n*~㤰 6;h6@4[Р>[67~|&g>W~ `VZZZZZZZZZZd]) ̵ (mBAtX,& @0Z~-DUy`^gKXp]ΨrR対l?$dX@J= +_|SRo: ) A"%{* PB{Sϱ147Sx3?&GksZ0j}Siq\ os@0l ^ziYBѩµAk`o R!Zg$sW]uU#a"s}&k턚PCPg+PBo|z] (`Obha^m.Ea*{PLU) ;NQ$ƭgl-%X> m3|{Urco`KAH )rp-/UA^+ Ġm.)}/[׷kJ+m۶̻}~.?yQ8-P:[<Жh*?:}/:P#23NKKKKKKKKKKKJTAoQ12T[ .Xz,li06A"t\5bm>7ʢ0kŬ@0X)QtDJ$hnR+|Sr;aTUfzGevO~•Js/m Zq,-^cǎrmCŠ@UThF^vz W*.S \ٳmRx-:j1Ŝ ͇dOb{# Hl %-----------mmP_PbZ@S( ҶA@5-t+%z ˉpAf T{ R2a®w0fFMB LDBe ?Q>9>ۖJxA]*ZTBm<`zpmQ[6u{VHJ>]oQ ol悒{F9S)^}w xs6j#JXnqS5dPL`*Oر{©{uC FS] P `x1Ik€+Bx]e~SK,|(˭ r*SWSc)Q:Kȸ{\ -Q$Oy n QtA%6Vs ʍ~Zڻ, hL]EVMKKKKKKKKKKKK[wn [>` nTDJ (`KEUք] 0@zPjS*(D۷*2Ֆ x68ma6F|)"NI83 6<9&ȣPk $߼x ,\y啥J|/ƨ0>i׶)--------m[nTz !"?NB'R&\`0hRcj}a˜)t C w\d@Ч= C[1[P/T`P8Ax' #SQ`[(DG? -0S~/)-PJ.'N* udws J -F_2mB[M3sH/j/e+VG|ZZZZZZZZZZZ$X 009^~ .ڵ(G 8 EZ ATJ#`Z >ũ,XMe푽qV*ЪE`׎4J‰)WYP`,eo``j#* J&r|ٛnjTaTEe y6Nc3/rnfBXH~Wm>*ʶ[QAypy27BUsWpOvN6Z 32{:ƞmtK  nQJN+!AYKQV&&5NLqRR& t )rx"U^`=|R9/sL/| @^z*[TPy]-V[tRK$ԼX,m"B+YicNW0V3zG‚pwqG<>4}m47E7'L?9ւVpZZZZZZZZZZp% 6`IEM'KQƒ/'*8Br0-LZUh/!B ֮kFl(S2r=咯 *-XQ2 9{:6(Ȯ@Px1z6m۶_#5Y81v|pHu ŜBէ>vmOj,\F%>̔d 4&k<sNd0?(«}͙g  4O#"Dݳ3ȍ\MݦSA>7s"@Xy/6 ; ``B01pFuU 4 *pQ`U[gS1`I (`(_Q}A*-j <0IpŒ}W8GWRQ&̔kTS?k_()jţJlkBSd$Kl]6g[Prc,s7;vnmB8bdB=_ ?5=⭭ڴI!0n}` H(%t)(}_n]yŕeb1o_@ }x*L*L%pd4t͡C}R8h5p6Ť(`9' (0L U_n&y`V 1ٽK,0q(4|> !TqpV:gPe>ƣmA{(Ȟ{,ZXhR/2!iiiiiiiii2-J򄲂n hQeCid`85V&8??_*4 Uvk )` wݥu4 ȑ#% /֎q̀!3NyڧR>(XTuV 4K \^}Sρq ny"j|/h".32TZp.\:nB3`7WeDS{Ϳ"E >/7* NNKKKKKKKK\N)l8Ns\̶BQ-m(ITMj*J|W ΢ j`?AUX?P(51ꯜW&D8Ti) mDܠ/OA-י}q0:=`bl[$ AbqX'9T d{_Δc -8 {Y@! f/ȃ={1L=&*;!S?on*Bk6%ױ+K0{_j%Ej2X#{g_`ZPF"S+JBkb#sbV΃Gj5`Ñ,X`SbA"VYe޻;ZS(°) Zs4؄mc lL5` aSm)8=S*w'a~JQ+1֎ji*Ef2uȊ> c{}Lt ~/)[~_5> X,bX,ݟm1Ke{O(>O>,R c .]ڞWk/k}1zv&AVj5?:7Wր8 &]Ȗ IDAT8lKF5k4OLGteOUZA* /vm… [0lo3P0+%r Udɒr]˝Ew )T^rY[֭k},ߘzI)Ҧ{e@ġWX,bX,A)8 +}uk/IɔzoiԼ&ӁL` g_J+0=s۹ zJU$ cʦt*&XGmJc9G!Vm큥曭´=W|nRڡF?%dPc@ʂٿZ|K ei˸(|,(˞sn2^q{oC,_|`x]U}Zu`gcݖBie[0fYүcX,bXaC/^IMu&:RN QO"HwJ$ ?ؠ3@h )XΊT8j Rc~ Kֻ}R=esg2mJ\K{Rf/OҺ) X!-j-0>́JcgW3>۪^%Swπ3lE(*MkS17oX[wq11~["XX,b ]q0@NJ2 \{[]eR˫btcWͭU.jL{vAAGzVSMTap*b `)|قlz$UXA(J'ؔUcXA` {u6H Sip/TJ'=r]A*) UDbw*U iժU=o2`y"0mWz Z>6L6ם-@oYDj桻-l+%| bX,nmt֬YMJ*蠾ٲTZ){JtK.4 ~sjZu ]IA! O_o|Z`W?@ԽTLp=*Jm;4s 6뮻]|UIZ,@4DʱqR~ÞbhB8Y qT+Rnܹ-}[|ķs9%Ɣabl~dmj6F]PO[۞HlCUy[TNw)StX,bpolhŊp,PJ.]K*D2Q<Oj @!.(jK tgTB-Yp;I)'EX9W:1ڢƂJAVM@((N%]=R+T\ KςrcL[+N8E1kO- xØkb?>O…X3ys??wq跲Gl~G4~7]̍gp,bX-v(`DUa)Ҙ NS5U *HQ_ 99Z 0%" J_M)_T)׌" vvU6fb@p9H*yo`) f#+]Z]`m(:芋RR=k/-W5Z ތi ؤDs%v?яg) H^Z5}oR(iW-FJ8,bJ붟͙kVj|meԹ5EUvosd믿5%>bX,zeC_}USۼjiO)@!ʯ`x+)`୎;L*1Sg Sa) tuJ4fN>dS=S瞦&/ 0L9sd.eT* OPJ_fMmE)[^]@3Vj7[0Vž@j;">A#%X@>}O6_Y;4g7N)Ż s6b ͕k`\ai;]bES<;7\cbX,k;w_<hdd,EF1)P2;+RbI'ԙ3gN2FYTf{d/UW*1s=` = Q_zE= p9&c n`B ܨh V9(?hbfuN.,<;o޼Z d{|6 YJP؊?S)b9Sؘ,@_:9yk SiݝIu~l<{JiU5b[DZj:/%߷~Dmt:#bX, ,^xb̙#zX=MR#)J*0@'F0H9p\W]uJ`HN9Jշk=U|X@𭽧PP 4Xj3`J1Fs%jRXťQ2v ,AJV u͚5kUx\{1Z0?/7)K֯1R Hl!/_wu^|8%fͯb_3ӧE g:E9>|7 >q_v._JV NS ҀTXCy +EYLŽk׮mςb@h1 Z[c A:35^W@ [ 0^I{1ZԠ&kBLݸqcS6WZ,4^*80J?jgK- PE,Hի:,{،3X,bpol`ʕCCC#G>H pTRpApȒ R ER B/ N Ԥ1q e-*!]a,j&P=eofOq7,]٘opl)/'B%]f.-lR~A~x̢Et`q* ةVq F{~]p{5Cշ>E Aji6(h]#՞Tx~pYǓO>wLcX,bXZ76pYgM|w#HIsfJI~ L3 ^%T#8M} gP Qp !)EjR([o@ n +e@~}O40} !s(ˢ,S,\gyUJmM,[,$E܂Tiv튿1ZP":e3-K.m)5b j]1S});o*zKZ̗uֵ8X0A{ƍkTbX, tol`…H8^\ > h+UP%gl(HR6 pbTFMFg*:\\I U h bU +D`[hmP@GeT` (rK8i5 *&xE}rjr*XuQoX,bXZС:zi kXg4P~(=$ AfWX{\)C!0IEdJjU>Y)wq x-NU*Z~@(S-k qP FO` p[ 0}U/m.e ݫ'|) {x"RX~}ST&_Uړ jk48y晦mWl0.`LRP5>ҙ 4ŊMBb~>|q xWKݵ>Vj-h,-6x"GyK_՟ڔmoڴi[o bX,ٳg8j 8%'Q"8D@LpLIV RAȥ0Z `MCP@RZTVQHpOG_Ŗ THVM3*6r <}QHԉ%hnI wDq.€d39 }K1vJB.| \W 3/Rg*>R͛ }:O=nL{>,In>Y`3^J۷~{JmpM }/v`fpmb̙{l8bX,ַ _|0JRr 聭|oKZ/tQ6XmKGk  ̀'"DjAvU@5JPZ0~ڂR[@)` Цr K/2 M %8u|L{Tg'EJI? 1Cj)P_~yȯsv>mςahŎ =0onbs nl\R>gi?~l\6HMw 7DlgbX,#X,bpolhppptw]; 6"ur6"XD{t|zh7>0YjA'kNW^dpXGF=4Z{S [7L`@X|?8bY~ (^(tZࢦTv?bkK5Ը+ \uEEXH,A?`gB3ֿiKEbc>)aIng>gH#I*SEڧlx̓X(;`-}ϙS?GbX,{cC˖-%@> 0*5R-ځ! kT &}|R j@>^`ʻ}N " oТR8 ՙg~h:rgk36+)Uī|jf?prR\;` g`w¬gLdSbآjH A" 7nlmR]7nϛ)'sj/x{,9ɞhmQqg743+{:-T 0rOLLϛ7/m3 FIDATbX,[ ƆN9߾)JJ%`JpTS96G14: ?Y[Te"X <ㄤ\Ljf͚{> 45Ua_7U4ZE(*3/: dۂ%KZ#~(b}@!bh`Ŋ L2յmxGSeբ0~L<@J0A )b7q3L%RJyb[i5bUU[L +LYeő>~cX,b}k?qa*J$3^Š>T^g|S[(@ վS&q)Q> T@M MIu[0&EX*\ : M;j^|Ŗ>MM1|ko+vn0@T`L JJ`PAqϝ;]3}{K=g|)ȥMKPkn1Xg-[lNFJַwqL|XS`>b,}_w9'X,bXZ uYpFDtҠ ( ^{Rʰέu5 S SY@,]o y7UM`ٵIpGʡg@T+ +`gcOU Jw 6!?7./Vdbb 3RU,u8.W HJznoR+~|Vj` q~XܠBا4S](̀.VbRr]iuޯq -Ҟ巌<ڂXx֢XRy䑱+WcX,b}k<0;PyrA2$ TY } d#P[-u +-*.P{J,u5 HK^ށ6 KjROfjRp(T&@.KA4cgˌƶۿ?7# Z±b PY5.#7SO= k闏2*H =3VJ]. Зn.Ś.;ڭ:҈r pR̓/3ڦ_qg[`߀/br駟-_<bX,[ VX1~)R_)3[l.8%Cp 1 vhC[zF{I1^KOTCq = =G^~p˽@}\>j" j1 @r쉥xopg|~K { V[d L X3}lT*2UUup Ű__JK[>x1*9؋[12zEZyYzJ`,V>[~kLJw}wW^ bX,ֶo-z#R@}[3 :A ٰaC@܀dbzmY<+ԇ"J*|͛=&>K@ʿE63*UHwm˽שJt^J{6 ewm_Bq^6n̋bҞ{5״C̵yYZn;;Z!/@W1:_Ž,l;駟bX,Mضn6v[3AJyj"Pa5REU[̔c;@Y lJOw*i}R)u~H +TL_ˇRQ\uñg{L+)nWw/@jSa{2b πH_~_wh8bX,ַm .\gӂO)R .KYp`Ri?(Ҟ`]EYm7z.-W5誂_U v HVzrWG71NȭYtv P-A5;g\E, PݭX`OU6өؠԫmUG}) ^\Y^h l<ҺsՠScX,bXl[m_~mk`7LP۽?iKa.œ/ߗY}v<կRçKL.lɶd5]O!/vnAumqj]{9iJ. PMMwoU XfxÆ c>lX,bXZZfu:ESx:xzT@}l(OT$S=niqZ)ҕ-5}Q_|W^pLa2 `^Ix믿k=;bX,Ztc\}IENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/_static/logo-owl-full.png0000644000175100001640000007146300000000000023517 0ustar00runnerdocker00000000000000PNG  IHDRW IDATx \uy3XѶzVZ ڀ$r)((R1bR0RM@@xm-[(*Xy?-f9e_khh?O~R/RG?;mݶjӦM1gΜjhhijlW܃{キ<1[ou[fJRj`Ўsέ/x-p__vSܘ ,߫zzou] \w}yy=oDT*JflVՓg?YveTh,U@O~ת|;#G8W#IRT*5#/z]wUr-%,hѢjÆ խ:.p Fl?q)_}~rf&JR-L[qF<8(Z-[>WBT+R_3mIOrOYgUs(rYRT*IX.Rg'Uys>)BTzԣRLtK8r~VQƍo}[%$;g9>RT*J,͉~վ[mէ>+_J=o~dM%d#__/| ooU;s՜m攐`VT*Jmy <9X ,(9?`@_^+{G\1oy-\0wh__cBPJ*JR-Os/r| QoFX 77G z#MrNG@JG{ЭW{sj/pW_׋+ճ|fz8PJRԖ% V-qS>T_K 0EAnK_RIjPwݮζB`0ۯ'ކU5ϡ}\ړg: s=7'RJH0*JRK;9J@؊(WZ a~9eu@[KnQ\ A\rIO.9KA9׵9qZ| ` u݁j @j…e@O3 +#ixoWXT% /| ߮a}RT*5=tF=h\}ctas1#ϕyI}s8u^~Zvmwdh/E7_Xu# [?] q!Q??nܿf͚o|pxY~};`NJ@a@N`U*JRz$Sp7U_|qO~k;㫛oZxq {* 5\S^@;TЇ1ڵ^[;l`_^}/c<#ۿy{K'< P qvi᫣?m=yӵ;VO~򓫿+ ?s_KRT*5xjmڴ m' Co*JR՜^3y[)!:s8G/ x0y[%Ѿ6l @ĥJ =9R3Ar\\55'smdV)/Z9;x>p>8q8_gBJ0JR`jN;me՚Z*d5 m]@s$ qs$.ˤv@ gHy\`E,YSYΏ!@tVԝ?>'y™ K{0wuגK-иj2~ՁqJm ?09V$/gEH{=jT*J|ICmbŊ6mE6}.%&H*${]^ss` n<.򲗽8D68@M[Ņx9]@NBRӦ1h"`r@M2s8Zѽ&|9`KX9Vޛz[qc WT*h \~j> (iwIL Ǣ(p XB~Gy{ quE*p%rY@G{ %%?g 8'*+c?\92~m50O|!Ag+?XZn2ݫz+!1[re8 fB{*JZ>'ܞj*^i^ /p8G/`JW&.-I>K_Z,+[qS9@'p|.gpv<1nmuQ ߟ韖*DAӆ'saܩ_;rq@JRTjUrιDΓ8L5p!r@ (*sp\-suҸ8zq~kΕ?%lzr8N|&nk9[ 9!| "w% =0ꪒO47f(, ε4Kz[`MU{0vQST*5NjW2U W, dΌR Hc<+#kK shpȓV -&Q0aLcBPus7>י#Rկ~p28pJR UR p@вpH-.4&'c7 ;4H5| ^PܨC9,m56mPQx||责g“rToN..chZ$Tza@kGwu !#hCf8Rr \2c`h2<=a F|w?7.%-k_!8sJR@MtAf@= dNZqgrqt5( %c-a6 OMM:rx==ۮ@48Q~zvXO\]{% 1aI;3KX$C> {ULR@kV-ŭVO>D1 7,N:ix_@@fj@0q=H>)Yy]ڗ Xp2Q. ?p-I8PR`JhR_s.l~eEbE61`>uWSA+u\{{נr3T*J  \%$w sdsdr8:!=9`RwJx 83^K8@B– ,7BxZݭ()'u&@2v!49`ڌmeBP=ă6˹JJR@+᪾ ĕ3N { yV0lٮCV||EE>Q$؜bŊLB}{*Q;q]5J |@P%$ =ݪGoq F(r<2FBδo,˜]tQ3(f5~JJR@k8@BoϜ%Uz_}y}Gvx|EQ6T jn^4U3@c?K~8a 7mҹJRԠkY&:I`ƒ{#I2gmoƼ,x򗀔v^fO v~,0@"tUӭ*ԆG>裏.u| +,+Q{` |A0ZPh00pPqz.{JRTjԳU/b<B9&xJB2Ur q!JR$&& Tl9 r8OH_` I? hq"/.F:aC}*xυlrČB@'cu&͘ޭZw_#/˸>e?}e<@Y*JRAs5@p\" Аxw@sP[H *%7P9- q8F 1;`:ɿ:>rM7TBBjo$qWE$x.=ˆ5E彤{3gf\B|;K(N򹢞}ΕF'HA2 @H^s> ~L=)3Eeep^r|cȒg!8v^xq9W0%}a-*˷y}cV[{\RTj5H2Âũܘs=me8JBb/x>ӆ<qQ3`&H d +r\iF,!B.~:R;xJ͵ }Ҟp8S\2g@&1Oq95X>h'_ CLR@k‚ 8p@04nPQ@qO+ғnX &'NWH8xLV:_ IXr9G*>)xpoyH110f@g 8q<r8]U.Bhp0[ۙsJRA Uum6r)^CB`Q{ ( {qb=xRqe;0\q# Ā0%\EhS29VUjF@?A rUTP@MqEs:3|.pf,5(אJRTjQB;H8X-h83i?s Y)Pkr(@ Zm*Rs)ΘQ ȕh@Tm.1 $ [rJLYF>\,XW\qpM-@eLnViC/+ciWaT*J -_HN $(*|f\lc#(!9n{]ZiKb:7I x9/+ %"iMnt7N0A<^xF9aΣH6s 8@BjX, % <Cm8)|)9YO"?ˋ[-"ݸ6m6laT*J fe)"K0qc@s8, p$DpnМ}8Ius/@5"|ի^U p\Ny00%d )ZQh+0c#icRГôljո򀐲+Gb wn,0hP't$)aB C*!4%|'W)ڵ.6}KUҖ\)>s/}K Tn/#I*mFc69U/HЇՍ\+lӁ2 Bvbg9U4feCrzT*JR[Nh0tE < J& `5P@ qr|sНTFޓ>iI*jDn"QoJѸwL4 7cP4Y1( X}3)0H1+`ӯ\/I#tr r AGOX^pe ۱T*JR3Wc:W@@ X ^@TxP&R} xh`ť] d*% g4-_sLizf*c26!E^ܵ]B5}j]}gR85jB?@I8,  蠃*޼i<$ 39@@ ( " 09.Iuxs3(h9sx: *d,]c,?pq5i歏:;Kj'`)W}k˼&{Vuw-#,n0G4羦ڂ*sjU`E wo gGJm׿<&?֌8t\? CR;PFރ'50z0`e[\U{r$)8799ؓũ26<,cV e ʭR?e[v7 4ɡ %Thu` 9x_ ) Ѿs*,-4Lm[TUzԖ%?#b}ܦ^:WU^`GX}p](xx (]5 ہ(=^ E/*ąK\t\;:ycS~$# 9>IO*Ms̍{%X bC% :αY!,ݪNEb4;4"a.$oss=$sPo՜~ H3&& us@>$q0"gu qJ>S8OK Eb{8P/IU`.+ >IQ.VH(Y*tVM׀Jm Jakvqc<@ O.ҥK쓰RSyU)'rxcq4I:?C Ƚ^>u\%"h!`JtZSeN@ |SJ`+% aӿ뀞cڷ)/s7nMOc1W:CԸ!*-anyR3T/hfiN!p80mo{[\Gy@]0ŋ Q6X%sLNqȄ\+.p~UW3}cƕRSK/pĀL9aEr8fB~.= [x q`6 # e&XR҂)r:^My+XP5CWX/F87+`%p@ 0r\Qn>U_WJh6LIG pŸRQ# EQ4f(W 7 r C)'Y{2!A}j3г1 8`g}UsJGӹo:.M fF]-UsB[`FGLJ8 #8@ fāiɒ%Z)@FԜ6_$5`YRAy;ꨣ׼5չ[Bd  ])]ॽIOB@ӥm(?~2SO=0!`X‘6#Ws*M뜒9ZXT/m%//t@"Y \.( ñcLN 1 +FNp wH邏NNxK\ثT$=!(@/|8E[( t|{3QܮؒD/|_[W^yeɝkO9S^ɑ26 %/ʼԾdfʕ%OJvЎUƼV~{u1T'pBXp'4oU#RXT0 zʽg$kP&f\XV}]wUfoZ!W`R5'HdbI\O-(!1H>V]r%qW%9[^.袲pd  1`;3]nQ\ۯ0yT^-o ;rN?pݜ-㐃f!t@Y RcjE^̂>Woqhhh3ƩV5m Kbn 8Tڵ^ؤ⺍cpoKmy5-ZP6֮in45[FB*P) PK^9G|pO+9;dž%rl)Ҟ6`nGI@ I(`tkJM.탶>H& pI LsepWD/0ۢE% `|WJQBrÄX7xcG޻*M(xHKYjtչLS/cLw6-V/kǪj]_S-n'0B+&fIв_S5SLѲܒX]<*A $$+)4:8IŽR]29 ȜuY &B L< w)>ֿݬqL⽞?MBMr-6 MվnIԣ)ӂз%PS7h0~C1o p7E !9)8w|,]') qXo.AgJΒs/qk={Q sNt˝VAnv!s +4qά`o)Pi}M{/qPX֜ͪb*cWkVνQAh\9W\- '2.πQ{@Q^xaMe\ @@+0hj\RSbi[T;FM~g_5pu)> sn&tC =3Z_קlj~BḶs) #GXh tO<Ӵ&g7Y7}֮SxM/B0${/4'd,D*9ED$v P Dq!3Pma<yK%a4 q><,@ ^r|fL\79`1N:Q_ B\/c 8}]uU@*\g9Zi,UXcaYЈ olnӒ'V<-߷?FasW/ LȋX;cg%\S8e`Y]o?_t~6WWggZM~DWM~ m_pg$m q S p ȉ @.Y^uBg x]% p8J@EBL 0Ø@pѿqw߽$ɣ] zڸ٪FeB}/0d: r8XѯyMz衦20uSؔĴ:M׏Umcj6v7uaawX .I`Z_1#46pBM f}KG)&٩e]Kl 7I][\wu%qoVwPN *؏vSW۪]:W`#!51!:BJ:+("e"6_^u0"e$GKNcB~BN\8!YoEC}΀v >.P  MrW<#=HT؅=?6a\:^@R8^pr9}ƕ5 ~n#Bm+3dzb -λi&U=qӚj&j3@.?Ӧyt\,:[=/$.նjרpU-I 2 Sk`½|C%TQ3̀)zK1tCОuVˤ?yUJ:U{<㐐5 q 8/a/z;Y;䓇K>C| q u 6#Ԉ@նMfݴWڠk[S.1E8mK2i2dGv;m/Id\)j2nOޣ'\,W\Q`mh Ŷ0`>u#'\BfD["$'K:{ir G DnC!<.`3F@mm:7ɸO9Pޕ> j{ڵЬ"4Em; ]OwY@ (yrToCNн5L2Q@EU7ԫ[\X ,akw C15]|OzzUl:؀ך5k @']˝wQGU>\)R)p`n@R @K‚XsmwESv3v~F$sʴMsr;!4ikp:!Yx46hZ(}K_*qwFmivvk9\k urdutjgnf\IJMƔp\'Ԧ&߷~N kU–8UE{Y,w >?tՂ*%s@ H^!pxkX T\"tq\o"aj%A^z补v8(8jKsz+^Q9m|{fJ4D}=UU+5m|)5U{Nl !fV^Wj*qC;@d?:mծ# HDR{{ Ƚ @#J(@v<,{6&hi{!;zIq@xzt Z8X>AuD|?DW˗//!H. 店%Gx8V)R*tz<`吙Т0^_:i¯Cպs39Y-\P'Ou"hYSh] XMlY0ݞW5w[mծ!1@4H* %2D})g򭜯uH i(PհPV!a5GQ-֕b,q3>|.HjԿp#( ;4sU(0h[;\5x+`=c4^p Dܵ,(ڿZ֒q׮m9?? mVfd_ҊipnV'wG6m1(\y&D r@ՃP,p\}8j\2!Gb`1nH7^P9g*;ǂT_Z_w֛$^T/gb*7N6Ou&N[V̚ zwS`_V6^w>&Jcݫkt_ia?YS+\Eݧ(ĺPIm+Ilv(a945 jE D`ႁ4=!AE/gL\Z%<;qbn^+5㗜/\*W?o|c29VI:[,>UhLIMw@˄i W\ &j 63Ziν>:6 ͯ{TS ɘ6p ixm.hv#<%ksflݰ{Vn f CBYcs@#PG]na8]7"AfHPwr9"Hf\%7KBpym<`P-eN%S3c ʌT; t\it2 ONǕ2k/!{Ujth_.uք|QY}AnzL#>^aA[q(9NDG>X~K@+@*Ͳ11PNiK$<`5nmh)|jl\#%J .+D' V8 ӞRj @G-HMXtmoV5\M˿Wuq~X>Aqۜe+zl-uGق._6z>9ߓ[zT *4-r-vwհ.ZX~E`lYZU*ˇ[mU$`%k0\%.; ̨( IDAT \n>+ǹ>O([ӽ?aGrRDkC5q<}7JhB8+9V8>Tge%*eq怅o~Geun<1U51cw߽Ts5+~a7fVK#   .( A‡)9X2GZ qԄO+%b>ǀpό;T$ MX'\'bU"@U9žsfkɛo&2И׭[WMo*^}tЪ'/f՘QMIhiaQi]rzZ 4WSa/R犼v9M_-X7MNQߘ-;OzܛYQ۪]#* G6'Gqs$\>p@]!:Xh;E- G7r$9f?0Hܟ:G*A\0%}j˸v9 %pZŸVʯ2oP crvAՁXB`u)t< Xcj2$GX:d5 Mxv퓵!s}e6Q6պ>~V˧xLЉvXwK[U}VnpU/~)GH/!JX^Bj c)Zr?{Jgr #gL&UVW0 88H92~SQ>|_+% 6 L- Gp=ⴝy晥$(* <2DBA%[n TϤ V{x /)}7>Qs i)S}M?ζ0Sm.6{gv7WF&Ȁ+~s>Ǫ:N|APYxXv9~P D[}0iP1NsC)edNn#fn.C,+y@ [_Vn6jȚ~OA$RT+ʽRg+|9^kKknBT}z]W57Wj*Z^L7h2wt?Otm?:7Y #પ72X%R-pb*NW_]`c6 \q 99 '%z9W2rH p) 4ȩr8N>l~ԏm“5:IDr8Xr-y? e2_s9ʵBԦJ:$_4 W/ƵtS남S]xu/Sܾ 6}f&O;6l]=&|3BZk{nix~fkUu+N PGD>(.Xk^*,=0U`JIpX5L+/93KNH`*%A`d|N@@"H{ֳUX&  oi$9q 8/׸7\1P*ݫf!kC]\tv}hq4aB0tj:.츼m{ {AasÏt߯{qs ֜DžLh0}9):so$%)%;*E,cμKK%w2+W_|qH qk}aj=fYVNﵧU,sU}@)h/9pGx&=,oui{)߶R\Y~4Բ^scߜŁӈ APùK%R#D`BHLق38l$W3$.W#< A*fwԝ>BQ*J%*=]wݵ?r%<Z Z8&A2a?0g.b J2 r3ear.b sTN*KmcfiPgdjǫ{0~Ol y/l0k '+MM;8B\(!=04K.Ӓ%K `qp->r2# qATԩ{@- 4iGXյcV%mh)<%}U9eVֽ{)N~KœXEy/\',q',J1RT*5YW۪]#Cs疼 + ŕPq. JBl[.Nxl#]F{><(I& @grXY\h\ h\.@rʢH2N.|-@~H2qPZ/(4 Lq\U*JYc 7vӬ> |st!Bk$,vG l9Kqw^ q@pZQB+ ^׷A^"p@R;HE}*_yn\H{ q(8/we<`u–QN*}_?rۛT*JMbK5]e^>OnsPN=r/` OBj7^]zWqs@__{9 /e} Zr+I.t)qy?* JJ8u2 '0r8T vxn^ceg*88^ԗw Zr =[M&(T*JM&zUϡ{XB;Ǘ=:NO \(!3 .w lX=('ƽ[خf=(ui<%A x8/ '=`T&$!Dna7~R-NsO9.˹ ^VF\$ GNY5<2Is* XT*FmMK3tSJYa ^~XP7FXN P"'##L0Ƹ@`}{_q8Gt\+(l@E[*?zUu6Z =Ā.ccdK$/]t)GämI!BsVBv\rˬ`HؖǼ e,i~7EDA1}ϵdeX0JR3@^ʹӢO;UUͭ2 $xHNT@g Bh` @ɱ^ q.p8a`̜@LVq 0#+.O^:I77INnw[  Im4Hq3omsb|C;:YUUs}CV#Ry STU_B5,TV5# (# ` !p +B1nx1\A1NϜo^L0/$Ps@\PPT]H2cdYsXXn{ "ST*oXpC *m rHIa,b.Bj8IrsO9B>Jkq5ה׎p#Yk_~@y#,!@c ~4$ (-t2qcMHz5hEAUIz!HP:WIQ:WTOkP\G9@: ȭƈ3fF-!05& 9pq 8$slc;MBJ$&8MMIW5(U]r>1q8ҷxtM;g|J;p" [e,3.fn2'$9w\¢g|11~ҹJR@ka/wPա8!3##)+_J '%&Ssa1`@P!AɒTMq@wIrl/V52`$") y(;_WohܠI\2X&2 %r7;7} O7 :@ WT*h $\p! ]y%W @ p B=IΑ$Tn%HH)s;j{NS&\rbD <]~p)%:SV1#w%l@;Ƚ2&e  /is.䒲uPlCT*J b>-2E @m)H}(sJlp(H4AR p83ٛ1$K6 ) x./2O4~EœҌQvI t2gl J9W pJR Uץj k\R=O.JqdB`X!8R9`͵L1"O) 1M\m8U{WDԪU +PH.C-}]Rjv+rA{7|v'C!Is1&]?eAЖST*Lp:4JM<*U\N+Q'Q"AO,jdq '// ()i@n\-f$dk80~W ;NDy/Pt %u"kflLrpde&=r*6֗6a+[p#S\RTj5a^EH: pTRp_W3,@Zzu/mPKϣ*(3RT*5Кp%ŕ  Ji͖ +cօ]+!ndp绞C %K&R&|IFv(ɹ=\+.4`JQ:G<g@0܁,'.Z\ovˬ 'k,@M8BZ>NU׷UpJRA V#e=Q V4$! XX9ӆrԧ ) &<@ c?AcRG hȕfMQA\/C.\)s 9OsXԲj2+ Ҏyt Ci7c%@!s8\Q6  g?M¬n\re'W}r9pAkݺuAP T*JC] \XouW(2$'l1SBh*ێu:n' y$p#px/ArqBqܢL~Q2_~]o1P%rjJy[r:nQ X]gn#fՂLR@kspp'*@% Dr\.;%"/ 09DW 4|+cq.W 7ehA+`)9u$[(yͩAn. c+aEsK|>k 9Qdu ^\8en pJR@@@ԁEUV8R9P4CeE X i*Bz`GO^=K<[9hҗ+t>\(B P^P8]zW%Oc̉exVF ܌;J@EL1 /*JRk+Qի(-q' { *@2՛Fvˇ%dA\ԘЇ>T@y(eR&I9.<}!AmIׇp9T` @c>BEɡ9"k O盟6?ϔVI:ǽQ].*JRk]gGqD~%I: jtV]w] )Wd8 •-xPkf͚IIdU# SA>ΘB‚;!AsdΓ%oc??nT*J  Z6my#><. %1C`Oĭ! 0|Кp<% $k(+\(p|8eQ q@]}%*r;LP@Пvm_rl^wJH4W!QI}=(QzGBV1#RT*5 =K1+@Lpi8/W^yeIR`sK%0tIKBr<Ȓ'1(q%.@:#A+H8= rnq b){vaU=%^gQv+pҦ9r.. c{U9?JR`j̰`U$yk<@GB@q!7@ pTM"7+= qJo{IjW?K`qP$uyXxl %^<2>tRT*5ՂVk. pM@BҸqs)+O~S"4a8p!7 P iK@గϹR\,!7Ou)qfܦ҆%T d\.A|P48Kqׁ9ڛҗ+'\iN(?ϭ>܁(IuµNZ%NU*JZ W ˗@HlL {3e ?jyk_;H0dA[IDATa86mڟp@.x`1k8Kt2P q$;,z+ Bj_ITTŖ>q˝\|ХLßٟ:BR"~$ 0lnK6*JRʹzX)^@ 80YHNSDTrp㽕| mnp[$&(B\p~ yB(OI>ؒ%K -Z~}õ%S=u 䖁E}EN8EM-Yq$[`𦾴{iJL̹JR@gl-oyˈo=:@5w `gȪ>ҁwy%' qw= ,@ĭ Z|&&d'|= HP?LhF(IRnDoG>$}׃<37wn*aAr{ hlD'4펡g]tRT*5$jI q]@ITJ?r@=`yQ]V {%d9Kⷒ x.&O 0Ƀ>V&ɜ.!<9N*s26Gډmz>LnN8Nk~ K/\/IH rԴ\ *S+}{?}WUP7תKXA`l<T*JRӤַjy@\&_@Fr: 1l$.V70>sݙgY OS@Ûɜ󶷽RK9P LJN7d6jw:P TAPk#ǜ ni{F=yr!ϙ3'ÂT*XO~r<0H(QP& G H,'qU64 Nm#ljpP`9묳J7ȡ:s Hi P$ϊf% @`T&B-u ',WU3f,'嵰(xuUW +C-\C8DGR@H5tїב'̨].04,j_cW8Z<*}mz8ܼ,y^nشR?VU^s5a{SPUUsJR@<ꨣiqj9h0. P}`` p+t@9`se@'8dx9P(mH<\2Уe^>z(8/ucҿuc琙'`dv]Byp#SGlFNYͨ +Z\'?\RTj`5Pa?>|sl@x*yGlB|B`/r'v>Fx-Ҏ>9nQTm"SJA0$Xї&<r⾩mel&z8g JxѼxgL/ q\É6Iwqs׸51rQ qRT*5(:{7Pr_`k+섽<H$ t0sš}s~W`kDROyS~Wg~Û;#mr}pq |9 $ 9 Mc1B\)1h//`|D.˘lDEQ<b#Aߞ-.p̔wU^կn9_UUU*JVs;\*  _DUP07|q]Zi%6m"q"8v9`"t& /:C-3c `G_H`hV=A+0nݺҞ.(3o<\ ǕsHEv5&&}k~לkN `N)_0.W_];p=ܓaT*J *,;\`ܫ|sE)ε^[`KE=!,%ܳs6^\!$gekp @p'3f~9Wc~L ,;׃#}k<=79I ԁ86eB@ @:|k+r'/{Y>KmA=y:WT*X 7tN+\4qi/|rn9yqN/pvsjP"D1pkZDqx q~z!;@_8Låm\@NV"hl8sa`%il`򻀐~8t'b䯙3@27,Js}f"]%tRT*Jmz͚5+կq0=S '=.w9g`!"9E$7:ɹ9`,q8WNM\a4m\gn_p*"$=t>8Q2}٧AѾ8I#wJ(tUŋ{wxЬX\O=v/5o޼;tRT*5(JBo}ȀǸK(/uh(w\(; qfkq 8AnN8T i7JJXx\]R;[o-mr|&p Oؙ1҇ȥ6:Z';=zڍs$sST*JmzmYV[pR${rx )J+,!9pgg݅@xJ6 &^zi3ŕ6 (G.2 q:SPOhiϹNE2j} (rY#(0JBsOӽ6_ȷz1!C,Θ\|v-T*J nQz\/zn ExJ8>J(2`EB@\gŠNH}-(Ep\]:4a?\&+ 9U+1Vǭ+C|͟KPn 4g~`9P7AyQU }§Xv?n~:蠄T*J ézvm7'L T ;()F?_$+( "VG}tmS\,.8x; h+bK\=%\RTC Bv(#h)um K j6җ`1\*P焹7g}vbHRjJ1P?LE 'r894 7D8Vi[؍(<˖-+< aƓO>@K&,Y:S/`#[ h8f~͑3doAyM 9Orj< <dims5VBG˼]ϹvFs9r欌^k.\JRTjPkŊ=CgWk`Oa=_\@*6b^P|r-x[R-_FbHз7m@a焄DiRsFSSԔB>+Y1< ƞEHzr l.'0$'N8(iN㱃89W(9Gn9; 9fbr#E@@5b@zΖ}CSFj!6*Np8sU*JjV7nf'>4iVk<۽p@~W9ya=s-Ps@2]\ +/q!{e?({!)委iZ>W1qsrV+ߩ8]\.өT*Jլ pao\ރ&0ĹW2aZ)ꃍ S$8rf!`;z 9ֹq!Թ1@"nq}߿?+JRi& \5M ցwEIENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/_static/openapi_view.html0000644000175100001640000000113100000000000023646 0ustar00runnerdocker00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/api.rst0000644000175100001640000001624300000000000020162 0ustar00runnerdocker00000000000000API === The external (json/form) API is described `here`_ .. _here: _static/openapi_view.html Core ---- .. autoclass:: flask_security.Security :members: .. data:: flask_security.current_user A proxy for the current user. .. function:: flask_security.Security.unauthorized_handler If an endpoint fails authentication or authorization from one of the decorators described below (except ``login_required``), a method annotated with this decorator will be called. For ``login_required`` (which is implemented in Flask-Login) use **flask_security.login_manager.unauthorized_handler** .. deprecated:: 3.3.0 Protecting Views ---------------- .. autofunction:: flask_security.anonymous_user_required .. autofunction:: flask_security.http_auth_required .. autofunction:: flask_security.auth_token_required .. autofunction:: flask_security.auth_required .. autofunction:: flask_security.login_required .. autofunction:: flask_security.roles_required .. autofunction:: flask_security.roles_accepted .. autofunction:: flask_security.permissions_required .. autofunction:: flask_security.permissions_accepted .. autofunction:: flask_security.unauth_csrf .. autofunction:: flask_security.handle_csrf User Object Helpers ------------------- .. autoclass:: flask_security.UserMixin :members: .. autoclass:: flask_security.RoleMixin :members: .. autoclass:: flask_security.AnonymousUser :members: Datastores ---------- .. autoclass:: flask_security.UserDatastore :members: .. autoclass:: flask_security.SQLAlchemyUserDatastore :members: :inherited-members: .. autoclass:: flask_security.SQLAlchemySessionUserDatastore :members: :inherited-members: .. autoclass:: flask_security.MongoEngineUserDatastore :members: :inherited-members: .. autoclass:: flask_security.PeeweeUserDatastore :members: :inherited-members: .. autoclass:: flask_security.PonyUserDatastore :members: :inherited-members: Utils ----- .. autofunction:: flask_security.login_user .. autofunction:: flask_security.logout_user .. autofunction:: flask_security.check_and_update_authn_fresh .. autofunction:: flask_security.get_hmac .. autofunction:: flask_security.get_request_attr .. autofunction:: flask_security.verify_password .. autofunction:: flask_security.verify_and_update_password .. autofunction:: flask_security.hash_password .. autofunction:: flask_security.uia_phone_mapper .. autofunction:: flask_security.uia_email_mapper .. autofunction:: flask_security.url_for_security .. autofunction:: flask_security.send_mail .. autofunction:: flask_security.get_token_status .. autofunction:: flask_security.check_and_get_token_status .. autofunction:: flask_security.get_url .. autofunction:: flask_security.password_length_validator .. autofunction:: flask_security.password_complexity_validator .. autofunction:: flask_security.password_breached_validator .. autofunction:: flask_security.pwned .. autofunction:: flask_security.transform_url .. autofunction:: flask_security.unique_identity_attribute .. autofunction:: flask_security.us_send_security_token .. autofunction:: flask_security.tf_send_security_token .. autoclass:: flask_security.FsJsonEncoder .. autoclass:: flask_security.Totp :members: get_last_counter, set_last_counter, generate_qrcode .. autoclass:: flask_security.PhoneUtil :members: :special-members: __init__ .. autoclass:: flask_security.MailUtil :members: :special-members: __init__ .. autoclass:: flask_security.PasswordUtil :members: :special-members: __init__ .. autoclass:: flask_security.SmsSenderBaseClass :members: send_sms .. autoclass:: flask_security.SmsSenderFactory :members: createSender .. _signals_topic: Signals ------- See the `Flask documentation on signals`_ for information on how to use these signals in your code. .. tip:: Remember to add ``**extra_args`` to your signature so that if we add additional parameters in the future your code doesn't break. See the documentation for the signals provided by the Flask-Login and Flask-Principal extensions. In addition to those signals, Flask-Security sends the following signals. .. data:: user_authenticated Sent when a user successfully authenticates. In addition to the app (which is the sender), it is passed `user`, and `authn_via` arguments. The `authn_via` argument specifies how the user authenticated - it will be a list with possible values of ``password``, ``sms``, ``authenticator``, ``email``, ``confirm``, ``reset``, ``register``. .. versionadded:: 3.4.0 .. data:: user_registered Sent when a user registers on the site. In addition to the app (which is the sender), it is passed `user`, `confirm_token` and `form_data` arguments. `form_data` is a dictionary representation of registration form's content received with registration request. .. data:: user_confirmed Sent when a user is confirmed. In addition to the app (which is the sender), it is passed a `user` argument. .. data:: confirm_instructions_sent Sent when a user requests confirmation instructions. In addition to the app (which is the sender), it is passed a `user` argument. .. data:: login_instructions_sent Sent when passwordless login is used and user logs in. In addition to the app (which is the sender), it is passed `user` and `login_token` arguments. .. data:: password_reset Sent when a user completes a password reset. In addition to the app (which is the sender), it is passed a `user` argument. .. data:: password_changed Sent when a user completes a password change. In addition to the app (which is the sender), it is passed a `user` argument. .. data:: reset_password_instructions_sent Sent when a user requests a password reset. In addition to the app (which is the sender), it is passed `user` and `token` arguments. .. data:: tf_code_confirmed Sent when a user performs two-factor authentication login on the site. In addition to the app (which is the sender), it is passed `user` and `method` arguments. .. versionadded:: 3.3.0 .. data:: tf_profile_changed Sent when two-factor is used and user logs in. In addition to the app (which is the sender), it is passed `user` and `method` arguments. .. versionadded:: 3.3.0 .. data:: tf_disabled Sent when two-factor is disabled. In addition to the app (which is the sender), it is passed `user` argument. .. versionadded:: 3.3.0 .. data:: tf_security_token_sent Sent when a two factor security/access code is sent. In addition to the app (which is the sender), it is passed `user`, `method`, and `token` arguments. .. versionadded:: 3.3.0 .. data:: us_security_token_sent Sent when a unified sign in access code is sent. In addition to the app (which is the sender), it is passed `user`, `method`, `token`, `phone_number`, and `send_magic_link` arguments. .. versionadded:: 3.4.0 .. data:: us_profile_changed Sent when user completes changing their unified sign in profile. In addition to the app (which is the sender), it is passed `user` and `method` arguments. .. versionadded:: 3.4.0 .. _Flask documentation on signals: https://flask.palletsprojects.com/en/1.1.x/signals/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/authors.rst0000644000175100001640000000003000000000000021061 0ustar00runnerdocker00000000000000.. include:: ../AUTHORS ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/changelog.rst0000644000175100001640000000003400000000000021327 0ustar00runnerdocker00000000000000.. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/conf.py0000644000175100001640000002052400000000000020153 0ustar00runnerdocker00000000000000# # Flask-Security documentation build configuration file, created by # sphinx-quickstart on Mon Mar 12 15:35:21 2012. # # This file is execfile()d with the current directory set to its containing # dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys from pallets_sphinx_themes import ProjectLink # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) # -- General configuration ----------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ "pallets_sphinx_themes", "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx_issues", ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "Flask-Security" copyright = "2012-2020" author = "Matt Wright & Chris Wagner" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "4.0.0" # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. # pygments_style = "pocoo" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] nitpicky = True nitpick_ignore = [("py:attr", "LoginManager.unauthorized"), ("py:class", "function")] # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = "flask" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = {"index_sidebar_logo": False} html_context = { "project_links": [ ProjectLink("PyPI releases", "https://pypi.org/project/Flask-Security-Too/"), ProjectLink( "Source Code", "https://github.com/Flask-Middleware/flask-security/" ), ProjectLink( "Issue Tracker", "https://github.com/Flask-Middleware/flask-security/issues/", ), ] } # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = "Flask-Security Documentation ({}).format(version)" html_logo = "_static/logo-owl-105.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static", "openapi.yaml"] # Custom sidebar templates, maps document names to template names. html_sidebars = { "index": ["project.html", "localtoc.html", "searchbox.html"], "**": ["localtoc.html", "relations.html", "searchbox.html"], } singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]} # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # Output file base name for HTML help builder. htmlhelp_basename = "Flask-Securitydoc" # -- Options for LaTeX output -------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ("index", "Flask-Security.tex", "Flask-Security Documentation", author, "manual") ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for Texinfo output ------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "Flask-Security", "Flask-Security Documentation", "Matt Wright", "Flask-Security", "One line description of project.", "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------- # Bibliographic Dublin Core info. epub_title = "Flask-Security" epub_author = "Matt Wright" epub_publisher = "J. Christopher Wagner" epub_copyright = "2012-2020" # The language of the text. It defaults to the language option # or en if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. # epub_exclude_files = [] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"https://docs.python.org/3": None} # -- Options for sphinx-issues --------------------------------------------- # Github repo issues_github_path = "Flask-Middleware/flask-security" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1611628791.0 Flask-Security-Too-4.0.0/docs/configuration.rst0000644000175100001640000012173300000000000022261 0ustar00runnerdocker00000000000000Configuration ============= The following configuration values are used by Flask-Security: Core -------------- These configuration keys are used globally across all features. .. py:data:: SECRET_KEY This is actually part of Flask - but is used by Flask-Security to sign all tokens. It is critical this is set to a strong value. For python3 consider using: ``secrets.token_urlsafe()`` .. py:data:: SECURITY_BLUEPRINT_NAME Specifies the name for the Flask-Security blueprint. Default: ``security``. .. py:data:: SECURITY_URL_PREFIX Specifies the URL prefix for the Flask-Security blueprint. Default: ``None``. .. py:data:: SECURITY_SUBDOMAIN Specifies the subdomain for the Flask-Security blueprint. If your authenticated content is on a different subdomain, also enable :py:data:`SECURITY_REDIRECT_ALLOW_SUBDOMAINS`. Default: ``None``. .. py:data:: SECURITY_FLASH_MESSAGES Specifies whether or not to flash messages during security procedures. Default: ``True``. .. py:data:: SECURITY_I18N_DOMAIN Specifies the name for domain used for translations. Default: ``flask_security``. .. py:data:: SECURITY_I18N_DIRNAME Specifies the directory containing the ``MO`` files used for translations. Default: ``[PATH_LIB]/flask_security/translations``. .. py:data:: SECURITY_PASSWORD_HASH Specifies the password hash algorithm to use when hashing passwords. Recommended values for production systems are ``bcrypt``, ``argon2``, ``sha512_crypt``, or ``pbkdf2_sha512``. Some algorithms require the installation of a backend package (e.g. `bcrypt`_, `argon2`_). Default:``bcrypt``. .. py:data:: SECURITY_PASSWORD_SCHEMES List of support password hash algorithms. ``SECURITY_PASSWORD_HASH`` must be from this list. Passwords encrypted with any of these schemes will be honored. .. py:data:: SECURITY_DEPRECATED_PASSWORD_SCHEMES List of password hash algorithms that are considered weak and will be accepted, however on first use, will be re-hashed to the current setting of ``SECURITY_PASSWORD_HASH``. Default: ``["auto"]`` which means any password found that wasn't hashed using ``SECURITY_PASSWORD_HASH`` will be re-hashed. .. py:data:: SECURITY_PASSWORD_SALT Specifies the HMAC salt. This is required for all schemes that are configured for double hashing. A good salt can be generated using: ``secrets.SystemRandom().getrandbits(128)``. Default: ``None``. .. py:data:: SECURITY_PASSWORD_SINGLE_HASH A list of schemes that should not be hashed twice. By default, passwords are hashed twice, first with ``SECURITY_PASSWORD_SALT``, and then with a random salt. Default: a list of known schemes not working with double hashing (`django_{digest}`, `plaintext`). .. py:data:: SECURITY_HASHING_SCHEMES List of algorithms used for encrypting/hashing sensitive data within a token (Such as is sent with confirmation or reset password). Default: ``sha256_crypt``. .. py:data:: SECURITY_DEPRECATED_HASHING_SCHEMES List of deprecated algorithms used for creating and validating tokens. Default: ``hex_md5``. .. py:data:: SECURITY_PASSWORD_HASH_OPTIONS Specifies additional options to be passed to the hashing method. This is deprecated as of passlib 1.7. .. deprecated:: 3.4.0 see: :py:data:`SECURITY_PASSWORD_HASH_PASSLIB_OPTIONS` .. py:data:: SECURITY_PASSWORD_HASH_PASSLIB_OPTIONS Pass additional options to the various hashing methods. This is a dict of the form ``{__