Codebase list python-faraday / 60059cd
Embeds the previous version of flask_security Sophie Brun 3 years ago
80 changed file(s) with 16789 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
3838 python3-syslog-rfc5424-formatter,
3939 python3-tqdm,
4040 python3-twisted,
41 python3-webargs (>= 7.0.0)
41 python3-webargs (>= 7.0.0),
42 # for flask-security embedded:
43 python3-flask-babelex,
44 python3-flask-login,
45 python3-flask-mail,
46 python3-passlib,
47 python3-flaskext.wtf,
48 python3-itsdangerous,
49 python3-speaklater
4250 Standards-Version: 4.5.0
4351 Homepage: https://faradaysec.com
4452 Vcs-Git: https://gitlab.com/kalilinux/packages/python-faraday.git
5967 sudo,
6068 xdg-utils,
6169 zsh | zsh-beta,
70 # for flask-security embedded:
71 python3-flask-babelex,
72 python3-flask-login,
73 python3-flask-mail,
74 python3-passlib,
75 python3-flaskext.wtf,
76 python3-itsdangerous,
77 python3-speaklater,
6278 ${misc:Depends},
6379 ${shlibs:Depends}
6480 Recommends: fonts-font-awesome,
11 debian/migrate-database usr/lib/python3/dist-packages/faraday/debian-scripts
22 usr/lib/python3*
33 usr/bin
4 debian/python-modules/* usr/lib/python3/dist-packages/faraday
0 From: Sophie Brun <[email protected]>
1 Date: Thu, 11 Feb 2021 11:07:56 +0100
2 Subject: Use local flask-security
3
4 ---
5 faraday/manage.py | 2 +-
6 faraday/server/api/modules/token.py | 2 +-
7 faraday/server/app.py | 6 +++---
8 faraday/server/commands/change_password.py | 4 ++--
9 faraday/server/commands/initdb.py | 2 +-
10 faraday/server/models.py | 2 +-
11 tests/test_command_change_password.py | 4 ++--
12 7 files changed, 11 insertions(+), 11 deletions(-)
13
14 diff --git a/faraday/manage.py b/faraday/manage.py
15 index 973dc59..b84a1ab 100755
16 --- a/faraday/manage.py
17 +++ b/faraday/manage.py
18 @@ -53,7 +53,7 @@ from faraday.server.commands import import_vulnerability_template
19 from faraday.server.models import db, User
20 from faraday.server.web import app
21 from faraday_plugins.plugins.manager import PluginsManager
22 -from flask_security.utils import hash_password
23 +from faraday.flask_security.utils import hash_password
24
25
26 CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
27 diff --git a/faraday/server/api/modules/token.py b/faraday/server/api/modules/token.py
28 index fe8e224..4ffd763 100644
29 --- a/faraday/server/api/modules/token.py
30 +++ b/faraday/server/api/modules/token.py
31 @@ -1,6 +1,6 @@
32 from itsdangerous import TimedJSONWebSignatureSerializer
33 from flask import Blueprint, g
34 -from flask_security.utils import hash_data
35 +from faraday.flask_security.utils import hash_data
36 from flask import current_app as app
37
38
39 diff --git a/faraday/server/app.py b/faraday/server/app.py
40 index 6ff58f3..697416a 100644
41 --- a/faraday/server/app.py
42 +++ b/faraday/server/app.py
43 @@ -17,12 +17,12 @@ import flask
44 from flask import Flask, session, g, request
45 from flask.json import JSONEncoder
46 from flask_sqlalchemy import get_debug_queries
47 -from flask_security import (
48 +from faraday.flask_security import (
49 Security,
50 SQLAlchemyUserDatastore,
51 )
52 -from flask_security.forms import LoginForm
53 -from flask_security.utils import (
54 +from faraday.flask_security.forms import LoginForm
55 +from faraday.flask_security.utils import (
56 _datastore,
57 get_message,
58 verify_and_update_password,
59 diff --git a/faraday/server/commands/change_password.py b/faraday/server/commands/change_password.py
60 index 42a7b1d..cfd6559 100644
61 --- a/faraday/server/commands/change_password.py
62 +++ b/faraday/server/commands/change_password.py
63 @@ -1,6 +1,6 @@
64 from faraday.server.web import app
65 from faraday.server.models import User, db
66 -from flask_security.utils import hash_password
67 +from faraday.flask_security.utils import hash_password
68
69
70 def changes_password(username, password):
71 @@ -13,4 +13,4 @@ def changes_password(username, password):
72 print("Password changed succesfully")
73 else:
74 print("User not found in Faraday's Database")
75 -# I'm Py3
76 \ No newline at end of file
77 +# I'm Py3
78 diff --git a/faraday/server/commands/initdb.py b/faraday/server/commands/initdb.py
79 index d09b84b..175c369 100644
80 --- a/faraday/server/commands/initdb.py
81 +++ b/faraday/server/commands/initdb.py
82 @@ -28,7 +28,7 @@ from faraday.server.utils.database import is_unique_constraint_violation
83 from configparser import ConfigParser, NoSectionError
84
85 from flask import current_app
86 -from flask_security.utils import hash_password
87 +from faraday.flask_security.utils import hash_password
88
89 from colorama import init
90 from colorama import Fore
91 diff --git a/faraday/server/models.py b/faraday/server/models.py
92 index 40dbe44..e3ad6ab 100644
93 --- a/faraday/server/models.py
94 +++ b/faraday/server/models.py
95 @@ -46,7 +46,7 @@ from flask_sqlalchemy import (
96 from depot.fields.sqlalchemy import UploadedFileField
97
98 from faraday.server.fields import JSONType
99 -from flask_security import (
100 +from faraday.flask_security import (
101 UserMixin,
102 )
103
104 diff --git a/tests/test_command_change_password.py b/tests/test_command_change_password.py
105 index 99efcbd..0bec8c7 100644
106 --- a/tests/test_command_change_password.py
107 +++ b/tests/test_command_change_password.py
108 @@ -1,4 +1,4 @@
109 -from flask_security.utils import hash_password, verify_password
110 +from faraday.flask_security.utils import hash_password, verify_password
111
112 from faraday.server.commands.change_password import changes_password
113 from faraday.server.models import User
114 @@ -15,4 +15,4 @@ def test_changes_password_command(session):
115 user = User.query.filter_by(username='test_change_pass').first()
116
117 assert not verify_password('old_pass', user.password)
118 - assert verify_password('new_pass', user.password)
119 \ No newline at end of file
120 + assert verify_password('new_pass', user.password)
22 use-packaged-filteralchemy.patch
33 Remove-failing-tests.patch
44 fix-flask-security-requirement.patch
5 Use-local-flask-security.patch
0 # -*- coding: utf-8 -*-
1 """
2 flask_security
3 ~~~~~~~~~~~~~~
4
5 Flask-Security is a Flask extension that aims to add quick and simple
6 security via Flask-Login, Flask-Principal, Flask-WTF, and passlib.
7
8 :copyright: (c) 2012-2019 by Matt Wright.
9 :copyright: (c) 2019-2020 by J. Christopher Wagner.
10 :license: MIT, see LICENSE for more details.
11 """
12
13 # flake8: noqa: F401
14
15 from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
16 from .datastore import (
17 UserDatastore,
18 SQLAlchemyUserDatastore,
19 MongoEngineUserDatastore,
20 PeeweeUserDatastore,
21 PonyUserDatastore,
22 SQLAlchemySessionUserDatastore,
23 )
24 from .decorators import (
25 auth_token_required,
26 anonymous_user_required,
27 handle_csrf,
28 http_auth_required,
29 login_required,
30 roles_accepted,
31 roles_required,
32 auth_required,
33 permissions_accepted,
34 permissions_required,
35 unauth_csrf,
36 )
37 from .forms import (
38 ChangePasswordForm,
39 ForgotPasswordForm,
40 LoginForm,
41 RegisterForm,
42 ResetPasswordForm,
43 PasswordlessLoginForm,
44 ConfirmRegisterForm,
45 SendConfirmationForm,
46 TwoFactorRescueForm,
47 TwoFactorSetupForm,
48 TwoFactorVerifyCodeForm,
49 TwoFactorVerifyPasswordForm,
50 VerifyForm,
51 )
52 from .phone_util import PhoneUtil
53 from .signals import (
54 confirm_instructions_sent,
55 login_instructions_sent,
56 password_changed,
57 password_reset,
58 reset_password_instructions_sent,
59 tf_code_confirmed,
60 tf_profile_changed,
61 tf_security_token_sent,
62 tf_disabled,
63 user_authenticated,
64 user_confirmed,
65 user_registered,
66 us_security_token_sent,
67 us_profile_changed,
68 )
69 from .totp import Totp
70 from .twofactor import tf_send_security_token
71 from .unified_signin import (
72 UnifiedSigninForm,
73 UnifiedSigninSetupForm,
74 UnifiedSigninSetupValidateForm,
75 UnifiedVerifyForm,
76 us_send_security_token,
77 )
78 from .utils import (
79 FsJsonEncoder,
80 SmsSenderBaseClass,
81 SmsSenderFactory,
82 check_and_get_token_status,
83 get_hmac,
84 get_token_status,
85 get_url,
86 hash_password,
87 check_and_update_authn_fresh,
88 login_user,
89 logout_user,
90 password_breached_validator,
91 password_complexity_validator,
92 password_length_validator,
93 pwned,
94 send_mail,
95 transform_url,
96 uia_phone_mapper,
97 uia_email_mapper,
98 url_for_security,
99 verify_password,
100 verify_and_update_password,
101 )
102
103 __version__ = "3.4.2"
0 """
1 Temporary workaround while we still support p2.7
2
3 :copyright: (c) 2019 by J. Christopher Wagner (jwag).
4 :license: MIT, see LICENSE for more details.
5 """
6
7 from flask import current_app
8 from werkzeug.local import LocalProxy
9
10 _security = LocalProxy(lambda: current_app.extensions["security"])
11
12 _datastore = LocalProxy(lambda: _security.datastore)
13
14
15 async def _commit(response=None): # pragma: no cover
16 _datastore.commit()
17 return response
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.babel
3 ~~~~~~~~~~~~~~~~~~~~
4
5 I18N support for Flask-Security.
6 """
7
8 from flask_babelex import Domain
9 from wtforms.i18n import messages_path
10
11 wtforms_domain = Domain(messages_path(), domain="wtforms")
12
13
14 class Translations(object):
15 """Fixes WTForms translation support and uses wtforms translations."""
16
17 def gettext(self, string):
18 return wtforms_domain.gettext(string)
19
20 def ngettext(self, singular, plural, n):
21 return wtforms_domain.ngettext(singular, plural, n)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.cache
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security token cache module
6
7 :copyright: (c) 2019.
8 :license: MIT, see LICENSE for more details.
9 """
10
11 from .utils import config_value
12
13
14 class VerifyHashCache:
15 """Cache handler to make it quick password check by bypassing
16 already checked passwords against exact same couple of token/password.
17 This cache handler is more efficient on small apps that
18 run on few processes as cache is only shared between threads."""
19
20 def __init__(self):
21 ttl = config_value("VERIFY_HASH_CACHE_TTL", default=(60 * 5))
22 max_size = config_value("VERIFY_HASH_CACHE_MAX_SIZE", default=500)
23
24 try:
25 from cachetools import TTLCache
26
27 self._cache = TTLCache(max_size, ttl)
28 except ImportError:
29 # this should have been checked at app init.
30 raise
31
32 def has_verify_hash_cache(self, user):
33 """Check given user id is in cache."""
34 return self._cache.get(user.id)
35
36 def set_cache(self, user):
37 """When a password is checked, then result is put in cache."""
38 self._cache[user.id] = True
39
40 def clear(self):
41 """Clear cache"""
42 self._cache.clear()
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.changeable
3 ~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security recoverable module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :author: Eskil Heyn Olsen
9 :license: MIT, see LICENSE for more details.
10 """
11
12 from flask import current_app
13 from werkzeug.local import LocalProxy
14
15 from .signals import password_changed
16 from .utils import config_value, hash_password
17
18 # Convenient references
19 _security = LocalProxy(lambda: current_app.extensions["security"])
20
21 _datastore = LocalProxy(lambda: _security.datastore)
22
23
24 def send_password_changed_notice(user):
25 """Sends the password changed notice email for the specified user.
26
27 :param user: The user to send the notice to
28 """
29 if config_value("SEND_PASSWORD_CHANGE_EMAIL"):
30 subject = config_value("EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE")
31 _security._send_mail(subject, user.email, "change_notice", user=user)
32
33
34 def change_user_password(user, password):
35 """Change the specified user's password
36
37 :param user: The user to change_password
38 :param password: The unhashed new password
39 """
40 user.password = hash_password(password)
41 if config_value("BACKWARDS_COMPAT_AUTH_TOKEN_INVALID"):
42 _datastore.set_uniquifier(user)
43 _datastore.put(user)
44 send_password_changed_notice(user)
45 password_changed.send(current_app._get_current_object(), user=user)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.cli
3 ~~~~~~~~~~~~~~~~~~
4
5 Command Line Interface for managing accounts and roles.
6
7 :copyright: (c) 2016 by CERN.
8 :copyright: (c) 2019 by J. Christopher Wagner
9 :license: MIT, see LICENSE for more details.
10 """
11
12 from __future__ import absolute_import, print_function
13
14 from functools import wraps
15
16 import click
17 from flask import current_app
18 from werkzeug.datastructures import MultiDict
19 from werkzeug.local import LocalProxy
20 from .quart_compat import get_quart_status
21
22 from .utils import hash_password
23
24 if get_quart_status(): # pragma: no cover
25 import quart.cli
26 import functools
27
28 # quart cli doesn't provide the with_appcontext function
29 def with_appcontext(f):
30 """Wraps a callback so that it's guaranteed to be executed with the
31 script's application context. If callbacks are registered directly
32 to the ``app.cli`` object then they are wrapped with this function
33 by default unless it's disabled.
34 """
35
36 @click.pass_context
37 def decorator(__ctx, *args, **kwargs):
38 with __ctx.ensure_object(quart.cli.ScriptInfo).load_app().app_context():
39 return __ctx.invoke(f, *args, **kwargs)
40
41 return functools.update_wrapper(decorator, f)
42
43
44 else:
45 import flask.cli
46
47 with_appcontext = flask.cli.with_appcontext
48
49
50 _security = LocalProxy(lambda: current_app.extensions["security"])
51 _datastore = LocalProxy(lambda: current_app.extensions["security"].datastore)
52
53
54 def commit(fn):
55 """Decorator to commit changes in datastore."""
56
57 @wraps(fn)
58 def wrapper(*args, **kwargs):
59 fn(*args, **kwargs)
60 _datastore.commit()
61
62 return wrapper
63
64
65 @click.group()
66 def users():
67 """User commands."""
68
69
70 @click.group()
71 def roles():
72 """Role commands."""
73
74
75 @users.command("create")
76 @click.argument("identity")
77 @click.password_option()
78 @click.option("-a", "--active", default=False, is_flag=True)
79 @with_appcontext
80 @commit
81 def users_create(identity, password, active):
82 """Create a user."""
83 kwargs = {attr: identity for attr in _security.user_identity_attributes}
84 kwargs.update(**{"password": password})
85
86 form = _security.confirm_register_form(MultiDict(kwargs), meta={"csrf": False})
87
88 if form.validate():
89 kwargs["password"] = hash_password(kwargs["password"])
90 kwargs["active"] = active
91 _datastore.create_user(**kwargs)
92 click.secho("User created successfully.", fg="green")
93 kwargs["password"] = "****"
94 click.echo(kwargs)
95 else:
96 raise click.UsageError("Error creating user. %s" % form.errors)
97
98
99 @roles.command("create")
100 @click.argument("name")
101 @click.option("-d", "--description", default=None)
102 @click.option("-p", "--permissions")
103 @with_appcontext
104 @commit
105 def roles_create(**kwargs):
106 """Create a role."""
107
108 # For some reaosn Click puts arguments in kwargs - even if they weren't specified.
109 if "permissions" in kwargs and not kwargs["permissions"]:
110 del kwargs["permissions"]
111 if "permissions" in kwargs and not hasattr(_datastore.role_model, "permissions"):
112 raise click.UsageError("Role model does not support permissions")
113 _datastore.create_role(**kwargs)
114 click.secho('Role "%(name)s" created successfully.' % kwargs, fg="green")
115
116
117 @roles.command("add")
118 @click.argument("user")
119 @click.argument("role")
120 @with_appcontext
121 @commit
122 def roles_add(user, role):
123 """Add user to role."""
124 user, role = _datastore._prepare_role_modify_args(user, role)
125 if user is None:
126 raise click.UsageError("Cannot find user.")
127 if role is None:
128 raise click.UsageError("Cannot find role.")
129 if _datastore.add_role_to_user(user, role):
130 click.secho(
131 'Role "{0}" added to user "{1}" ' "successfully.".format(role, user),
132 fg="green",
133 )
134 else:
135 raise click.UsageError("Cannot add role to user.")
136
137
138 @roles.command("remove")
139 @click.argument("user")
140 @click.argument("role")
141 @with_appcontext
142 @commit
143 def roles_remove(user, role):
144 """Remove user from role."""
145 user, role = _datastore._prepare_role_modify_args(user, role)
146 if user is None:
147 raise click.UsageError("Cannot find user.")
148 if role is None:
149 raise click.UsageError("Cannot find role.")
150 if _datastore.remove_role_from_user(user, role):
151 click.secho(
152 'Role "{0}" removed from user "{1}" ' "successfully.".format(role, user),
153 fg="green",
154 )
155 else:
156 raise click.UsageError("Cannot remove role from user.")
157
158
159 @users.command("activate")
160 @click.argument("user")
161 @with_appcontext
162 @commit
163 def users_activate(user):
164 """Activate a user."""
165 user_obj = _datastore.get_user(user)
166 if user_obj is None:
167 raise click.UsageError("ERROR: User not found.")
168 if _datastore.activate_user(user_obj):
169 click.secho('User "{0}" has been activated.'.format(user), fg="green")
170 else:
171 click.secho('User "{0}" was already activated.'.format(user), fg="yellow")
172
173
174 @users.command("deactivate")
175 @click.argument("user")
176 @with_appcontext
177 @commit
178 def users_deactivate(user):
179 """Deactivate a user."""
180 user_obj = _datastore.get_user(user)
181 if user_obj is None:
182 raise click.UsageError("ERROR: User not found.")
183 if _datastore.deactivate_user(user_obj):
184 click.secho('User "{0}" has been deactivated.'.format(user), fg="green")
185 else:
186 click.secho('User "{0}" was already deactivated.'.format(user), fg="yellow")
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.confirmable
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security confirmable module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2017 by CERN.
9 :license: MIT, see LICENSE for more details.
10 """
11
12 from flask import current_app as app
13 from werkzeug.local import LocalProxy
14
15 from .signals import confirm_instructions_sent, user_confirmed
16 from .utils import (
17 config_value,
18 get_token_status,
19 hash_data,
20 url_for_security,
21 verify_hash,
22 )
23
24 # Convenient references
25 _security = LocalProxy(lambda: app.extensions["security"])
26
27 _datastore = LocalProxy(lambda: _security.datastore)
28
29
30 def generate_confirmation_link(user):
31 token = generate_confirmation_token(user)
32 return url_for_security("confirm_email", token=token, _external=True), token
33
34
35 def send_confirmation_instructions(user):
36 """Sends the confirmation instructions email for the specified user.
37
38 :param user: The user to send the instructions to
39 """
40
41 confirmation_link, token = generate_confirmation_link(user)
42
43 _security._send_mail(
44 config_value("EMAIL_SUBJECT_CONFIRM"),
45 user.email,
46 "confirmation_instructions",
47 user=user,
48 confirmation_link=confirmation_link,
49 )
50
51 confirm_instructions_sent.send(app._get_current_object(), user=user, token=token)
52
53
54 def generate_confirmation_token(user):
55 """Generates a unique confirmation token for the specified user.
56
57 :param user: The user to work with
58 """
59 data = [str(user.id), hash_data(user.email)]
60 return _security.confirm_serializer.dumps(data)
61
62
63 def requires_confirmation(user):
64 """Returns `True` if the user requires confirmation."""
65 return (
66 _security.confirmable
67 and not _security.login_without_confirmation
68 and user.confirmed_at is None
69 )
70
71
72 def confirm_email_token_status(token):
73 """Returns the expired status, invalid status, and user of a confirmation
74 token. For example::
75
76 expired, invalid, user = confirm_email_token_status('...')
77
78 :param token: The confirmation token
79 """
80 expired, invalid, user, token_data = get_token_status(
81 token, "confirm", "CONFIRM_EMAIL", return_data=True
82 )
83 if not invalid and user:
84 user_id, token_email_hash = token_data
85 invalid = not verify_hash(token_email_hash, user.email)
86 return expired, invalid, user
87
88
89 def confirm_user(user):
90 """Confirms the specified user
91
92 :param user: The user to confirm
93 """
94 if user.confirmed_at is not None:
95 return False
96 user.confirmed_at = _security.datetime_factory()
97 _datastore.put(user)
98 user_confirmed.send(app._get_current_object(), user=user)
99 return True
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.core
3 ~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security core module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2017 by CERN.
9 :copyright: (c) 2017 by ETH Zurich, Swiss Data Science Center.
10 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
11 :license: MIT, see LICENSE for more details.
12 """
13
14 from datetime import datetime, timedelta
15 import warnings
16 import sys
17
18 import pkg_resources
19 from flask import _request_ctx_stack, current_app, render_template
20 from flask_babelex import Domain
21 from flask_login import AnonymousUserMixin, LoginManager
22 from flask_login import UserMixin as BaseUserMixin
23 from flask_login import current_user
24 from flask_principal import Identity, Principal, RoleNeed, UserNeed, identity_loaded
25 from itsdangerous import URLSafeTimedSerializer
26 from passlib.context import CryptContext
27 from werkzeug.datastructures import ImmutableList
28 from werkzeug.local import LocalProxy, Local
29
30 from .decorators import (
31 default_reauthn_handler,
32 default_unauthn_handler,
33 default_unauthz_handler,
34 )
35 from .forms import (
36 ChangePasswordForm,
37 ConfirmRegisterForm,
38 ForgotPasswordForm,
39 LoginForm,
40 PasswordlessLoginForm,
41 RegisterForm,
42 ResetPasswordForm,
43 SendConfirmationForm,
44 TwoFactorVerifyCodeForm,
45 TwoFactorSetupForm,
46 TwoFactorVerifyPasswordForm,
47 TwoFactorRescueForm,
48 VerifyForm,
49 )
50 from .phone_util import PhoneUtil
51 from .twofactor import tf_send_security_token
52 from .unified_signin import (
53 UnifiedSigninForm,
54 UnifiedSigninSetupForm,
55 UnifiedSigninSetupValidateForm,
56 UnifiedVerifyForm,
57 us_send_security_token,
58 )
59 from .totp import Totp
60 from .utils import _
61 from .utils import config_value as cv
62 from .utils import (
63 FsJsonEncoder,
64 FsPermNeed,
65 csrf_cookie_handler,
66 default_want_json,
67 default_password_validator,
68 get_config,
69 get_identity_attributes,
70 get_message,
71 hash_data,
72 localize_callback,
73 send_mail,
74 string_types,
75 uia_email_mapper,
76 uia_phone_mapper,
77 url_for_security,
78 verify_and_update_password,
79 verify_hash,
80 )
81 from .views import create_blueprint, default_render_json
82 from .cache import VerifyHashCache
83
84 # Convenient references
85 _security = LocalProxy(lambda: current_app.extensions["security"])
86 _datastore = LocalProxy(lambda: _security.datastore)
87 local_cache = Local()
88
89 # List of authentication mechanisms supported.
90 AUTHN_MECHANISMS = ("basic", "session", "token")
91
92
93 #: Default Flask-Security configuration
94 _default_config = {
95 "BLUEPRINT_NAME": "security",
96 "CLI_ROLES_NAME": "roles",
97 "CLI_USERS_NAME": "users",
98 "URL_PREFIX": None,
99 "SUBDOMAIN": None,
100 "FLASH_MESSAGES": True,
101 "I18N_DOMAIN": "flask_security",
102 "I18N_DIRNAME": pkg_resources.resource_filename("flask_security", "translations"),
103 "PASSWORD_HASH": "bcrypt",
104 "PASSWORD_SALT": None,
105 "PASSWORD_SINGLE_HASH": {
106 "django_argon2",
107 "django_bcrypt_sha256",
108 "django_pbkdf2_sha256",
109 "django_pbkdf2_sha1",
110 "django_bcrypt",
111 "django_salted_md5",
112 "django_salted_sha1",
113 "django_des_crypt",
114 "plaintext",
115 },
116 "PASSWORD_SCHEMES": [
117 "bcrypt",
118 "argon2",
119 "des_crypt",
120 "pbkdf2_sha256",
121 "pbkdf2_sha512",
122 "sha256_crypt",
123 "sha512_crypt",
124 # And always last one...
125 "plaintext",
126 ],
127 "PASSWORD_HASH_OPTIONS": {}, # Deprecated at passlib 1.7
128 "PASSWORD_HASH_PASSLIB_OPTIONS": {
129 "argon2__rounds": 10 # 1.7.1 default is 2.
130 }, # >= 1.7.1 method to pass options.
131 "PASSWORD_LENGTH_MIN": 8,
132 "PASSWORD_COMPLEXITY_CHECKER": None,
133 "PASSWORD_CHECK_BREACHED": False,
134 "PASSWORD_BREACHED_COUNT": 1,
135 "DEPRECATED_PASSWORD_SCHEMES": ["auto"],
136 "LOGIN_URL": "/login",
137 "LOGOUT_URL": "/logout",
138 "REGISTER_URL": "/register",
139 "RESET_URL": "/reset",
140 "CHANGE_URL": "/change",
141 "CONFIRM_URL": "/confirm",
142 "VERIFY_URL": "/verify",
143 "TWO_FACTOR_SETUP_URL": "/tf-setup",
144 "TWO_FACTOR_TOKEN_VALIDATION_URL": "/tf-validate",
145 "TWO_FACTOR_QRCODE_URL": "/tf-qrcode",
146 "TWO_FACTOR_RESCUE_URL": "/tf-rescue",
147 "TWO_FACTOR_CONFIRM_URL": "/tf-confirm",
148 "LOGOUT_METHODS": ["GET", "POST"],
149 "POST_LOGIN_VIEW": "/",
150 "POST_LOGOUT_VIEW": "/",
151 "CONFIRM_ERROR_VIEW": None,
152 "POST_REGISTER_VIEW": None,
153 "POST_CONFIRM_VIEW": None,
154 "POST_RESET_VIEW": None,
155 "POST_CHANGE_VIEW": None,
156 "POST_VERIFY_VIEW": None,
157 "UNAUTHORIZED_VIEW": None,
158 "RESET_ERROR_VIEW": None,
159 "RESET_VIEW": None,
160 "LOGIN_ERROR_VIEW": None,
161 "REDIRECT_HOST": None,
162 "REDIRECT_BEHAVIOR": None,
163 "FORGOT_PASSWORD_TEMPLATE": "security/forgot_password.html",
164 "LOGIN_USER_TEMPLATE": "security/login_user.html",
165 "REGISTER_USER_TEMPLATE": "security/register_user.html",
166 "RESET_PASSWORD_TEMPLATE": "security/reset_password.html",
167 "CHANGE_PASSWORD_TEMPLATE": "security/change_password.html",
168 "SEND_CONFIRMATION_TEMPLATE": "security/send_confirmation.html",
169 "SEND_LOGIN_TEMPLATE": "security/send_login.html",
170 "VERIFY_TEMPLATE": "security/verify.html",
171 "TWO_FACTOR_VERIFY_CODE_TEMPLATE": "security/two_factor_verify_code.html",
172 "TWO_FACTOR_SETUP_TEMPLATE": "security/two_factor_setup.html",
173 "TWO_FACTOR_VERIFY_PASSWORD_TEMPLATE": "security/two_factor_verify_password.html",
174 "CONFIRMABLE": False,
175 "REGISTERABLE": False,
176 "RECOVERABLE": False,
177 "TRACKABLE": False,
178 "PASSWORDLESS": False,
179 "CHANGEABLE": False,
180 "TWO_FACTOR": False,
181 "SEND_REGISTER_EMAIL": True,
182 "SEND_PASSWORD_CHANGE_EMAIL": True,
183 "SEND_PASSWORD_RESET_EMAIL": True,
184 "SEND_PASSWORD_RESET_NOTICE_EMAIL": True,
185 "LOGIN_WITHIN": "1 days",
186 "TWO_FACTOR_AUTHENTICATOR_VALIDITY": 120,
187 "TWO_FACTOR_MAIL_VALIDITY": 300,
188 "TWO_FACTOR_SMS_VALIDITY": 120,
189 "CONFIRM_EMAIL_WITHIN": "5 days",
190 "RESET_PASSWORD_WITHIN": "5 days",
191 "LOGIN_WITHOUT_CONFIRMATION": False,
192 "AUTO_LOGIN_AFTER_CONFIRM": True,
193 "EMAIL_SENDER": LocalProxy(
194 lambda: current_app.config.get("MAIL_DEFAULT_SENDER", "no-reply@localhost")
195 ),
196 "TWO_FACTOR_RESCUE_MAIL": "no-reply@localhost",
197 "TOKEN_AUTHENTICATION_KEY": "auth_token",
198 "TOKEN_AUTHENTICATION_HEADER": "Authentication-Token",
199 "TOKEN_MAX_AGE": None,
200 "CONFIRM_SALT": "confirm-salt",
201 "RESET_SALT": "reset-salt",
202 "LOGIN_SALT": "login-salt",
203 "CHANGE_SALT": "change-salt",
204 "REMEMBER_SALT": "remember-salt",
205 "DEFAULT_REMEMBER_ME": False,
206 "DEFAULT_HTTP_AUTH_REALM": _("Login Required"),
207 "EMAIL_SUBJECT_REGISTER": _("Welcome"),
208 "EMAIL_SUBJECT_CONFIRM": _("Please confirm your email"),
209 "EMAIL_SUBJECT_PASSWORDLESS": _("Login instructions"),
210 "EMAIL_SUBJECT_PASSWORD_NOTICE": _("Your password has been reset"),
211 "EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE": _("Your password has been changed"),
212 "EMAIL_SUBJECT_PASSWORD_RESET": _("Password reset instructions"),
213 "EMAIL_PLAINTEXT": True,
214 "EMAIL_HTML": True,
215 "EMAIL_SUBJECT_TWO_FACTOR": _("Two-factor Login"),
216 "EMAIL_SUBJECT_TWO_FACTOR_RESCUE": _("Two-factor Rescue"),
217 "USER_IDENTITY_ATTRIBUTES": ["email"],
218 "USER_IDENTITY_MAPPINGS": [
219 {"email": uia_email_mapper},
220 {"us_phone_number": uia_phone_mapper},
221 ],
222 "PHONE_REGION_DEFAULT": "US",
223 "FRESHNESS": timedelta(hours=24),
224 "FRESHNESS_GRACE_PERIOD": timedelta(hours=1),
225 "HASHING_SCHEMES": ["sha256_crypt", "hex_md5"],
226 "DEPRECATED_HASHING_SCHEMES": ["hex_md5"],
227 "DATETIME_FACTORY": datetime.utcnow,
228 "USE_VERIFY_PASSWORD_CACHE": False,
229 "VERIFY_HASH_CACHE_TTL": 60 * 5,
230 "VERIFY_HASH_CACHE_MAX_SIZE": 500,
231 "TOTP_SECRETS": None,
232 "TOTP_ISSUER": None,
233 "SMS_SERVICE": "Dummy",
234 "SMS_SERVICE_CONFIG": {
235 "ACCOUNT_SID": None,
236 "AUTH_TOKEN": None,
237 "PHONE_NUMBER": None,
238 },
239 "TWO_FACTOR_REQUIRED": False,
240 "TWO_FACTOR_SECRET": None, # Deprecated - use TOTP_SECRETS
241 "TWO_FACTOR_ENABLED_METHODS": ["email", "authenticator", "sms"],
242 "TWO_FACTOR_URI_SERVICE_NAME": "service_name", # Deprecated - use TOTP_ISSUER
243 "TWO_FACTOR_SMS_SERVICE": "Dummy", # Deprecated - use SMS_SERVICE
244 "TWO_FACTOR_SMS_SERVICE_CONFIG": { # Deprecated - use SMS_SERVICE_CONFIG
245 "ACCOUNT_SID": None,
246 "AUTH_TOKEN": None,
247 "PHONE_NUMBER": None,
248 },
249 "UNIFIED_SIGNIN": False,
250 "US_SETUP_SALT": "us-setup-salt",
251 "US_SIGNIN_URL": "/us-signin",
252 "US_SIGNIN_SEND_CODE_URL": "/us-signin/send-code",
253 "US_SETUP_URL": "/us-setup",
254 "US_VERIFY_URL": "/us-verify",
255 "US_VERIFY_SEND_CODE_URL": "/us-verify/send-code",
256 "US_VERIFY_LINK_URL": "/us-verify-link",
257 "US_QRCODE_URL": "/us-qrcode",
258 "US_POST_SETUP_VIEW": None,
259 "US_SIGNIN_TEMPLATE": "security/us_signin.html",
260 "US_SETUP_TEMPLATE": "security/us_setup.html",
261 "US_VERIFY_TEMPLATE": "security/us_verify.html",
262 "US_ENABLED_METHODS": ["password", "email", "authenticator", "sms"],
263 "US_MFA_REQUIRED": ["password", "email"],
264 "US_TOKEN_VALIDITY": 120,
265 "US_EMAIL_SUBJECT": _("Verification Code"),
266 "US_SETUP_WITHIN": "30 minutes",
267 "US_SIGNIN_REPLACES_LOGIN": False,
268 "CSRF_PROTECT_MECHANISMS": AUTHN_MECHANISMS,
269 "CSRF_IGNORE_UNAUTH_ENDPOINTS": False,
270 "CSRF_COOKIE": {"key": None},
271 "CSRF_HEADER": "X-XSRF-Token",
272 "CSRF_COOKIE_REFRESH_EACH_REQUEST": False,
273 "BACKWARDS_COMPAT_UNAUTHN": False,
274 "BACKWARDS_COMPAT_AUTH_TOKEN": False,
275 "BACKWARDS_COMPAT_AUTH_TOKEN_INVALIDATE": False,
276 "JOIN_USER_ROLES": True,
277 }
278
279 #: Default Flask-Security messages
280 _default_messages = {
281 "API_ERROR": (_("Input not appropriate for requested API"), "error"),
282 "UNAUTHORIZED": (_("You do not have permission to view this resource."), "error"),
283 "UNAUTHENTICATED": (
284 _("You are not authenticated. Please supply the correct credentials."),
285 "error",
286 ),
287 "REAUTHENTICATION_REQUIRED": (
288 _("You must re-authenticate to access this endpoint"),
289 "error",
290 ),
291 "CONFIRM_REGISTRATION": (
292 _("Thank you. Confirmation instructions have been sent to %(email)s."),
293 "success",
294 ),
295 "EMAIL_CONFIRMED": (_("Thank you. Your email has been confirmed."), "success"),
296 "ALREADY_CONFIRMED": (_("Your email has already been confirmed."), "info"),
297 "INVALID_CONFIRMATION_TOKEN": (_("Invalid confirmation token."), "error"),
298 "EMAIL_ALREADY_ASSOCIATED": (
299 _("%(email)s is already associated with an account."),
300 "error",
301 ),
302 "PASSWORD_MISMATCH": (_("Password does not match"), "error"),
303 "RETYPE_PASSWORD_MISMATCH": (_("Passwords do not match"), "error"),
304 "INVALID_REDIRECT": (_("Redirections outside the domain are forbidden"), "error"),
305 "PASSWORD_RESET_REQUEST": (
306 _("Instructions to reset your password have been sent to %(email)s."),
307 "info",
308 ),
309 "PASSWORD_RESET_EXPIRED": (
310 _(
311 "You did not reset your password within %(within)s. "
312 "New instructions have been sent to %(email)s."
313 ),
314 "error",
315 ),
316 "INVALID_RESET_PASSWORD_TOKEN": (_("Invalid reset password token."), "error"),
317 "CONFIRMATION_REQUIRED": (_("Email requires confirmation."), "error"),
318 "CONFIRMATION_REQUEST": (
319 _("Confirmation instructions have been sent to %(email)s."),
320 "info",
321 ),
322 "CONFIRMATION_EXPIRED": (
323 _(
324 "You did not confirm your email within %(within)s. "
325 "New instructions to confirm your email have been sent "
326 "to %(email)s."
327 ),
328 "error",
329 ),
330 "LOGIN_EXPIRED": (
331 _(
332 "You did not login within %(within)s. New instructions to login "
333 "have been sent to %(email)s."
334 ),
335 "error",
336 ),
337 "LOGIN_EMAIL_SENT": (
338 _("Instructions to login have been sent to %(email)s."),
339 "success",
340 ),
341 "INVALID_LOGIN_TOKEN": (_("Invalid login token."), "error"),
342 "DISABLED_ACCOUNT": (_("Account is disabled."), "error"),
343 "EMAIL_NOT_PROVIDED": (_("Email not provided"), "error"),
344 "INVALID_EMAIL_ADDRESS": (_("Invalid email address"), "error"),
345 "INVALID_CODE": (_("Invalid code"), "error"),
346 "PASSWORD_NOT_PROVIDED": (_("Password not provided"), "error"),
347 "PASSWORD_NOT_SET": (_("No password is set for this user"), "error"),
348 "PASSWORD_INVALID_LENGTH": (
349 _("Password must be at least %(length)s characters"),
350 "error",
351 ),
352 "PASSWORD_TOO_SIMPLE": (_("Password not complex enough"), "error"),
353 "PASSWORD_BREACHED": (_("Password on breached list"), "error"),
354 "PASSWORD_BREACHED_SITE_ERROR": (
355 _("Failed to contact breached passwords site"),
356 "error",
357 ),
358 "PHONE_INVALID": (_("Phone number not valid e.g. missing country code"), "error"),
359 "USER_DOES_NOT_EXIST": (_("Specified user does not exist"), "error"),
360 "INVALID_PASSWORD": (_("Invalid password"), "error"),
361 "INVALID_PASSWORD_CODE": (_("Password or code submitted is not valid"), "error"),
362 "PASSWORDLESS_LOGIN_SUCCESSFUL": (_("You have successfully logged in."), "success"),
363 "FORGOT_PASSWORD": (_("Forgot password?"), "info"),
364 "PASSWORD_RESET": (
365 _(
366 "You successfully reset your password and you have been logged in "
367 "automatically."
368 ),
369 "success",
370 ),
371 "PASSWORD_IS_THE_SAME": (
372 _("Your new password must be different than your previous password."),
373 "error",
374 ),
375 "PASSWORD_CHANGE": (_("You successfully changed your password."), "success"),
376 "LOGIN": (_("Please log in to access this page."), "info"),
377 "REFRESH": (_("Please reauthenticate to access this page."), "info"),
378 "REAUTHENTICATION_SUCCESSFUL": (_("Reauthentication successful"), "info"),
379 "ANONYMOUS_USER_REQUIRED": (
380 _("You can only access this endpoint when not logged in."),
381 "error",
382 ),
383 "FAILED_TO_SEND_CODE": (_("Failed to send code. Please try again later"), "error"),
384 "TWO_FACTOR_INVALID_TOKEN": (_("Invalid Token"), "error"),
385 "TWO_FACTOR_LOGIN_SUCCESSFUL": (_("Your token has been confirmed"), "success"),
386 "TWO_FACTOR_CHANGE_METHOD_SUCCESSFUL": (
387 _("You successfully changed your two-factor method."),
388 "success",
389 ),
390 "TWO_FACTOR_PASSWORD_CONFIRMATION_DONE": (
391 _("You successfully confirmed password"),
392 "success",
393 ),
394 "TWO_FACTOR_PASSWORD_CONFIRMATION_NEEDED": (
395 _("Password confirmation is needed in order to access page"),
396 "error",
397 ),
398 "TWO_FACTOR_PERMISSION_DENIED": (
399 _("You currently do not have permissions to access this page"),
400 "error",
401 ),
402 "TWO_FACTOR_METHOD_NOT_AVAILABLE": (_("Marked method is not valid"), "error"),
403 "TWO_FACTOR_DISABLED": (
404 _("You successfully disabled two factor authorization."),
405 "success",
406 ),
407 "US_METHOD_NOT_AVAILABLE": (_("Requested method is not valid"), "error"),
408 "US_SETUP_EXPIRED": (
409 _("Setup must be completed within %(within)s. Please start over."),
410 "error",
411 ),
412 "US_SETUP_SUCCESSFUL": (_("Unified sign in setup successful"), "info"),
413 "US_SPECIFY_IDENTITY": (_("You must specify a valid identity to sign in"), "error"),
414 "USE_CODE": (_("Use this code to sign in: %(code)s."), "info"),
415 }
416
417 _default_forms = {
418 "login_form": LoginForm,
419 "verify_form": VerifyForm,
420 "confirm_register_form": ConfirmRegisterForm,
421 "register_form": RegisterForm,
422 "forgot_password_form": ForgotPasswordForm,
423 "reset_password_form": ResetPasswordForm,
424 "change_password_form": ChangePasswordForm,
425 "send_confirmation_form": SendConfirmationForm,
426 "passwordless_login_form": PasswordlessLoginForm,
427 "two_factor_verify_code_form": TwoFactorVerifyCodeForm,
428 "two_factor_setup_form": TwoFactorSetupForm,
429 "two_factor_verify_password_form": TwoFactorVerifyPasswordForm,
430 "two_factor_rescue_form": TwoFactorRescueForm,
431 "us_signin_form": UnifiedSigninForm,
432 "us_setup_form": UnifiedSigninSetupForm,
433 "us_setup_validate_form": UnifiedSigninSetupValidateForm,
434 "us_verify_form": UnifiedVerifyForm,
435 }
436
437
438 def _user_loader(user_id):
439 """ Try to load based on fs_uniquifier (alternative_id) if available.
440
441 Note that we don't try, and fall back to the other - primarily because some DBs
442 and drivers (psycopg2) really really hate getting mismatched types during queries.
443 They hate it enough that they abort the 'transaction' and refuse to do anything
444 in the future until the transaction is rolled-back. But we don't really control
445 that and there doesn't seem to be any way to catch the actual offensive query -
446 just next time and forever, things fail.
447 This assumes that if the app has fs_uniquifier, it is non-nullable as we specify
448 so we use that and only that.
449 """
450 if hasattr(_datastore.user_model, "fs_uniquifier"):
451 selector = dict(fs_uniquifier=str(user_id))
452 else:
453 selector = dict(id=user_id)
454 user = _security.datastore.find_user(**selector)
455 if user and user.active:
456 return user
457 return None
458
459
460 def _request_loader(request):
461 # Short-circuit if we have already been called and verified.
462 # This can happen since Flask-Login will call us (if no session) and our own
463 # decorator @auth_token_required can call us.
464 # N.B. we don't call current_user here since that in fact might try and LOAD
465 # a user - which would call us again.
466 if all(hasattr(_request_ctx_stack.top, k) for k in ["fs_authn_via", "user"]):
467 if _request_ctx_stack.top.fs_authn_via == "token":
468 return _request_ctx_stack.top.user
469
470 header_key = _security.token_authentication_header
471 args_key = _security.token_authentication_key
472 header_token = request.headers.get(header_key, None)
473 token = request.args.get(args_key, header_token)
474 if request.is_json:
475 data = request.get_json(silent=True) or {}
476 if isinstance(data, dict):
477 token = data.get(args_key, token)
478
479 use_cache = cv("USE_VERIFY_PASSWORD_CACHE")
480
481 try:
482 data = _security.remember_token_serializer.loads(
483 token, max_age=_security.token_max_age
484 )
485 user = _security.datastore.find_user(id=data[0])
486 if not user.active:
487 user = None
488 except Exception:
489 user = None
490
491 if not user:
492 return _security.login_manager.anonymous_user()
493 if use_cache:
494 cache = getattr(local_cache, "verify_hash_cache", None)
495 if cache is None:
496 cache = VerifyHashCache()
497 local_cache.verify_hash_cache = cache
498 if cache.has_verify_hash_cache(user):
499 _request_ctx_stack.top.fs_authn_via = "token"
500 return user
501 if user.verify_auth_token(data):
502 _request_ctx_stack.top.fs_authn_via = "token"
503 cache.set_cache(user)
504 return user
505 else:
506 if user.verify_auth_token(data):
507 _request_ctx_stack.top.fs_authn_via = "token"
508 return user
509
510 return _security.login_manager.anonymous_user()
511
512
513 def _identity_loader():
514 if not isinstance(current_user._get_current_object(), AnonymousUserMixin):
515 identity = Identity(current_user.id)
516 return identity
517
518
519 def _on_identity_loaded(sender, identity):
520 if hasattr(current_user, "id"):
521 identity.provides.add(UserNeed(current_user.id))
522
523 for role in getattr(current_user, "roles", []):
524 identity.provides.add(RoleNeed(role.name))
525 for fsperm in role.get_permissions():
526 identity.provides.add(FsPermNeed(fsperm))
527
528 identity.user = current_user
529
530
531 def _get_login_manager(app, anonymous_user):
532 lm = LoginManager()
533 lm.anonymous_user = anonymous_user or AnonymousUser
534 lm.localize_callback = localize_callback
535 lm.login_view = "%s.login" % cv("BLUEPRINT_NAME", app=app)
536 lm.user_loader(_user_loader)
537 lm.request_loader(_request_loader)
538
539 if cv("FLASH_MESSAGES", app=app):
540 lm.login_message, lm.login_message_category = cv("MSG_LOGIN", app=app)
541 lm.needs_refresh_message, lm.needs_refresh_message_category = cv(
542 "MSG_REFRESH", app=app
543 )
544 else:
545 lm.login_message = None
546 lm.needs_refresh_message = None
547
548 lm.init_app(app)
549 return lm
550
551
552 def _get_principal(app):
553 p = Principal(app, use_sessions=False)
554 p.identity_loader(_identity_loader)
555 return p
556
557
558 def _get_pwd_context(app):
559 pw_hash = cv("PASSWORD_HASH", app=app)
560 schemes = cv("PASSWORD_SCHEMES", app=app)
561 deprecated = cv("DEPRECATED_PASSWORD_SCHEMES", app=app)
562 if pw_hash not in schemes:
563 allowed = ", ".join(schemes[:-1]) + " and " + schemes[-1]
564 raise ValueError(
565 "Invalid password hashing scheme %r. Allowed values are %s"
566 % (pw_hash, allowed)
567 )
568 cc = CryptContext(
569 schemes=schemes,
570 default=pw_hash,
571 deprecated=deprecated,
572 **cv("PASSWORD_HASH_PASSLIB_OPTIONS", app=app)
573 )
574 return cc
575
576
577 def _get_i18n_domain(app):
578 return Domain(
579 dirname=cv("I18N_DIRNAME", app=app), domain=cv("I18N_DOMAIN", app=app)
580 )
581
582
583 def _get_hashing_context(app):
584 schemes = cv("HASHING_SCHEMES", app=app)
585 deprecated = cv("DEPRECATED_HASHING_SCHEMES", app=app)
586 return CryptContext(schemes=schemes, deprecated=deprecated)
587
588
589 def _get_serializer(app, name):
590 secret_key = app.config.get("SECRET_KEY")
591 salt = app.config.get("SECURITY_%s_SALT" % name.upper())
592 return URLSafeTimedSerializer(secret_key=secret_key, salt=salt)
593
594
595 def _get_state(app, datastore, anonymous_user=None, **kwargs):
596 for key, value in get_config(app).items():
597 kwargs[key.lower()] = value
598
599 kwargs.update(
600 dict(
601 app=app,
602 datastore=datastore,
603 principal=_get_principal(app),
604 pwd_context=_get_pwd_context(app),
605 hashing_context=_get_hashing_context(app),
606 i18n_domain=_get_i18n_domain(app),
607 remember_token_serializer=_get_serializer(app, "remember"),
608 login_serializer=_get_serializer(app, "login"),
609 reset_serializer=_get_serializer(app, "reset"),
610 confirm_serializer=_get_serializer(app, "confirm"),
611 us_setup_serializer=_get_serializer(app, "us_setup"),
612 _context_processors={},
613 _send_mail_task=None,
614 _send_mail=kwargs.get("send_mail", send_mail),
615 _unauthorized_callback=None,
616 _render_json=default_render_json,
617 _want_json=default_want_json,
618 _unauthn_handler=default_unauthn_handler,
619 _reauthn_handler=default_reauthn_handler,
620 _unauthz_handler=default_unauthz_handler,
621 _password_validator=default_password_validator,
622 )
623 )
624
625 if "login_manager" not in kwargs:
626 kwargs["login_manager"] = _get_login_manager(app, anonymous_user)
627
628 for key, value in _default_forms.items():
629 if key not in kwargs or not kwargs[key]:
630 kwargs[key] = value
631
632 return _SecurityState(**kwargs)
633
634
635 def _context_processor():
636 return dict(url_for_security=url_for_security, security=_security)
637
638
639 class RoleMixin(object):
640 """Mixin for `Role` model definitions"""
641
642 def __eq__(self, other):
643 return self.name == other or self.name == getattr(other, "name", None)
644
645 def __ne__(self, other):
646 return not self.__eq__(other)
647
648 def __hash__(self):
649 return hash(self.name)
650
651 def get_permissions(self):
652 """
653 Return set of permissions associated with role.
654
655 Either takes a comma separated string of permissions or
656 an interable of strings if permissions are in their own
657 table.
658
659 .. versionadded:: 3.3.0
660 """
661 if hasattr(self, "permissions") and self.permissions:
662 if isinstance(self.permissions, set):
663 return self.permissions
664 elif isinstance(self.permissions, list):
665 return set(self.permissions)
666 else:
667 # Assume this is a comma separated list
668 return set(self.permissions.split(","))
669 return set([])
670
671 def add_permissions(self, permissions):
672 """
673 Add one or more permissions to role.
674
675 :param permissions: a set, list, or single string.
676
677 Caller must commit to DB.
678
679 .. versionadded:: 3.3.0
680 """
681 if hasattr(self, "permissions"):
682 current_perms = self.get_permissions()
683 if isinstance(permissions, set):
684 perms = permissions
685 elif isinstance(permissions, list):
686 perms = set(permissions)
687 else:
688 perms = {permissions}
689 self.permissions = ",".join(current_perms.union(perms))
690 else:
691 raise NotImplementedError("Role model doesn't have permissions")
692
693 def remove_permissions(self, permissions):
694 """
695 Remove one or more permissions from role.
696
697 :param permissions: a set, list, or single string.
698
699 Caller must commit to DB.
700
701 .. versionadded:: 3.3.0
702 """
703 if hasattr(self, "permissions"):
704 current_perms = self.get_permissions()
705 if isinstance(permissions, set):
706 perms = permissions
707 elif isinstance(permissions, list):
708 perms = set(permissions)
709 else:
710 perms = {permissions}
711 self.permissions = ",".join(current_perms.difference(perms))
712 else:
713 raise NotImplementedError("Role model doesn't have permissions")
714
715
716 class UserMixin(BaseUserMixin):
717 """Mixin for `User` model definitions"""
718
719 def get_id(self):
720 """Returns the user identification attribute.
721
722 This will be `fs_uniquifier` if that is available, else base class id
723 (which is via Flask-Login and is user.id).
724
725 .. versionadded:: 3.4.0
726 """
727 if hasattr(self, "fs_uniquifier") and self.fs_uniquifier is not None:
728 # Use fs_uniquifier as alternative_id if available and not None
729 alternative_id = str(self.fs_uniquifier)
730 if len(alternative_id) > 0:
731 # Return only if alternative_id is a valid value
732 return alternative_id
733
734 # Use upstream value if alternative_id is unavailable
735 return BaseUserMixin.get_id(self)
736
737 @property
738 def is_active(self):
739 """Returns `True` if the user is active."""
740 return self.active
741
742 def get_auth_token(self):
743 """Constructs the user's authentication token.
744
745 This data MUST be securely signed using the ``remember_token_serializer``
746 """
747 data = [str(self.id), hash_data(self.password)]
748 if hasattr(self, "fs_uniquifier"):
749 data.append(self.fs_uniquifier)
750 return _security.remember_token_serializer.dumps(data)
751
752 def verify_auth_token(self, data):
753 """
754 Perform additional verification of contents of auth token.
755 Prior to this being called the token has been validated (via signing)
756 and has not expired.
757
758 :param data: the data as formulated by :meth:`get_auth_token`
759
760 .. versionadded:: 3.3.0
761 """
762 if len(data) > 2 and hasattr(self, "fs_uniquifier"):
763 # has uniquifier - use that
764 if data[2] == self.fs_uniquifier:
765 return True
766 # Don't even try old way - if they have defined a uniquifier
767 # we want that to be able to invalidate tokens if changed.
768 return False
769 # Fall back to old and very expensive check
770 if verify_hash(data[1], self.password):
771 return True
772 return False
773
774 def has_role(self, role):
775 """Returns `True` if the user identifies with the specified role.
776
777 :param role: A role name or `Role` instance"""
778 if isinstance(role, string_types):
779 return role in (role.name for role in self.roles)
780 else:
781 return role in self.roles
782
783 def has_permission(self, permission):
784 """
785 Returns `True` if user has this permission (via a role it has).
786
787 :param permission: permission string name
788
789 .. versionadded:: 3.3.0
790
791 """
792 for role in self.roles:
793 if hasattr(role, "permissions"):
794 if permission in role.get_permissions():
795 return True
796 return False
797
798 def get_security_payload(self):
799 """Serialize user object as response payload."""
800 return {"id": str(self.id)}
801
802 def get_redirect_qparams(self, existing=None):
803 """Return user info that will be added to redirect query params.
804
805 :param existing: A dict that will be updated.
806 :return: A dict whose keys will be query params and values will be query values.
807
808 .. versionadded:: 3.2.0
809 """
810 if not existing:
811 existing = {}
812 existing.update({"email": self.email})
813 return existing
814
815 def verify_and_update_password(self, password):
816 """Returns ``True`` if the password is valid for the specified user.
817
818 Additionally, the hashed password in the database is updated if the
819 hashing algorithm happens to have changed.
820
821 N.B. you MUST call DB commit if you are using a session-based datastore
822 (such as SqlAlchemy) since the user instance might have been altered
823 (i.e. ``app.security.datastore.commit()``).
824 This is usually handled in the view.
825
826 :param password: A plaintext password to verify
827
828 .. versionadded:: 3.2.0
829 """
830 return verify_and_update_password(password, self)
831
832 def calc_username(self):
833 """ Come up with the best 'username' based on how the app
834 is configured (via :py:data:`SECURITY_USER_IDENTITY_ATTRIBUTES`).
835 Returns the first non-null match (and converts to string).
836 In theory this should NEVER be the empty string unless the user
837 record isn't actually valid.
838
839 .. versionadded:: 3.4.0
840 """
841 cusername = None
842 for attr in get_identity_attributes():
843 cusername = getattr(self, attr, None)
844 if cusername is not None and len(str(cusername)) > 0:
845 break
846 return str(cusername) if cusername is not None else ""
847
848 def us_send_security_token(self, method, **kwargs):
849 """ Generate and send the security code for unified sign in.
850
851 :param method: The method in which the code will be sent
852 :param kwargs: Opaque parameters that are subject to change at any time
853 :return: None if successful, error message if not.
854
855 This is a wrapper around :meth:`us_send_security_token`
856 that can be overridden to manage any errors.
857
858 .. versionadded:: 3.4.0
859 """
860 try:
861 us_send_security_token(self, method, **kwargs)
862 except Exception:
863 return get_message("FAILED_TO_SEND_CODE")[0]
864 return None
865
866 def tf_send_security_token(self, method, **kwargs):
867 """ Generate and send the security code for two-factor.
868
869 :param method: The method in which the code will be sent
870 :param kwargs: Opaque parameters that are subject to change at any time
871 :return: None if successful, error message if not.
872
873 This is a wrapper around :meth:`tf_send_security_token`
874 that can be overridden to manage any errors.
875
876 .. versionadded:: 3.4.0
877 """
878 try:
879 tf_send_security_token(self, method, **kwargs)
880 except Exception:
881 return get_message("FAILED_TO_SEND_CODE")[0]
882 return None
883
884
885 class AnonymousUser(AnonymousUserMixin):
886 """AnonymousUser definition"""
887
888 def __init__(self):
889 self.roles = ImmutableList()
890
891 def has_role(self, *args):
892 """Returns `False`"""
893 return False
894
895
896 class _SecurityState(object):
897 def __init__(self, **kwargs):
898 for key, value in kwargs.items():
899 setattr(self, key.lower(), value)
900
901 def _add_ctx_processor(self, endpoint, fn):
902 group = self._context_processors.setdefault(endpoint, [])
903 fn not in group and group.append(fn)
904
905 def _run_ctx_processor(self, endpoint):
906 rv = {}
907 for g in [None, endpoint]:
908 for fn in self._context_processors.setdefault(g, []):
909 rv.update(fn())
910 return rv
911
912 def context_processor(self, fn):
913 self._add_ctx_processor(None, fn)
914
915 def forgot_password_context_processor(self, fn):
916 self._add_ctx_processor("forgot_password", fn)
917
918 def login_context_processor(self, fn):
919 self._add_ctx_processor("login", fn)
920
921 def register_context_processor(self, fn):
922 self._add_ctx_processor("register", fn)
923
924 def reset_password_context_processor(self, fn):
925 self._add_ctx_processor("reset_password", fn)
926
927 def change_password_context_processor(self, fn):
928 self._add_ctx_processor("change_password", fn)
929
930 def send_confirmation_context_processor(self, fn):
931 self._add_ctx_processor("send_confirmation", fn)
932
933 def send_login_context_processor(self, fn):
934 self._add_ctx_processor("send_login", fn)
935
936 def verify_context_processor(self, fn):
937 self._add_ctx_processor("verify", fn)
938
939 def mail_context_processor(self, fn):
940 self._add_ctx_processor("mail", fn)
941
942 def tf_verify_password_context_processor(self, fn):
943 self._add_ctx_processor("tf_verify_password", fn)
944
945 def tf_setup_context_processor(self, fn):
946 self._add_ctx_processor("tf_setup", fn)
947
948 def tf_token_validation_context_processor(self, fn):
949 self._add_ctx_processor("tf_token_validation", fn)
950
951 def us_signin_context_processor(self, fn):
952 self._add_ctx_processor("us_signin", fn)
953
954 def us_setup_context_processor(self, fn):
955 self._add_ctx_processor("us_setup", fn)
956
957 def us_verify_context_processor(self, fn):
958 self._add_ctx_processor("us_verify", fn)
959
960 def send_mail_task(self, fn):
961 self._send_mail_task = fn
962
963 def send_mail(self, fn):
964 self._send_mail = fn
965
966 def unauthorized_handler(self, fn):
967 warnings.warn(
968 "'unauthorized_handler' has been replaced with"
969 " 'unauthz_handler' and 'unauthn_handler'",
970 DeprecationWarning,
971 )
972 self._unauthorized_callback = fn
973
974 def totp_factory(self, tf):
975 self._totp_factory = tf
976
977 def render_json(self, fn):
978 self._render_json = fn
979
980 def want_json(self, fn):
981 self._want_json = fn
982
983 def unauthz_handler(self, cb):
984 self._unauthz_handler = cb
985
986 def unauthn_handler(self, cb):
987 self._unauthn_handler = cb
988
989 def reauthn_handler(self, cb):
990 self._reauthn_handler = cb
991
992 def password_validator(self, cb):
993 self._password_validator = cb
994
995
996 class Security(object):
997 """The :class:`Security` class initializes the Flask-Security extension.
998
999 :param app: The application.
1000 :param datastore: An instance of a user datastore.
1001 :param register_blueprint: to register the Security blueprint or not.
1002 :param login_form: set form for the login view
1003 :param verify_form: set form for re-authentication due to freshness check
1004 :param register_form: set form for the register view when
1005 *SECURITY_CONFIRMABLE* is false
1006 :param confirm_register_form: set form for the register view when
1007 *SECURITY_CONFIRMABLE* is true
1008 :param forgot_password_form: set form for the forgot password view
1009 :param reset_password_form: set form for the reset password view
1010 :param change_password_form: set form for the change password view
1011 :param send_confirmation_form: set form for the send confirmation view
1012 :param passwordless_login_form: set form for the passwordless login view
1013 :param two_factor_setup_form: set form for the 2FA setup view
1014 :param two_factor_verify_code_form: set form the the 2FA verify code view
1015 :param two_factor_rescue_form: set form for the 2FA rescue view
1016 :param two_factor_verify_password_form: set form for the 2FA verify password view
1017 :param us_signin_form: set form for the unified sign in view
1018 :param us_setup_form: set form for the unified sign in setup view
1019 :param us_setup_validate_form: set form for the unified sign in setup validate view
1020 :param us_verify_form: set form for re-authenticating due to freshness check
1021 :param anonymous_user: class to use for anonymous user
1022 :param render_template: function to use to render templates. The default is Flask's
1023 render_template() function.
1024 :param send_mail: function to use to send email. Defaults to :func:`send_mail`
1025 :param json_encoder_cls: Class to use as blueprint.json_encoder.
1026 Defaults to :class:`FsJsonEncoder`
1027 :param totp_cls: Class to use as TOTP factory. Defaults to :class:`Totp`
1028 :param phone_util_cls: Class to use for phone number utilities.
1029 Defaults to :class:`PhoneUtil`
1030
1031 .. versionadded:: 3.4.0
1032 ``verify_form`` added as part of freshness/re-authentication
1033
1034 .. versionadded:: 3.4.0
1035 ``us_signin_form``, ``us_setup_form``, ``us_setup_validate_form``, and
1036 ``us_verify_form`` added as part of the :ref:`unified-sign-in` feature.
1037
1038 .. versionadded:: 3.4.0
1039 ``totp_cls`` added to enable applications to implement replay protection - see
1040 :py:class:`Totp`.
1041
1042 .. versionadded:: 3.4.0
1043 ``phone_util_cls`` added to allow different phone number
1044 parsing implementations - see :py:class:`PhoneUtil`
1045 """
1046
1047 def __init__(self, app=None, datastore=None, register_blueprint=True, **kwargs):
1048
1049 self.app = app
1050 self._datastore = datastore
1051 self._register_blueprint = register_blueprint
1052 self._kwargs = kwargs
1053
1054 self._state = None # set by init_app
1055 if app is not None and datastore is not None:
1056 self._state = self.init_app(
1057 app, datastore, register_blueprint=register_blueprint, **kwargs
1058 )
1059
1060 def init_app(self, app, datastore=None, register_blueprint=None, **kwargs):
1061 """Initializes the Flask-Security extension for the specified
1062 application and datastore implementation.
1063
1064 :param app: The application.
1065 :param datastore: An instance of a user datastore.
1066 :param register_blueprint: to register the Security blueprint or not.
1067 """
1068 self.app = app
1069
1070 if datastore is None:
1071 datastore = self._datastore
1072
1073 if register_blueprint is None:
1074 register_blueprint = self._register_blueprint
1075
1076 for key, value in self._kwargs.items():
1077 kwargs.setdefault(key, value)
1078
1079 if "render_template" not in kwargs:
1080 kwargs.setdefault("render_template", self.render_template)
1081 if "json_encoder_cls" not in kwargs:
1082 kwargs.setdefault("json_encoder_cls", FsJsonEncoder)
1083 if "totp_cls" not in kwargs:
1084 kwargs.setdefault("totp_cls", Totp)
1085 if "phone_util_cls" not in kwargs:
1086 kwargs.setdefault("phone_util_cls", PhoneUtil)
1087
1088 for key, value in _default_config.items():
1089 app.config.setdefault("SECURITY_" + key, value)
1090
1091 for key, value in _default_messages.items():
1092 app.config.setdefault("SECURITY_MSG_" + key, value)
1093
1094 identity_loaded.connect_via(app)(_on_identity_loaded)
1095
1096 self._state = state = _get_state(app, datastore, **kwargs)
1097
1098 if register_blueprint:
1099 bp = create_blueprint(
1100 app, state, __name__, json_encoder=kwargs["json_encoder_cls"]
1101 )
1102 app.register_blueprint(bp)
1103 app.context_processor(_context_processor)
1104
1105 @app.before_first_request
1106 def _register_i18n():
1107 # N.B. as of jinja 2.9 '_' is always registered
1108 # http://jinja.pocoo.org/docs/2.10/extensions/#i18n-extension
1109 if "_" not in app.jinja_env.globals:
1110 current_app.jinja_env.globals["_"] = state.i18n_domain.gettext
1111 # Register so other packages can reference our translations.
1112 current_app.jinja_env.globals["_fsdomain"] = state.i18n_domain.gettext
1113
1114 @app.before_first_request
1115 def _csrf_init():
1116 # various config checks - some of these are opinionated in that there
1117 # could be a reason for some of these combinations - but in general
1118 # they cause strange behavior.
1119 # WTF_CSRF_ENABLED defaults to True if not set in Flask-WTF
1120 if not current_app.config.get("WTF_CSRF_ENABLED", True):
1121 return
1122 csrf = current_app.extensions.get("csrf", None)
1123
1124 # If they don't want ALL mechanisms protected, then they must
1125 # set WTF_CSRF_CHECK_DEFAULT=False so that our decorators get control.
1126 if cv("CSRF_PROTECT_MECHANISMS") != AUTHN_MECHANISMS:
1127 if not csrf:
1128 # This isn't good.
1129 raise ValueError(
1130 "CSRF_PROTECT_MECHANISMS defined but"
1131 " CsrfProtect not part of application"
1132 )
1133 if current_app.config.get("WTF_CSRF_CHECK_DEFAULT", True):
1134 raise ValueError(
1135 "WTF_CSRF_CHECK_DEFAULT must be set to False if"
1136 " CSRF_PROTECT_MECHANISMS is set"
1137 )
1138 # We don't get control unless they turn off WTF_CSRF_CHECK_DEFAULT if
1139 # they have enabled global CSRFProtect.
1140 if (
1141 cv("CSRF_IGNORE_UNAUTH_ENDPOINTS")
1142 and csrf
1143 and current_app.config.get("WTF_CSRF_CHECK_DEFAULT", False)
1144 ):
1145 raise ValueError(
1146 "To ignore unauth endpoints you must set WTF_CSRF_CHECK_DEFAULT"
1147 " to False"
1148 )
1149
1150 csrf_cookie = cv("CSRF_COOKIE")
1151 if csrf_cookie and csrf_cookie["key"] and not csrf:
1152 # Common use case is for cookie value to be used as contents for header
1153 # which is only looked at when CsrfProtect is initialized.
1154 # Yes, this is opinionated - they can always get CSRF token via:
1155 # 'get /login'
1156 raise ValueError(
1157 "CSRF_COOKIE defined however CsrfProtect not part of application"
1158 )
1159
1160 if csrf:
1161 csrf.exempt("flask_security.views.logout")
1162 if csrf_cookie and csrf_cookie["key"]:
1163 current_app.after_request(csrf_cookie_handler)
1164 # Add configured header to WTF_CSRF_HEADERS
1165 current_app.config["WTF_CSRF_HEADERS"].append(cv("CSRF_HEADER"))
1166
1167 @app.before_first_request
1168 def _init_phone_util():
1169 state._phone_util = state.phone_util_cls()
1170
1171 app.extensions["security"] = state
1172
1173 if hasattr(app, "cli"):
1174 from .cli import users, roles
1175
1176 if state.cli_users_name:
1177 app.cli.add_command(users, state.cli_users_name)
1178 if state.cli_roles_name:
1179 app.cli.add_command(roles, state.cli_roles_name)
1180
1181 # Migrate from TWO_FACTOR config to generic config.
1182 for newc, oldc in [
1183 ("SECURITY_SMS_SERVICE", "SECURITY_TWO_FACTOR_SMS_SERVICE"),
1184 ("SECURITY_SMS_SERVICE_CONFIG", "SECURITY_TWO_FACTOR_SMS_SERVICE_CONFIG"),
1185 ("SECURITY_TOTP_SECRETS", "SECURITY_TWO_FACTOR_SECRET"),
1186 ("SECURITY_TOTP_ISSUER", "SECURITY_TWO_FACTOR_URI_SERVICE_NAME"),
1187 ]:
1188 if not app.config.get(newc, None):
1189 app.config[newc] = app.config.get(oldc, None)
1190
1191 # Two factor configuration checks and setup
1192 multi_factor = False
1193 if cv("UNIFIED_SIGNIN", app=app):
1194 multi_factor = True
1195 if len(cv("US_ENABLED_METHODS", app=app)) < 1:
1196 raise ValueError("Must configure some US_ENABLED_METHODS")
1197 if cv("TWO_FACTOR", app=app):
1198 multi_factor = True
1199 if len(cv("TWO_FACTOR_ENABLED_METHODS", app=app)) < 1:
1200 raise ValueError("Must configure some TWO_FACTOR_ENABLED_METHODS")
1201
1202 if multi_factor:
1203 self._check_modules("pyqrcode", "TWO_FACTOR or UNIFIED_SIGNIN")
1204 self._check_modules("cryptography", "TWO_FACTOR or UNIFIED_SIGNIN")
1205
1206 need_sms = (
1207 cv("UNIFIED_SIGNIN", app=app)
1208 and "sms" in cv("US_ENABLED_METHODS", app=app)
1209 ) or (
1210 cv("TWO_FACTOR", app=app)
1211 and "sms" in cv("TWO_FACTOR_ENABLED_METHODS", app=app)
1212 )
1213 if need_sms:
1214 sms_service = cv("SMS_SERVICE", app=app)
1215 if sms_service == "Twilio": # pragma: no cover
1216 self._check_modules("twilio", "SMS")
1217 if state.phone_util_cls == PhoneUtil:
1218 self._check_modules("phonenumbers", "SMS")
1219
1220 secrets = cv("TOTP_SECRETS", app=app)
1221 issuer = cv("TOTP_ISSUER", app=app)
1222 if not secrets or not issuer:
1223 raise ValueError("Both TOTP_SECRETS and TOTP_ISSUER must be set")
1224 state.totp_factory(state.totp_cls(secrets, issuer))
1225
1226 if cv("USE_VERIFY_PASSWORD_CACHE", app=app):
1227 self._check_modules("cachetools", "USE_VERIFY_PASSWORD_CACHE")
1228
1229 if cv("PASSWORD_COMPLEXITY_CHECKER", app=app) == "zxcvbn":
1230 self._check_modules("zxcvbn", "PASSWORD_COMPLEXITY_CHECKER")
1231 return state
1232
1233 def _check_modules(self, module, config_name): # pragma: no cover
1234 PY3 = sys.version_info[0] == 3
1235 if PY3:
1236 from importlib.util import find_spec
1237
1238 module_exists = find_spec(module)
1239
1240 else:
1241 import imp
1242
1243 try:
1244 imp.find_module(module)
1245 module_exists = True
1246 except ImportError:
1247 module_exists = False
1248
1249 if not module_exists:
1250 raise ValueError("{} is required for {}".format(module, config_name))
1251
1252 return module_exists
1253
1254 def render_template(self, *args, **kwargs):
1255 return render_template(*args, **kwargs)
1256
1257 def send_mail(self, fn):
1258 """ Function used to send emails.
1259
1260 :param fn: Function with signature(subject, recipient, template, context)
1261
1262 See :meth:`send_mail` for details.
1263
1264 .. versionadded:: 3.1.0
1265 """
1266 self._state._send_mail = fn
1267
1268 def render_json(self, cb):
1269 """ Callback to render response payload as JSON.
1270
1271 :param cb: Callback function with
1272 signature (payload, code, headers=None, user=None)
1273
1274 :payload: A dict. Please see the formal API spec for details.
1275 :code: Http status code
1276 :headers: Headers object
1277 :user: the UserDatastore object (or None). Note that this is usually
1278 the same as current_user - but not always.
1279
1280 The default implementation simply returns::
1281
1282 headers["Content-Type"] = "application/json"
1283 payload = dict(meta=dict(code=code), response=payload)
1284 return make_response(jsonify(payload), code, headers)
1285
1286 .. important::
1287 Be aware the Flask's ``jsonify`` method will first look to see if a
1288 ``json_encoder`` has been set on the blueprint corresponding to the current
1289 request. If not then it looks for a ``json_encoder`` registered on the app;
1290 and finally uses Flask's default JSONEncoder class. Flask-Security registers
1291 :func:`FsJsonEncoder` as its blueprint json_encoder.
1292
1293
1294 This can be used by applications to unify all their JSON API responses.
1295 This is called in a request context and should return a Response or something
1296 Flask can create a Response from.
1297
1298 .. versionadded:: 3.3.0
1299 """
1300 self._state._render_json = cb
1301
1302 def want_json(self, fn):
1303 """ Function that returns True if response should be JSON (based on the request)
1304
1305 :param fn: Function with the following signature (request)
1306
1307 :request: Werkzueg/Flask request
1308
1309 The default implementation returns True if either the Content-Type is
1310 "application/json" or the best Accept header value is "application/json".
1311
1312 .. versionadded:: 3.3.0
1313 """
1314 self._state._want_json = fn
1315
1316 def unauthz_handler(self, cb):
1317 """
1318 Callback for failed authorization.
1319 This is called by the :func:`roles_required`, :func:`roles_accepted`,
1320 :func:`permissions_required`, or :func:`permissions_accepted`
1321 if a role or permission is missing.
1322
1323 :param cb: Callback function with signature (func, params)
1324
1325 :func: the decorator function (e.g. roles_required)
1326 :params: list of what (if any) was passed to the decorator.
1327
1328 Should return a Response or something Flask can create a Response from.
1329 Can raise an exception if it is handled as part of
1330 flask.errorhandler(<exception>)
1331
1332 With the passed parameters the application could deliver a concise error
1333 message.
1334
1335 .. versionadded:: 3.3.0
1336 """
1337 self._state._unauthz_handler = cb
1338
1339 def unauthn_handler(self, cb):
1340 """
1341 Callback for failed authentication.
1342 This is called by :func:`auth_required`, :func:`auth_token_required`
1343 or :func:`http_auth_required` if authentication fails.
1344
1345 :param cb: Callback function with signature (mechanisms, headers=None)
1346
1347 :mechanisms: List of which authentication mechanisms were tried
1348 :headers: dict of headers to return
1349
1350 Should return a Response or something Flask can create a Response from.
1351 Can raise an exception if it is handled as part of
1352 ``flask.errorhandler(<exception>)``
1353
1354 The default implementation will return a 401 response if the request was JSON,
1355 otherwise lets ``flask_login.login_manager.unauthorized()`` handle redirects.
1356
1357 .. versionadded:: 3.3.0
1358 """
1359 self._state._unauthn_handler = cb
1360
1361 def reauthn_handler(self, cb):
1362 """
1363 Callback when endpoint required a fresh authentication.
1364 This is called by :func:`auth_required`.
1365
1366 :param cb: Callback function with signature (within, grace)
1367
1368 :within: timedelta that endpoint required fresh authentication within.
1369 :grace: timedelta of grace period that endpoint allowed.
1370
1371 Should return a Response or something Flask can create a Response from.
1372 Can raise an exception if it is handled as part of
1373 ``flask.errorhandler(<exception>)``
1374
1375 The default implementation will return a 401 response if the request was JSON,
1376 otherwise will redirect to :py:data:`SECURITY_US_VERIFY_URL`
1377 (if :py:data:`SECURITY_UNIFIED_SIGNIN` is enabled)
1378 else to :py:data:`SECURITY_VERIFY_URL`.
1379 If both of those are None it sends an ``abort(401)``.
1380
1381 See :meth:`flask_security.auth_required` for details about freshness checking.
1382
1383 .. versionadded:: 3.4.0
1384 """
1385 self._state._reauthn_handler = cb
1386
1387 def password_validator(self, cb):
1388 """
1389 Callback for validating a user password.
1390 This is called on registration as well as change and reset password.
1391 For registration, ``kwargs`` will be all the form input fields that are
1392 attributes of the user model.
1393 For reset/change, ``kwargs`` will be user=UserModel
1394
1395 :param cb: Callback function with signature (password, is_register, kwargs)
1396
1397 :password: desired new plain text password
1398 :is_register: True if called as part of initial registration
1399 :kwargs: user info
1400
1401 Returns: None if password passes all validations. A list of (localized) messages
1402 if not.
1403
1404 .. versionadded:: 3.4.0
1405 Refer to :ref:`pass_validation_topic` for more information.
1406 """
1407 self._state._password_validator = cb
1408
1409 def __getattr__(self, name):
1410 return getattr(self._state, name, None)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.datastore
3 ~~~~~~~~~~~~~~~~~~~~~~~~
4
5 This module contains an user datastore classes.
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10 """
11 import json
12 import uuid
13
14 from .utils import config_value, get_identity_attributes, string_types
15
16
17 class Datastore(object):
18 def __init__(self, db):
19 self.db = db
20
21 def commit(self):
22 pass
23
24 def put(self, model):
25 raise NotImplementedError
26
27 def delete(self, model):
28 raise NotImplementedError
29
30
31 class SQLAlchemyDatastore(Datastore):
32 def commit(self):
33 self.db.session.commit()
34
35 def put(self, model):
36 self.db.session.add(model)
37 return model
38
39 def delete(self, model):
40 self.db.session.delete(model)
41
42
43 class MongoEngineDatastore(Datastore):
44 def put(self, model):
45 model.save()
46 return model
47
48 def delete(self, model):
49 model.delete()
50
51
52 class PeeweeDatastore(Datastore):
53 def put(self, model):
54 model.save()
55 return model
56
57 def delete(self, model):
58 model.delete_instance(recursive=True)
59
60
61 def with_pony_session(f):
62 from functools import wraps
63
64 @wraps(f)
65 def decorator(*args, **kwargs):
66 from pony.orm import db_session
67 from pony.orm.core import local
68 from flask import (
69 after_this_request,
70 current_app,
71 has_app_context,
72 has_request_context,
73 )
74 from flask.signals import appcontext_popped
75
76 register = local.db_context_counter == 0
77 if register and (has_app_context() or has_request_context()):
78 db_session.__enter__()
79
80 result = f(*args, **kwargs)
81
82 if register:
83 if has_request_context():
84
85 @after_this_request
86 def pop(request):
87 db_session.__exit__()
88 return request
89
90 elif has_app_context():
91
92 @appcontext_popped.connect_via(current_app._get_current_object())
93 def pop(sender, *args, **kwargs):
94 while local.db_context_counter:
95 db_session.__exit__()
96
97 else:
98 raise RuntimeError("Needs app or request context")
99 return result
100
101 return decorator
102
103
104 class PonyDatastore(Datastore):
105 def commit(self):
106 self.db.commit()
107
108 @with_pony_session
109 def put(self, model):
110 return model
111
112 @with_pony_session
113 def delete(self, model):
114 model.delete()
115
116
117 class UserDatastore(object):
118 """Abstracted user datastore.
119
120 :param user_model: A user model class definition
121 :param role_model: A role model class definition
122
123 .. important::
124 For mutating operations, the user/role will be added to the
125 datastore (by calling self.put(<object>). If the datastore is session based
126 (such as for SQLAlchemyDatastore) it is up to caller to actually
127 commit the transaction by calling datastore.commit().
128 """
129
130 def __init__(self, user_model, role_model):
131 self.user_model = user_model
132 self.role_model = role_model
133
134 def _prepare_role_modify_args(self, user, role):
135 if isinstance(user, string_types):
136 user = self.find_user(email=user)
137 if isinstance(role, string_types):
138 role = self.find_role(role)
139 return user, role
140
141 def _prepare_create_user_args(self, **kwargs):
142 kwargs.setdefault("active", True)
143 roles = kwargs.get("roles", [])
144 for i, role in enumerate(roles):
145 rn = role.name if isinstance(role, self.role_model) else role
146 # see if the role exists
147 roles[i] = self.find_role(rn)
148 kwargs["roles"] = roles
149 if hasattr(self.user_model, "fs_uniquifier"):
150 kwargs.setdefault("fs_uniquifier", uuid.uuid4().hex)
151 return kwargs
152
153 def _is_numeric(self, value):
154 try:
155 int(value)
156 except (TypeError, ValueError):
157 return False
158 return True
159
160 def _is_uuid(self, value):
161 return isinstance(value, uuid.UUID)
162
163 def get_user(self, id_or_email):
164 """Returns a user matching the specified ID or email address."""
165 raise NotImplementedError
166
167 def find_user(self, *args, **kwargs):
168 """Returns a user matching the provided parameters."""
169 raise NotImplementedError
170
171 def find_role(self, *args, **kwargs):
172 """Returns a role matching the provided name."""
173 raise NotImplementedError
174
175 def add_role_to_user(self, user, role):
176 """Adds a role to a user.
177
178 :param user: The user to manipulate. Can be an User object or email
179 :param role: The role to add to the user. Can be a Role object or
180 string role name
181 """
182 user, role = self._prepare_role_modify_args(user, role)
183 if role not in user.roles:
184 user.roles.append(role)
185 self.put(user)
186 return True
187 return False
188
189 def remove_role_from_user(self, user, role):
190 """Removes a role from a user.
191
192 :param user: The user to manipulate. Can be an User object or email
193 :param role: The role to remove from the user. Can be a Role object or
194 string role name
195 """
196 rv = False
197 user, role = self._prepare_role_modify_args(user, role)
198 if role in user.roles:
199 rv = True
200 user.roles.remove(role)
201 self.put(user)
202 return rv
203
204 def toggle_active(self, user):
205 """Toggles a user's active status. Always returns True."""
206 user.active = not user.active
207 self.put(user)
208 return True
209
210 def deactivate_user(self, user):
211 """Deactivates a specified user. Returns `True` if a change was made.
212
213 This will immediately disallow access to all endpoints that require
214 authentication either via session or tokens.
215 The user will not be able to log in again.
216
217 :param user: The user to deactivate
218 """
219 if user.active:
220 user.active = False
221 self.put(user)
222 return True
223 return False
224
225 def activate_user(self, user):
226 """Activates a specified user. Returns `True` if a change was made.
227
228 :param user: The user to activate
229 """
230 if not user.active:
231 user.active = True
232 self.put(user)
233 return True
234 return False
235
236 def set_uniquifier(self, user, uniquifier=None):
237 """ Set user's authentication token uniquifier.
238 This will immediately render outstanding auth tokens,
239 session cookies and remember cookies invalid.
240
241 :param user: User to modify
242 :param uniquifier: Unique value - if none then uuid.uuid4().hex is used
243
244 This method is a no-op if the user model doesn't contain the attribute
245 ``fs_uniquifier``
246
247 .. versionadded:: 3.3.0
248 """
249 if not hasattr(user, "fs_uniquifier"):
250 return
251 if not uniquifier:
252 uniquifier = uuid.uuid4().hex
253 user.fs_uniquifier = uniquifier
254 self.put(user)
255
256 def create_role(self, **kwargs):
257 """
258 Creates and returns a new role from the given parameters.
259 Supported params (depending on RoleModel):
260
261 :kwparam name: Role name
262 :kwparam permissions: a comma delimited list of permissions, a set or a list.
263 These are user-defined strings that correspond to strings used with
264 @permissions_required()
265
266 .. versionadded:: 3.3.0
267
268 """
269
270 # By default we just use raw DB model create - for permissions we want to
271 # be nicer and allow sending in a list or set or comma separated string.
272 if "permissions" in kwargs and hasattr(self.role_model, "permissions"):
273 perms = kwargs["permissions"]
274 if isinstance(perms, list) or isinstance(perms, set):
275 perms = ",".join(perms)
276 elif isinstance(perms, string_types):
277 # squash spaces.
278 perms = ",".join([p.strip() for p in perms.split(",")])
279 kwargs["permissions"] = perms
280
281 role = self.role_model(**kwargs)
282 return self.put(role)
283
284 def find_or_create_role(self, name, **kwargs):
285 """Returns a role matching the given name or creates it with any
286 additionally provided parameters.
287 """
288 kwargs["name"] = name
289 return self.find_role(name) or self.create_role(**kwargs)
290
291 def create_user(self, **kwargs):
292 """Creates and returns a new user from the given parameters.
293
294 :kwparam email: required.
295 :kwparam password: Hashed password.
296 :kwparam roles: list of roles to be added to user.
297 Can be Role objects or strings
298
299 .. danger::
300 Be aware that whatever `password` is passed in will
301 be stored directly in the DB. Do NOT pass in a plaintext password!
302 Best practice is to pass in ``hash_password(plaintext_password)``.
303
304 Furthermore, no validation is done on the password (e.g for minimum length).
305 Best practice is to call
306 ``app.security._password_validator(plaintext_password, True)``
307 and look for a ``None`` return meaning the password conforms to the
308 configured validations.
309
310 The new user's ``active`` property will be set to ``True``
311 unless explicitly set to ``False`` in `kwargs`.
312 """
313 kwargs = self._prepare_create_user_args(**kwargs)
314 user = self.user_model(**kwargs)
315 return self.put(user)
316
317 def delete_user(self, user):
318 """Deletes the specified user.
319
320 :param user: The user to delete
321 """
322 self.delete(user)
323
324 def reset_user_access(self, user):
325 """
326 Use this method to reset user authentication methods in the case of compromise.
327 This will:
328
329 * reset fs_uniquifier - which causes session cookie, remember cookie, auth
330 tokens to be unusable
331 * remove all unified signin TOTP secrets so those can't be used
332 * remove all two-factor secrets so those can't be used
333
334 Note that if using unified sign in and allow 'email' as a way to receive a code
335 if the email is compromised - login is still possible. To handle this - it
336 is better to deactivate the user.
337
338 Note - this method isn't used directly by Flask-Security - it is provided
339 as a helper for an applications administrative needs.
340
341 Remember to call commit on DB if needed.
342
343 .. versionadded:: 3.4.1
344 """
345 self.set_uniquifier(user)
346 if hasattr(user, "us_totp_secrets"):
347 self.us_reset(user)
348 if hasattr(user, "tf_primary_method"):
349 self.tf_reset(user)
350
351 def tf_set(self, user, primary_method, totp_secret=None, phone=None):
352 """ Set two-factor info into user record.
353 This carefully only changes things if different.
354
355 If totp_secret isn't provided - existing one won't be changed.
356 If phone isn't provided, the existing phone number won't be changed.
357
358 This could be called from an application to apiori setup a user for two factor
359 without the user having to go through the setup process.
360
361 To get a totp_secret - use ``app.security._totp_factory.generate_totp_secret()``
362
363 .. versionadded: 3.4.1
364 """
365
366 changed = False
367 if user.tf_primary_method != primary_method:
368 user.tf_primary_method = primary_method
369 changed = True
370 if totp_secret and user.tf_totp_secret != totp_secret:
371 user.tf_totp_secret = totp_secret
372 changed = True
373 if phone and user.tf_phone_number != phone:
374 user.tf_phone_number = phone
375 changed = True
376 if changed:
377 self.put(user)
378
379 def tf_reset(self, user):
380 """ Disable two-factor auth for user
381
382 .. versionadded: 3.4.1
383 """
384 user.tf_primary_method = None
385 user.tf_totp_secret = None
386 user.tf_phone_number = None
387 self.put(user)
388
389 def us_get_totp_secrets(self, user):
390 """ Return totp secrets.
391 These are json encoded in the DB.
392
393 Returns a dict with methods as keys and secrets as values.
394
395 .. versionadded:: 3.4.0
396 """
397 if not user.us_totp_secrets:
398 return {}
399 return json.loads(user.us_totp_secrets)
400
401 def us_put_totp_secrets(self, user, secrets):
402 """ Save secrets. Assume to be a dict (or None)
403 with keys as methods, and values as (encrypted) secrets.
404
405 .. versionadded:: 3.4.0
406 """
407 user.us_totp_secrets = json.dumps(secrets) if secrets else None
408 self.put(user)
409
410 def us_set(self, user, method, totp_secret=None, phone=None):
411 """ Set unified sign in info into user record.
412
413 If totp_secret isn't provided - existing one won't be changed.
414 If phone isn't provided, the existing phone number won't be changed.
415
416 This could be called from an application to apiori setup a user for unified
417 sign in without the user having to go through the setup process.
418
419 To get a totp_secret - use ``app.security._totp_factory.generate_totp_secret()``
420
421 .. versionadded: 3.4.1
422 """
423
424 if totp_secret:
425 totp_secrets = self.us_get_totp_secrets(user)
426 totp_secrets[method] = totp_secret
427 self.us_put_totp_secrets(user, totp_secrets)
428 if phone and user.us_phone_number != phone:
429 user.us_phone_number = phone
430 self.put(user)
431
432 def us_reset(self, user):
433 """ Disable unified sign in for user.
434 Be aware that if "email" is an allowed way to receive codes, they
435 will still work (as totp secrets are generated on the fly).
436 This will disable authenticator app and SMS.
437
438 .. versionadded: 3.4.1
439 """
440 user.us_totp_secrets = None
441 user.us_phone_number = None
442 self.put(user)
443
444
445 class SQLAlchemyUserDatastore(SQLAlchemyDatastore, UserDatastore):
446 """A SQLAlchemy datastore implementation for Flask-Security that assumes the
447 use of the Flask-SQLAlchemy extension.
448 """
449
450 def __init__(self, db, user_model, role_model):
451 SQLAlchemyDatastore.__init__(self, db)
452 UserDatastore.__init__(self, user_model, role_model)
453
454 def get_user(self, identifier):
455 from sqlalchemy import func as alchemyFn
456 from sqlalchemy import inspect
457 from sqlalchemy.sql import sqltypes
458 from sqlalchemy.dialects.postgresql import UUID as PSQL_UUID
459
460 user_model_query = self.user_model.query
461 if config_value("JOIN_USER_ROLES") and hasattr(self.user_model, "roles"):
462 from sqlalchemy.orm import joinedload
463
464 user_model_query = user_model_query.options(joinedload("roles"))
465
466 # To support both numeric, string, and UUID primary keys, and support
467 # calling this routine with either a numeric value or a string or a UUID
468 # we need to make sure the types basically match.
469 # psycopg2 for example will complain if we attempt to 'get' a
470 # numeric primary key with a string value.
471 # TODO: other datastores don't support this - they assume the only
472 # PK is user.id. That makes things easier but for backwards compat...
473 ins = inspect(self.user_model)
474 pk_type = ins.primary_key[0].type
475 pk_isnumeric = isinstance(pk_type, sqltypes.Integer)
476 pk_isuuid = isinstance(pk_type, PSQL_UUID)
477 # Are they the same or NOT numeric nor UUID
478 if (
479 (pk_isnumeric and self._is_numeric(identifier))
480 or (pk_isuuid and self._is_uuid(identifier))
481 or (not pk_isnumeric and not pk_isuuid)
482 ):
483 rv = self.user_model.query.get(identifier)
484 if rv is not None:
485 return rv
486
487 # Not PK - iterate through other attributes and look for 'identifier'
488 for attr in get_identity_attributes():
489 column = getattr(self.user_model, attr)
490 attr_isnumeric = isinstance(column.type, sqltypes.Integer)
491
492 query = None
493 if attr_isnumeric and self._is_numeric(identifier):
494 query = column == identifier
495 elif not attr_isnumeric and not self._is_numeric(identifier):
496 # Look for exact case-insensitive match - 'ilike' honors
497 # wild cards which isn't what we want.
498 query = alchemyFn.lower(column) == alchemyFn.lower(identifier)
499 if query is not None:
500 rv = user_model_query.filter(query).first()
501 if rv is not None:
502 return rv
503
504 def find_user(self, **kwargs):
505 query = self.user_model.query
506 if config_value("JOIN_USER_ROLES") and hasattr(self.user_model, "roles"):
507 from sqlalchemy.orm import joinedload
508
509 query = query.options(joinedload("roles"))
510
511 return query.filter_by(**kwargs).first()
512
513 def find_role(self, role):
514 return self.role_model.query.filter_by(name=role).first()
515
516
517 class SQLAlchemySessionUserDatastore(SQLAlchemyUserDatastore, SQLAlchemyDatastore):
518 """A SQLAlchemy datastore implementation for Flask-Security that assumes the
519 use of the flask_sqlalchemy_session extension.
520 """
521
522 def __init__(self, session, user_model, role_model):
523 class PretendFlaskSQLAlchemyDb(object):
524 """ This is a pretend db object, so we can just pass in a session.
525 """
526
527 def __init__(self, session):
528 self.session = session
529
530 SQLAlchemyUserDatastore.__init__(
531 self, PretendFlaskSQLAlchemyDb(session), user_model, role_model
532 )
533
534 def commit(self):
535 super(SQLAlchemySessionUserDatastore, self).commit()
536
537
538 class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
539 """A MongoEngine datastore implementation for Flask-Security that assumes
540 the use of the Flask-MongoEngine extension.
541 """
542
543 def __init__(self, db, user_model, role_model):
544 MongoEngineDatastore.__init__(self, db)
545 UserDatastore.__init__(self, user_model, role_model)
546
547 def get_user(self, identifier):
548 from mongoengine import ValidationError
549
550 try:
551 return self.user_model.objects(id=identifier).first()
552 except (ValidationError, ValueError):
553 pass
554
555 is_numeric = self._is_numeric(identifier)
556
557 for attr in get_identity_attributes():
558 query_key = attr if is_numeric else "%s__iexact" % attr
559 query = {query_key: identifier}
560 try:
561 rv = self.user_model.objects(**query).first()
562 if rv is not None:
563 return rv
564 except (ValidationError, ValueError):
565 # This can happen if identifier is a string but attribute is
566 # an int.
567 pass
568
569 def find_user(self, **kwargs):
570 try:
571 from mongoengine.queryset import Q, QCombination
572 except ImportError:
573 from mongoengine.queryset.visitor import Q, QCombination
574 from mongoengine.errors import ValidationError
575
576 queries = map(lambda i: Q(**{i[0]: i[1]}), kwargs.items())
577 query = QCombination(QCombination.AND, queries)
578 try:
579 return self.user_model.objects(query).first()
580 except ValidationError: # pragma: no cover
581 return None
582
583 def find_role(self, role):
584 return self.role_model.objects(name=role).first()
585
586
587 class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
588 """A PeeweeD datastore implementation for Flask-Security that assumes the
589 use of Peewee Flask utils.
590
591 :param user_model: A user model class definition
592 :param role_model: A role model class definition
593 :param role_link: A model implementing the many-to-many user-role relation
594 """
595
596 def __init__(self, db, user_model, role_model, role_link):
597 PeeweeDatastore.__init__(self, db)
598 UserDatastore.__init__(self, user_model, role_model)
599 self.UserRole = role_link
600
601 def get_user(self, identifier):
602 from peewee import fn as peeweeFn
603 from peewee import IntegerField
604
605 # For peewee we only (currently) support numeric primary keys.
606 if self._is_numeric(identifier):
607 try:
608 return self.user_model.get(self.user_model.id == identifier)
609 except (self.user_model.DoesNotExist, ValueError):
610 pass
611
612 for attr in get_identity_attributes():
613 # Read above (SQLAlchemy store) for why we are checking types.
614 column = getattr(self.user_model, attr)
615 attr_isnumeric = isinstance(column, IntegerField)
616 try:
617 if attr_isnumeric and self._is_numeric(identifier):
618 return self.user_model.get(column == identifier)
619 elif not attr_isnumeric and not self._is_numeric(identifier):
620 return self.user_model.get(
621 peeweeFn.Lower(column) == peeweeFn.Lower(identifier)
622 )
623 except (self.user_model.DoesNotExist, ValueError):
624 pass
625
626 def find_user(self, **kwargs):
627 try:
628 return self.user_model.filter(**kwargs).get()
629 except self.user_model.DoesNotExist:
630 return None
631
632 def find_role(self, role):
633 try:
634 return self.role_model.filter(name=role).get()
635 except self.role_model.DoesNotExist:
636 return None
637
638 def create_user(self, **kwargs):
639 """Creates and returns a new user from the given parameters."""
640 roles = kwargs.pop("roles", [])
641 user = self.user_model(**self._prepare_create_user_args(**kwargs))
642 user = self.put(user)
643 for role in roles:
644 self.add_role_to_user(user, role)
645 self.put(user)
646 return user
647
648 def add_role_to_user(self, user, role):
649 """Adds a role to a user.
650
651 :param user: The user to manipulate
652 :param role: The role to add to the user
653 """
654 user, role = self._prepare_role_modify_args(user, role)
655 result = self.UserRole.select().where(
656 self.UserRole.user == user.id, self.UserRole.role == role.id
657 )
658 if result.count():
659 return False
660 else:
661 self.put(self.UserRole.create(user=user.id, role=role.id))
662 return True
663
664 def remove_role_from_user(self, user, role):
665 """Removes a role from a user.
666
667 :param user: The user to manipulate
668 :param role: The role to remove from the user
669 """
670 user, role = self._prepare_role_modify_args(user, role)
671 result = self.UserRole.select().where(
672 self.UserRole.user == user, self.UserRole.role == role
673 )
674 if result.count():
675 query = self.UserRole.delete().where(
676 self.UserRole.user == user, self.UserRole.role == role
677 )
678 query.execute()
679 return True
680 else:
681 return False
682
683
684 class PonyUserDatastore(PonyDatastore, UserDatastore):
685 """A Pony ORM datastore implementation for Flask-Security.
686
687 Code primarily from https://github.com/ET-CS but taken over after
688 being abandoned.
689 """
690
691 def __init__(self, db, user_model, role_model):
692 PonyDatastore.__init__(self, db)
693 UserDatastore.__init__(self, user_model, role_model)
694
695 @with_pony_session
696 def get_user(self, identifier):
697 from pony.orm.core import ObjectNotFound
698
699 try:
700 return self.user_model[identifier]
701 except (ObjectNotFound, ValueError):
702 pass
703
704 for attr in get_identity_attributes():
705 # this is a nightmare, tl;dr we need to get the thing that
706 # corresponds to email (usually)
707 try:
708 user = self.user_model.get(**{attr: identifier})
709 if user is not None:
710 return user
711 except (TypeError, ValueError):
712 pass
713
714 @with_pony_session
715 def find_user(self, **kwargs):
716 return self.user_model.get(**kwargs)
717
718 @with_pony_session
719 def find_role(self, role):
720 return self.role_model.get(name=role)
721
722 @with_pony_session
723 def add_role_to_user(self, *args, **kwargs):
724 return super(PonyUserDatastore, self).add_role_to_user(*args, **kwargs)
725
726 @with_pony_session
727 def create_user(self, **kwargs):
728 return super(PonyUserDatastore, self).create_user(**kwargs)
729
730 @with_pony_session
731 def create_role(self, **kwargs):
732 return super(PonyUserDatastore, self).create_role(**kwargs)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.decorators
3 ~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security decorators module
6
7 :copyright: (c) 2012-2019 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10 """
11
12 from collections import namedtuple
13 import datetime
14 from functools import wraps
15
16 from flask import Response, _request_ctx_stack, abort, current_app, g, redirect, request
17 from flask_login import current_user, login_required # noqa: F401
18 from flask_principal import Identity, Permission, RoleNeed, identity_changed
19 from flask_wtf.csrf import CSRFError
20 from werkzeug.local import LocalProxy
21 from werkzeug.routing import BuildError
22
23 from .utils import (
24 FsPermNeed,
25 config_value,
26 do_flash,
27 get_message,
28 get_url,
29 check_and_update_authn_fresh,
30 json_error_response,
31 )
32
33 # Convenient references
34 _security = LocalProxy(lambda: current_app.extensions["security"])
35
36 _csrf = LocalProxy(lambda: current_app.extensions["csrf"])
37
38 BasicAuth = namedtuple("BasicAuth", "username, password")
39
40 # NOTE: this is here for backwards compatibility, it is deprecated and
41 # to be removed in 4.0
42 _default_unauthenticated_html = """
43 <h1>Unauthorized</h1>
44 <p>The server could not verify that you are authorized to access the URL
45 requested. You either supplied the wrong credentials (e.g. a bad password),
46 or your browser doesn't understand how to supply the credentials required.
47 </p>
48 """
49
50
51 def _get_unauthenticated_response(text=None, headers=None):
52 text = text or _default_unauthenticated_html
53 headers = headers or {}
54 return Response(text, 401, headers)
55
56
57 def _get_unauthorized_response(text=None, headers=None): # pragma: no cover
58 # People called this - even though it isn't public - no harm in keeping it.
59 return _get_unauthenticated_response(text, headers)
60
61
62 def default_unauthn_handler(mechanisms, headers=None):
63 """ Default callback for failures to authenticate
64
65 If caller wants JSON - return 401
66 Otherwise - assume caller is html and redirect if possible to a login view.
67 We let Flask-Login handle this.
68
69 """
70 msg = get_message("UNAUTHENTICATED")[0]
71
72 if config_value("BACKWARDS_COMPAT_UNAUTHN"):
73 return _get_unauthenticated_response(headers=headers)
74 if _security._want_json(request):
75 # Ignore headers since today, the only thing in there might be WWW-Authenticate
76 # and we never want to send that in a JSON response (browsers will intercept
77 # that and pop up their own login form).
78 payload = json_error_response(errors=msg)
79 return _security._render_json(payload, 401, None, None)
80 return _security.login_manager.unauthorized()
81
82
83 def default_reauthn_handler(within, grace):
84 """ Default callback for 'freshness' related authn failures.
85
86 If caller wants JSON - return 401
87 Otherwise - assume caller is html and redirect if possible to configured view.
88
89 """
90 m, c = get_message("REAUTHENTICATION_REQUIRED")
91
92 if _security._want_json(request):
93 is_us = config_value("UNIFIED_SIGNIN")
94 payload = json_error_response(errors=m)
95 payload["reauth_required"] = True
96 payload["unified_signin_enabled"] = is_us
97 return _security._render_json(payload, 401, None, None)
98
99 if config_value("UNIFIED_SIGNIN"):
100 view = _security.us_verify_url
101 else:
102 view = _security.verify_url
103 if view:
104 do_flash(m, c)
105 redirect_url = get_url(view, qparams={"next": request.url})
106 return redirect(redirect_url)
107 abort(401)
108
109
110 def default_unauthz_handler(func, params):
111 unauthz_message, unauthz_message_type = get_message("UNAUTHORIZED")
112 if _security._want_json(request):
113 payload = json_error_response(errors=unauthz_message)
114 return _security._render_json(payload, 403, None, None)
115 view = config_value("UNAUTHORIZED_VIEW")
116 if view:
117 if callable(view):
118 view = view()
119 else:
120 try:
121 view = get_url(view)
122 except BuildError:
123 view = None
124 do_flash(unauthz_message, unauthz_message_type)
125 redirect_to = "/"
126 if request.referrer and not request.referrer.split("?")[0].endswith(
127 request.path
128 ):
129 redirect_to = request.referrer
130
131 return redirect(view or redirect_to)
132 abort(403)
133
134
135 def _check_token():
136 # N.B. this isn't great Flask-Login 0.5.0 made this protected
137 # Issue https://github.com/maxcountryman/flask-login/issues/471
138 # was filed to restore public access. We want to call this via
139 # login_manager in case someone has overridden the login_manager which we
140 # allow.
141 if hasattr(_security.login_manager, "request_callback"):
142 # Pre 0.5.0
143 user = _security.login_manager.request_callback(request)
144 else:
145 user = _security.login_manager._request_callback(request)
146
147 if user and user.is_authenticated:
148 app = current_app._get_current_object()
149 _request_ctx_stack.top.user = user
150 identity_changed.send(app, identity=Identity(user.id))
151 return True
152
153 return False
154
155
156 def _check_http_auth():
157 auth = request.authorization or BasicAuth(username=None, password=None)
158 if not auth.username:
159 return False
160 user = _security.datastore.get_user(auth.username)
161
162 if user and user.verify_and_update_password(auth.password):
163 _security.datastore.commit()
164 app = current_app._get_current_object()
165 _request_ctx_stack.top.user = user
166 identity_changed.send(app, identity=Identity(user.id))
167 return True
168
169 return False
170
171
172 def handle_csrf(method):
173 """ Invoke CSRF protection based on authentication method.
174
175 Usually this is called as part of a decorator, but if that isn't
176 appropriate, endpoint code can call this directly.
177
178 If CSRF protection is appropriate, this will call flask_wtf::protect() which
179 will raise a ValidationError on CSRF failure.
180
181 This routine does nothing if any of these are true:
182
183 #) *WTF_CSRF_ENABLED* is set to False
184
185 #) the Flask-WTF CSRF module hasn't been initialized
186
187 #) csrfProtect already checked and accepted the token
188
189 If the passed in method is not in *SECURITY_CSRF_PROTECT_MECHANISMS* then not only
190 will no CSRF code be run, but a flag in the current context ``fs_ignore_csrf``
191 will be set so that downstream code knows to ignore any CSRF checks.
192
193 .. versionadded:: 3.3.0
194 """
195 if (
196 not current_app.config.get("WTF_CSRF_ENABLED", False)
197 or not current_app.extensions.get("csrf", None)
198 or g.get("csrf_valid", False)
199 ):
200 return
201
202 if config_value("CSRF_PROTECT_MECHANISMS"):
203 if method in config_value("CSRF_PROTECT_MECHANISMS"):
204 _csrf.protect()
205 else:
206 _request_ctx_stack.top.fs_ignore_csrf = True
207
208
209 def http_auth_required(realm):
210 """Decorator that protects endpoints using Basic HTTP authentication.
211
212 :param realm: optional realm name
213
214 Once authenticated, if so configured, CSRF protection will be tested.
215 """
216
217 def decorator(fn):
218 @wraps(fn)
219 def wrapper(*args, **kwargs):
220 if _check_http_auth():
221 handle_csrf("basic")
222 return fn(*args, **kwargs)
223 if _security._unauthorized_callback:
224 return _security._unauthorized_callback()
225 else:
226 r = _security.default_http_auth_realm if callable(realm) else realm
227 h = {"WWW-Authenticate": 'Basic realm="%s"' % r}
228 return _security._unauthn_handler(["basic"], headers=h)
229
230 return wrapper
231
232 if callable(realm):
233 return decorator(realm)
234 return decorator
235
236
237 def auth_token_required(fn):
238 """Decorator that protects endpoints using token authentication. The token
239 should be added to the request by the client by using a query string
240 variable with a name equal to the configuration value of
241 *SECURITY_TOKEN_AUTHENTICATION_KEY* or in a request header named that of
242 the configuration value of *SECURITY_TOKEN_AUTHENTICATION_HEADER*
243
244 Once authenticated, if so configured, CSRF protection will be tested.
245 """
246
247 @wraps(fn)
248 def decorated(*args, **kwargs):
249 if _check_token():
250 handle_csrf("token")
251 return fn(*args, **kwargs)
252 if _security._unauthorized_callback:
253 return _security._unauthorized_callback()
254 else:
255 return _security._unauthn_handler(["token"])
256
257 return decorated
258
259
260 def auth_required(*auth_methods, **kwargs):
261 """
262 Decorator that protects endpoints through multiple mechanisms
263 Example::
264
265 @app.route('/dashboard')
266 @auth_required('token', 'session')
267 def dashboard():
268 return 'Dashboard'
269
270 :param auth_methods: Specified mechanisms (token, basic, session). If not specified
271 then all current available mechanisms will be tried.
272 :kwparam within: Add 'freshness' check to authentication. Is either an int
273 specifying # of minutes, or a callable that returns a timedelta. For timedeltas,
274 timedelta.total_seconds() is used for the calculations:
275
276 - If > 0, then the caller must have authenticated within the time specified
277 (as measured using the session cookie).
278 - If 0 and not within the grace period (see below) the caller will
279 always be redirected to re-authenticate.
280 - If < 0 (the default) no freshness check is performed.
281
282 Note that Basic Auth, by definition, is always 'fresh' and will never result in
283 a redirect/error.
284 :kwparam grace: Add a grace period for freshness checks. As above, either an int
285 or a callable returning a timedelta. If not specified then
286 :py:data:`SECURITY_FRESHNESS_GRACE_PERIOD` is used. The grace period allows
287 callers to complete the required operations w/o being prompted again.
288 See :meth:`flask_security.check_and_update_authn_fresh` for details.
289
290 Note that regardless of order specified - they will be tried in the following
291 order: token, session, basic.
292
293 The first mechanism that succeeds is used, following that, depending on
294 configuration, CSRF protection will be tested.
295
296 On authentication failure `.Security.unauthorized_callback` (deprecated)
297 or :meth:`.Security.unauthn_handler` will be called.
298
299 .. versionchanged:: 3.3.0
300 If ``auth_methods`` isn't specified, then all will be tried. Authentication
301 mechanisms will always be tried in order of ``token``, ``session``, ``basic``
302 regardless of how they are specified in the ``auth_methods`` parameter.
303
304 .. versionchanged:: 3.4.0
305 Added ``within`` and ``grace`` parameters to enforce a freshness check.
306
307 """
308
309 login_mechanisms = {
310 "token": lambda: _check_token(),
311 "session": lambda: current_user.is_authenticated,
312 "basic": lambda: _check_http_auth(),
313 }
314 mechanisms_order = ["token", "session", "basic"]
315 if not auth_methods:
316 auth_methods = {"basic", "session", "token"}
317 else:
318 auth_methods = [am for am in auth_methods]
319
320 def wrapper(fn):
321 @wraps(fn)
322 def decorated_view(*args, **dkwargs):
323 # 2.7 doesn't support keyword args after *args....
324 within = kwargs.get("within", -1)
325 if callable(within):
326 within = within()
327 else:
328 within = datetime.timedelta(minutes=within)
329 grace = kwargs.get("grace", None)
330 if grace is None:
331 grace = config_value("FRESHNESS_GRACE_PERIOD")
332 elif callable(grace):
333 grace = grace()
334 else:
335 grace = datetime.timedelta(minutes=grace)
336
337 h = {}
338 if "basic" in auth_methods:
339 r = _security.default_http_auth_realm
340 h["WWW-Authenticate"] = 'Basic realm="%s"' % r
341 mechanisms = [
342 (method, login_mechanisms.get(method))
343 for method in mechanisms_order
344 if method in auth_methods
345 ]
346 for method, mechanism in mechanisms:
347 if mechanism and mechanism():
348 # successfully authenticated. Basic auth is by definition 'fresh'.
349 # Note that using token auth is ok - but caller still has to pass
350 # in a session cookie...
351 if method != "basic" and not check_and_update_authn_fresh(
352 within, grace
353 ):
354 return _security._reauthn_handler(within, grace)
355 handle_csrf(method)
356 return fn(*args, **dkwargs)
357 if _security._unauthorized_callback:
358 return _security._unauthorized_callback()
359 else:
360 return _security._unauthn_handler(auth_methods, headers=h)
361
362 return decorated_view
363
364 return wrapper
365
366
367 def unauth_csrf(fall_through=False):
368 """Decorator for endpoints that don't need authentication
369 but do want CSRF checks (available via Header rather than just form).
370 This is required when setting *WTF_CSRF_CHECK_DEFAULT* = **False** since in that
371 case, without this decorator, the form validation will attempt to do the CSRF
372 check, and that will fail since the csrf-token is in the header (for pure JSON
373 requests).
374
375 This decorator does nothing unless Flask-WTF::CSRFProtect has been initialized.
376
377 This decorator does nothing if *WTF_CSRF_ENABLED* == **False**.
378
379 This decorator will always require CSRF if the caller is authenticated.
380
381 This decorator will suppress CSRF if caller isn't authenticated and has set the
382 *SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS* config variable.
383
384 :param fall_through: if set to True, then if CSRF fails here - simply keep going.
385 This is appropriate if underlying view is form based and once the form is
386 instantiated, the csrf_token will be available.
387 Note that this can mask some errors such as 'The CSRF session token is missing.'
388 meaning that the caller didn't send a session cookie and instead the caller
389 might get a 'The CSRF token is missing.' error.
390
391 .. versionadded:: 3.3.0
392 """
393
394 def wrapper(fn):
395 @wraps(fn)
396 def decorated(*args, **kwargs):
397 if not current_app.config.get(
398 "WTF_CSRF_ENABLED", False
399 ) or not current_app.extensions.get("csrf", None):
400 return fn(*args, **kwargs)
401
402 if (
403 config_value("CSRF_IGNORE_UNAUTH_ENDPOINTS")
404 and not current_user.is_authenticated
405 ):
406 _request_ctx_stack.top.fs_ignore_csrf = True
407 else:
408 try:
409 _csrf.protect()
410 except CSRFError:
411 if not fall_through:
412 raise
413
414 return fn(*args, **kwargs)
415
416 return decorated
417
418 return wrapper
419
420
421 def roles_required(*roles):
422 """Decorator which specifies that a user must have all the specified roles.
423 Example::
424
425 @app.route('/dashboard')
426 @roles_required('admin', 'editor')
427 def dashboard():
428 return 'Dashboard'
429
430 The current user must have both the `admin` role and `editor` role in order
431 to view the page.
432
433 :param roles: The required roles.
434 """
435
436 def wrapper(fn):
437 @wraps(fn)
438 def decorated_view(*args, **kwargs):
439 perms = [Permission(RoleNeed(role)) for role in roles]
440 for perm in perms:
441 if not perm.can():
442 if _security._unauthorized_callback:
443 # Backwards compat - deprecated
444 return _security._unauthorized_callback()
445 return _security._unauthz_handler(roles_required, list(roles))
446 return fn(*args, **kwargs)
447
448 return decorated_view
449
450 return wrapper
451
452
453 def roles_accepted(*roles):
454 """Decorator which specifies that a user must have at least one of the
455 specified roles. Example::
456
457 @app.route('/create_post')
458 @roles_accepted('editor', 'author')
459 def create_post():
460 return 'Create Post'
461
462 The current user must have either the `editor` role or `author` role in
463 order to view the page.
464
465 :param roles: The possible roles.
466 """
467
468 def wrapper(fn):
469 @wraps(fn)
470 def decorated_view(*args, **kwargs):
471 perm = Permission(*[RoleNeed(role) for role in roles])
472 if perm.can():
473 return fn(*args, **kwargs)
474 if _security._unauthorized_callback:
475 # Backwards compat - deprecated
476 return _security._unauthorized_callback()
477 return _security._unauthz_handler(roles_accepted, list(roles))
478
479 return decorated_view
480
481 return wrapper
482
483
484 def permissions_required(*fsperms):
485 """Decorator which specifies that a user must have all the specified permissions.
486 Example::
487
488 @app.route('/dashboard')
489 @permissions_required('admin-write', 'editor-write')
490 def dashboard():
491 return 'Dashboard'
492
493 The current user must have BOTH permissions (via the roles it has)
494 to view the page.
495
496 N.B. Don't confuse these permissions with flask-principle Permission()!
497
498 :param fsperms: The required permissions.
499
500 .. versionadded:: 3.3.0
501 """
502
503 def wrapper(fn):
504 @wraps(fn)
505 def decorated_view(*args, **kwargs):
506 perms = [Permission(FsPermNeed(fsperm)) for fsperm in fsperms]
507 for perm in perms:
508 if not perm.can():
509 if _security._unauthorized_callback:
510 # Backwards compat - deprecated
511 return _security._unauthorized_callback()
512 return _security._unauthz_handler(
513 permissions_required, list(fsperms)
514 )
515
516 return fn(*args, **kwargs)
517
518 return decorated_view
519
520 return wrapper
521
522
523 def permissions_accepted(*fsperms):
524 """Decorator which specifies that a user must have at least one of the
525 specified permissions. Example::
526
527 @app.route('/create_post')
528 @permissions_accepted('editor-write', 'author-wrote')
529 def create_post():
530 return 'Create Post'
531
532 The current user must have one of the permissions (via the roles it has)
533 to view the page.
534
535 N.B. Don't confuse these permissions with flask-principle Permission()!
536
537 :param fsperms: The possible permissions.
538
539 .. versionadded:: 3.3.0
540 """
541
542 def wrapper(fn):
543 @wraps(fn)
544 def decorated_view(*args, **kwargs):
545 perm = Permission(*[FsPermNeed(fsperm) for fsperm in fsperms])
546 if perm.can():
547 return fn(*args, **kwargs)
548 if _security._unauthorized_callback:
549 # Backwards compat - deprecated
550 return _security._unauthorized_callback()
551 return _security._unauthz_handler(permissions_accepted, list(fsperms))
552
553 return decorated_view
554
555 return wrapper
556
557
558 def anonymous_user_required(f):
559 """Decorator which requires that caller NOT be logged in.
560 If a logged in user accesses an endpoint protected with this decorator
561 they will be redirected to the *SECURITY_POST_LOGIN_VIEW*.
562 If the caller requests a JSON response, a 400 will be returned.
563
564 .. versionchanged:: 3.3.0
565 Support for JSON response was added.
566 """
567
568 @wraps(f)
569 def wrapper(*args, **kwargs):
570 if current_user.is_authenticated:
571 if _security._want_json(request):
572 payload = json_error_response(
573 errors=get_message("ANONYMOUS_USER_REQUIRED")[0]
574 )
575 return _security._render_json(payload, 400, None, None)
576 else:
577 return redirect(get_url(_security.post_login_view))
578 return f(*args, **kwargs)
579
580 return wrapper
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.forms
3 ~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security forms module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2017 by CERN.
9 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
10 :license: MIT, see LICENSE for more details.
11 """
12
13 import inspect
14
15 from flask import Markup, current_app, request
16 from flask_login import current_user
17 from flask_wtf import FlaskForm as BaseForm
18 from speaklater import is_lazy_string, make_lazy_string
19 from werkzeug.local import LocalProxy
20 from wtforms import (
21 BooleanField,
22 Field,
23 HiddenField,
24 PasswordField,
25 RadioField,
26 StringField,
27 SubmitField,
28 ValidationError,
29 validators,
30 )
31
32 from .confirmable import requires_confirmation
33 from .utils import (
34 _,
35 _datastore,
36 config_value,
37 do_flash,
38 get_message,
39 hash_password,
40 localize_callback,
41 url_for_security,
42 validate_redirect_url,
43 )
44
45 # Convenient references
46 _security = LocalProxy(lambda: current_app.extensions["security"])
47
48 _default_field_labels = {
49 "email": _("Email Address"),
50 "password": _("Password"),
51 "remember_me": _("Remember Me"),
52 "login": _("Login"),
53 "signin": _("Sign In"),
54 "register": _("Register"),
55 "send_confirmation": _("Resend Confirmation Instructions"),
56 "recover_password": _("Recover Password"),
57 "reset_password": _("Reset Password"),
58 "retype_password": _("Retype Password"),
59 "new_password": _("New Password"),
60 "change_password": _("Change Password"),
61 "send_login_link": _("Send Login Link"),
62 "verify_password": _("Verify Password"),
63 "change_method": _("Change Method"),
64 "phone": _("Phone Number"),
65 "code": _("Authentication Code"),
66 "submit": _("Submit"),
67 "submitcode": _("Submit Code"),
68 "error": _("Error(s)"),
69 "identity": _("Identity"),
70 "sendcode": _("Send Code"),
71 "passcode": _("Passcode"),
72 }
73
74
75 class ValidatorMixin(object):
76 """
77 This is called at import time - so there is no app context.
78 Validators have state - namely self.message - but we need that
79 xlated on a per-request basis. So we want a lazy_string - but we can't create
80 that until we are in an app context.
81 """
82
83 def __init__(self, *args, **kwargs):
84 # If the message is available from config[MSG_xx] then it can be xlated.
85 # Otherwise it will be used as is.
86 if "message" in kwargs:
87 self._original_message = kwargs["message"]
88 del kwargs["message"]
89 else:
90 self._original_message = None
91 super(ValidatorMixin, self).__init__(*args, **kwargs)
92
93 def __call__(self, form, field):
94 if self._original_message and (
95 not is_lazy_string(self.message) and not self.message
96 ):
97 # Creat on first usage within app context.
98 cv = config_value("MSG_" + self._original_message)
99 if cv:
100 self.message = make_lazy_string(_local_xlate, cv[0])
101 else:
102 self.message = self._original_message
103 return super(ValidatorMixin, self).__call__(form, field)
104
105
106 class EqualTo(ValidatorMixin, validators.EqualTo):
107 pass
108
109
110 class Required(ValidatorMixin, validators.DataRequired):
111 pass
112
113
114 class Email(ValidatorMixin, validators.Email):
115 pass
116
117
118 class Length(ValidatorMixin, validators.Length):
119 pass
120
121
122 email_required = Required(message="EMAIL_NOT_PROVIDED")
123 email_validator = Email(message="INVALID_EMAIL_ADDRESS")
124 password_required = Required(message="PASSWORD_NOT_PROVIDED")
125
126
127 def _local_xlate(text):
128 """ LazyStrings need to be evaluated in the context of a request
129 where _security.i18_domain is available.
130 """
131 return localize_callback(text)
132
133
134 def get_form_field_label(key):
135 """ This is called during import since form fields are declared as part of
136 class. Thus can't call 'localize_callback' until we need to actually
137 translate/render form.
138 """
139 return make_lazy_string(_local_xlate, _default_field_labels.get(key, ""))
140
141
142 def unique_user_email(form, field):
143 if _datastore.get_user(field.data) is not None:
144 msg = get_message("EMAIL_ALREADY_ASSOCIATED", email=field.data)[0]
145 raise ValidationError(msg)
146
147
148 def valid_user_email(form, field):
149 form.user = _datastore.get_user(field.data)
150 if form.user is None:
151 raise ValidationError(get_message("USER_DOES_NOT_EXIST")[0])
152
153
154 class Form(BaseForm):
155 def __init__(self, *args, **kwargs):
156 if current_app.testing:
157 self.TIME_LIMIT = None
158 super(Form, self).__init__(*args, **kwargs)
159
160
161 class EmailFormMixin:
162 email = StringField(
163 get_form_field_label("email"), validators=[email_required, email_validator]
164 )
165
166
167 class UserEmailFormMixin:
168 user = None
169 email = StringField(
170 get_form_field_label("email"),
171 validators=[email_required, email_validator, valid_user_email],
172 )
173
174
175 class UniqueEmailFormMixin:
176 email = StringField(
177 get_form_field_label("email"),
178 validators=[email_required, email_validator, unique_user_email],
179 )
180
181
182 class PasswordFormMixin:
183 password = PasswordField(
184 get_form_field_label("password"), validators=[password_required]
185 )
186
187
188 class NewPasswordFormMixin:
189 password = PasswordField(
190 get_form_field_label("password"), validators=[password_required]
191 )
192
193
194 class PasswordConfirmFormMixin:
195 password_confirm = PasswordField(
196 get_form_field_label("retype_password"),
197 validators=[
198 EqualTo("password", message="RETYPE_PASSWORD_MISMATCH"),
199 password_required,
200 ],
201 )
202
203
204 class NextFormMixin:
205 next = HiddenField()
206
207 def validate_next(self, field):
208 if field.data and not validate_redirect_url(field.data):
209 field.data = ""
210 do_flash(*get_message("INVALID_REDIRECT"))
211 raise ValidationError(get_message("INVALID_REDIRECT")[0])
212
213
214 class RegisterFormMixin:
215 submit = SubmitField(get_form_field_label("register"))
216
217 def to_dict(self, only_user):
218 """
219 Return form data as dictionary
220 :param only_user: bool, if True then only fields that have
221 corresponding members in UserModel are returned
222 :return: dict
223 """
224
225 def is_field_and_user_attr(member):
226 if not isinstance(member, Field):
227 return False
228
229 # If only fields recorded on UserModel should be returned,
230 # perform check on user model, else return True
231 if only_user is True:
232 return hasattr(_datastore.user_model, member.name)
233 else:
234 return True
235
236 fields = inspect.getmembers(self, is_field_and_user_attr)
237 return dict((key, value.data) for key, value in fields)
238
239
240 class SendConfirmationForm(Form, UserEmailFormMixin):
241 """The default send confirmation form"""
242
243 submit = SubmitField(get_form_field_label("send_confirmation"))
244
245 def __init__(self, *args, **kwargs):
246 super(SendConfirmationForm, self).__init__(*args, **kwargs)
247 if request.method == "GET":
248 self.email.data = request.args.get("email", None)
249
250 def validate(self):
251 if not super(SendConfirmationForm, self).validate():
252 return False
253 if self.user.confirmed_at is not None:
254 self.email.errors.append(get_message("ALREADY_CONFIRMED")[0])
255 return False
256 return True
257
258
259 class ForgotPasswordForm(Form, UserEmailFormMixin):
260 """The default forgot password form"""
261
262 submit = SubmitField(get_form_field_label("recover_password"))
263
264 def validate(self):
265 if not super(ForgotPasswordForm, self).validate():
266 return False
267 if not self.user.is_active:
268 self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
269 return False
270 if requires_confirmation(self.user):
271 self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
272 return False
273 return True
274
275
276 class PasswordlessLoginForm(Form, UserEmailFormMixin):
277 """The passwordless login form"""
278
279 submit = SubmitField(get_form_field_label("send_login_link"))
280
281 def __init__(self, *args, **kwargs):
282 super(PasswordlessLoginForm, self).__init__(*args, **kwargs)
283
284 def validate(self):
285 if not super(PasswordlessLoginForm, self).validate():
286 return False
287 if not self.user.is_active:
288 self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
289 return False
290 return True
291
292
293 class LoginForm(Form, NextFormMixin):
294 """The default login form"""
295
296 email = StringField(get_form_field_label("email"), validators=[email_required])
297 password = PasswordField(
298 get_form_field_label("password"), validators=[password_required]
299 )
300 remember = BooleanField(get_form_field_label("remember_me"))
301 submit = SubmitField(get_form_field_label("login"))
302
303 def __init__(self, *args, **kwargs):
304 super(LoginForm, self).__init__(*args, **kwargs)
305 if not self.next.data:
306 self.next.data = request.args.get("next", "")
307 self.remember.default = config_value("DEFAULT_REMEMBER_ME")
308 if (
309 current_app.extensions["security"].recoverable
310 and not self.password.description
311 ):
312 html = Markup(
313 u'<a href="{url}">{message}</a>'.format(
314 url=url_for_security("forgot_password"),
315 message=get_message("FORGOT_PASSWORD")[0],
316 )
317 )
318 self.password.description = html
319
320 def validate(self):
321 if not super(LoginForm, self).validate():
322 return False
323
324 self.user = _datastore.get_user(self.email.data)
325
326 if self.user is None:
327 self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
328 # Reduce timing variation between existing and non-existing users
329 hash_password(self.password.data)
330 return False
331 if not self.user.password:
332 self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
333 # Reduce timing variation between existing and non-existing users
334 hash_password(self.password.data)
335 return False
336 if not self.user.verify_and_update_password(self.password.data):
337 self.password.errors.append(get_message("INVALID_PASSWORD")[0])
338 return False
339 if requires_confirmation(self.user):
340 self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
341 return False
342 if not self.user.is_active:
343 self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
344 return False
345 return True
346
347
348 class VerifyForm(Form, PasswordFormMixin):
349 """The verify authentication form"""
350
351 user = None
352 submit = SubmitField(get_form_field_label("verify_password"))
353
354 def validate(self):
355 if not super(VerifyForm, self).validate():
356 return False
357
358 self.user = current_user
359 if not self.user.verify_and_update_password(self.password.data):
360 self.password.errors.append(get_message("INVALID_PASSWORD")[0])
361 return False
362 return True
363
364
365 class ConfirmRegisterForm(Form, RegisterFormMixin, UniqueEmailFormMixin):
366 """ This form is used for registering when 'confirmable' is set.
367 The only difference between this and the other RegisterForm is that
368 this one doesn't require re-typing in the password...
369 """
370
371 # Password optional when Unified Signin enabled.
372 password = PasswordField(
373 get_form_field_label("password"), validators=[validators.Optional()]
374 )
375
376 def validate(self):
377 if not super(ConfirmRegisterForm, self).validate():
378 return False
379
380 # To support unified sign in - we permit registering with no password.
381 if not config_value("UNIFIED_SIGNIN"):
382 # password required
383 if not self.password.data or not self.password.data.strip():
384 self.password.errors.append(get_message("PASSWORD_NOT_PROVIDED")[0])
385 return False
386
387 if self.password.data:
388 # We do explicit validation here for passwords
389 # (rather than write a validator class) for 2 reasons:
390 # 1) We want to control which fields are passed -
391 # sometimes that's current_user
392 # other times it's the registration fields.
393 # 2) We want to be able to return multiple error messages.
394 rfields = {}
395 for k, v in self.data.items():
396 if hasattr(_datastore.user_model, k):
397 rfields[k] = v
398 del rfields["password"]
399 pbad = _security._password_validator(self.password.data, True, **rfields)
400 if pbad:
401 self.password.errors.extend(pbad)
402 return False
403 return True
404
405
406 class RegisterForm(ConfirmRegisterForm, NextFormMixin):
407
408 # Password optional when Unified Signin enabled.
409 password_confirm = PasswordField(
410 get_form_field_label("retype_password"),
411 validators=[
412 EqualTo("password", message="RETYPE_PASSWORD_MISMATCH"),
413 validators.Optional(),
414 ],
415 )
416
417 def validate(self):
418 if not super(RegisterForm, self).validate():
419 return False
420 if not config_value("UNIFIED_SIGNIN"):
421 # password_confirm required
422 if not self.password_confirm.data or not self.password_confirm.data.strip():
423 self.password_confirm.errors.append(
424 get_message("PASSWORD_NOT_PROVIDED")[0]
425 )
426 return False
427 return True
428
429 def __init__(self, *args, **kwargs):
430 super(RegisterForm, self).__init__(*args, **kwargs)
431 if not self.next.data:
432 self.next.data = request.args.get("next", "")
433
434
435 class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin):
436 """The default reset password form"""
437
438 submit = SubmitField(get_form_field_label("reset_password"))
439
440 def validate(self):
441 if not super(ResetPasswordForm, self).validate():
442 return False
443
444 pbad = _security._password_validator(
445 self.password.data, False, user=current_user
446 )
447 if pbad:
448 self.password.errors.extend(pbad)
449 return False
450 return True
451
452
453 class ChangePasswordForm(Form, PasswordFormMixin):
454 """The default change password form"""
455
456 new_password = PasswordField(
457 get_form_field_label("new_password"), validators=[password_required]
458 )
459
460 new_password_confirm = PasswordField(
461 get_form_field_label("retype_password"),
462 validators=[
463 EqualTo("new_password", message="RETYPE_PASSWORD_MISMATCH"),
464 password_required,
465 ],
466 )
467
468 submit = SubmitField(get_form_field_label("change_password"))
469
470 def validate(self):
471 if not super(ChangePasswordForm, self).validate():
472 return False
473
474 if not current_user.verify_and_update_password(self.password.data):
475 self.password.errors.append(get_message("INVALID_PASSWORD")[0])
476 return False
477 if self.password.data == self.new_password.data:
478 self.password.errors.append(get_message("PASSWORD_IS_THE_SAME")[0])
479 return False
480 pbad = _security._password_validator(
481 self.new_password.data, False, user=current_user
482 )
483 if pbad:
484 self.new_password.errors.extend(pbad)
485 return False
486 return True
487
488
489 class TwoFactorSetupForm(Form, UserEmailFormMixin):
490 """The Two-factor token validation form"""
491
492 setup = RadioField(
493 "Available Methods",
494 choices=[
495 ("email", "Set up using email"),
496 (
497 "authenticator",
498 "Set up using an authenticator app (e.g. google, lastpass, authy)",
499 ),
500 ("sms", "Set up using SMS"),
501 ("disable", "Disable two factor authentication"),
502 ],
503 )
504 phone = StringField(get_form_field_label("phone"))
505 submit = SubmitField(get_form_field_label("submit"))
506
507 def __init__(self, *args, **kwargs):
508 super(TwoFactorSetupForm, self).__init__(*args, **kwargs)
509
510 def validate(self):
511 # TODO: the super class validate is never called - thus we have to
512 # initialize errors to lists below. It also means that 'email' is never
513 # validated - though it isn't required so the mixin might not be correct.
514 choices = config_value("TWO_FACTOR_ENABLED_METHODS")
515 if "email" in choices:
516 # backwards compat
517 choices.append("mail")
518 if not config_value("TWO_FACTOR_REQUIRED"):
519 choices.append("disable")
520 if "setup" not in self.data or self.data["setup"] not in choices:
521 self.setup.errors = list()
522 self.setup.errors.append(get_message("TWO_FACTOR_METHOD_NOT_AVAILABLE")[0])
523 return False
524 if self.setup.data == "sms" and len(self.phone.data) > 0:
525 # Somewhat bizarre - but this isn't required the first time around
526 # when they select "sms". Then they get a field to fill out with
527 # phone number, then Submit again.
528 msg = _security._phone_util.validate_phone_number(self.phone.data)
529 if msg:
530 self.phone.errors = list()
531 self.phone.errors.append(msg)
532 return False
533
534 return True
535
536
537 class TwoFactorVerifyCodeForm(Form, UserEmailFormMixin):
538 """The Two-factor token validation form"""
539
540 code = StringField(get_form_field_label("code"))
541 submit = SubmitField(get_form_field_label("submitcode"))
542
543 def __init__(self, *args, **kwargs):
544 super(TwoFactorVerifyCodeForm, self).__init__(*args, **kwargs)
545
546 def validate(self):
547 # codes sent by sms or mail will be valid for another window cycle
548 if (
549 self.primary_method == "google_authenticator"
550 or self.primary_method == "authenticator"
551 ):
552 self.window = config_value("TWO_FACTOR_AUTHENTICATOR_VALIDITY")
553 elif self.primary_method == "email" or self.primary_method == "mail":
554 self.window = config_value("TWO_FACTOR_MAIL_VALIDITY")
555 elif self.primary_method == "sms":
556 self.window = config_value("TWO_FACTOR_SMS_VALIDITY")
557 else:
558 return False
559
560 # verify entered token with user's totp secret
561 if not _security._totp_factory.verify_totp(
562 token=self.code.data,
563 totp_secret=self.tf_totp_secret,
564 user=self.user,
565 window=self.window,
566 ):
567 self.code.errors = list()
568 self.code.errors.append(get_message("TWO_FACTOR_INVALID_TOKEN")[0])
569
570 return False
571
572 return True
573
574
575 class TwoFactorVerifyPasswordForm(Form, PasswordFormMixin):
576 """The verify password form"""
577
578 submit = SubmitField(get_form_field_label("verify_password"))
579
580 def validate(self):
581 if not super(TwoFactorVerifyPasswordForm, self).validate():
582 return False
583
584 self.user = current_user
585 if not self.user.verify_and_update_password(self.password.data):
586 self.password.errors.append(get_message("INVALID_PASSWORD")[0])
587 return False
588
589 return True
590
591
592 class TwoFactorRescueForm(Form):
593 """The Two-factor Rescue validation form """
594
595 help_setup = RadioField(
596 "Trouble Accessing Your Account?",
597 choices=[
598 ("lost_device", "Can not access mobile device?"),
599 ("no_mail_access", "Can not access mail account?"),
600 ],
601 )
602 submit = SubmitField(get_form_field_label("submit"))
603
604 def __init__(self, *args, **kwargs):
605 super(TwoFactorRescueForm, self).__init__(*args, **kwargs)
606
607 def validate(self):
608 if not super(TwoFactorRescueForm, self).validate():
609 return False
610 return True
0 """"
1 Copyright 2019 by J. Christopher Wagner (jwag). All rights reserved.
2 :license: MIT, see LICENSE for more details.
3
4 This packages contains OPTIONAL models for various ORMs/databases that can be used
5 to quickly get the required DB models setup.
6
7 These models have the fields for ALL features. This makes it easy for applications
8 to add features w/o a DB migration (and modern DBs are pretty efficient at storing
9 empty values!).
10
11 """
0 """
1 Copyright 2019 by J. Christopher Wagner (jwag). All rights reserved.
2 :license: MIT, see LICENSE for more details.
3
4
5 Complete models for all features when using Flask-SqlAlchemy
6
7 BE AWARE: Once any version of this is shipped no changes can be made - instead
8 a new version needs to be created.
9 """
10
11 import datetime
12 from sqlalchemy import (
13 Boolean,
14 DateTime,
15 Column,
16 Integer,
17 String,
18 UnicodeText,
19 ForeignKey,
20 )
21 from sqlalchemy.ext.declarative import declared_attr
22 from sqlalchemy.orm import relationship
23 from sqlalchemy.sql import func
24
25 from flask_security import RoleMixin, UserMixin
26
27
28 class FsModels(object):
29 """
30 Helper class for model mixins.
31 This records the ``db`` (which is a Flask-SqlAlchemy object) for use in
32 mixins.
33 """
34
35 roles_users = None
36 db = None
37 fs_model_version = 1
38
39 @classmethod
40 def set_db_info(cls, appdb):
41 """ Initialize Model.
42 This needs to be called after the DB object has been created
43 (e.g. db = Sqlalchemy())
44 """
45 cls.db = appdb
46 cls.roles_users = appdb.Table(
47 "roles_users",
48 Column("user_id", Integer(), ForeignKey("user.id")),
49 Column("role_id", Integer(), ForeignKey("role.id")),
50 )
51
52
53 class FsRoleMixin(RoleMixin):
54 id = Column(Integer(), primary_key=True)
55 name = Column(String(80), unique=True, nullable=False)
56 description = Column(String(255))
57 # A comma separated list of strings
58 permissions = Column(UnicodeText, nullable=True)
59 update_datetime = Column(
60 DateTime,
61 nullable=False,
62 server_default=func.now(),
63 onupdate=datetime.datetime.utcnow,
64 )
65
66
67 class FsUserMixin(UserMixin):
68 """ User information
69 """
70
71 # flask_security basic fields
72 id = Column(Integer, primary_key=True)
73 email = Column(String(255), unique=True, nullable=False)
74 # Username is important since shouldn't expose email to other users in most cases.
75 username = Column(String(255))
76 password = Column(String(255), nullable=False)
77 active = Column(Boolean(), nullable=False)
78
79 # Faster token checking
80 fs_uniquifier = Column(String(64), unique=True, nullable=False)
81
82 # confirmable
83 confirmed_at = Column(DateTime())
84
85 # trackable
86 last_login_at = Column(DateTime())
87 current_login_at = Column(DateTime())
88 last_login_ip = Column(String(64))
89 current_login_ip = Column(String(64))
90 login_count = Column(Integer)
91
92 # 2FA
93 tf_primary_method = Column(String(64), nullable=True)
94 tf_totp_secret = Column(String(255), nullable=True)
95 tf_phone_number = Column(String(128), nullable=True)
96
97 @declared_attr
98 def roles(cls):
99 return FsModels.db.relationship(
100 "Role",
101 secondary=FsModels.roles_users,
102 backref=FsModels.db.backref("users", lazy="dynamic"),
103 )
104
105 create_datetime = Column(DateTime, nullable=False, server_default=func.now())
106 update_datetime = Column(
107 DateTime,
108 nullable=False,
109 server_default=func.now(),
110 onupdate=datetime.datetime.utcnow,
111 )
112
113
114 """
115 These are placeholders - not current used
116 """
117
118
119 class FsOauth2ClientMixin(object):
120 """ Oauth2 client """
121
122 id = Column(String(64), primary_key=True)
123
124 @declared_attr
125 def user_id(cls):
126 return Column(
127 Integer, ForeignKey("user.id", ondelete="CASCADE"), nullable=False
128 )
129
130 @declared_attr
131 def user(cls):
132 return relationship("User")
133
134 grant_type = Column(String(32), nullable=False)
135 scopes = Column(UnicodeText(), default="")
136 response_type = Column(UnicodeText, nullable=False, default="")
137 redirect_uris = Column(UnicodeText())
138
139
140 class FsTokenMixin(object):
141 """ (Bearer) Tokens that have been given out """
142
143 id = Column(Integer, primary_key=True)
144
145 @declared_attr
146 def client_id(cls):
147 return Column(
148 Integer, ForeignKey("oauth2_client.id", ondelete="CASCADE"), nullable=False
149 )
150
151 # client = relationship("fs_oauth2_client")
152 @declared_attr
153 def user_id(cls):
154 return Column(
155 Integer, ForeignKey("user.id", ondelete="CASCADE"), nullable=False
156 )
157
158 scopes = Column(UnicodeText(), default="")
159 revoked = Column(Boolean(), nullable=False, default=False)
160 access_token = Column(String(100), unique=True, nullable=False)
161 refresh_token = Column(String(100), unique=True)
162 issued_at = Column(DateTime, nullable=False, server_default=func.now())
163 expires_at = Column(DateTime())
0 """
1 Copyright 2020 by J. Christopher Wagner (jwag). All rights reserved.
2 :license: MIT, see LICENSE for more details.
3
4
5 Complete models for all features when using Flask-SqlAlchemy
6
7 BE AWARE: Once any version of this is shipped no changes can be made - instead
8 a new version needs to be created.
9
10 This is Version 2:
11 - Add support for unified sign in.
12 - Make username unique (but not required).
13 """
14
15 from sqlalchemy import Column, String, Text
16 from sqlalchemy.ext.declarative import declared_attr
17
18
19 from .fsqla import FsModels as FsModelsV1
20 from .fsqla import FsUserMixin as FsUserMixinV1
21 from .fsqla import FsRoleMixin as FsRoleMixinV1
22
23
24 class FsModels(FsModelsV1):
25 fs_model_version = 2
26 pass
27
28
29 class FsRoleMixin(FsRoleMixinV1):
30 pass
31
32
33 class FsUserMixin(FsUserMixinV1):
34 """ User information
35 """
36
37 # Make username unique but not required.
38 username = Column(String(255), unique=True, nullable=True)
39
40 # unified sign in
41 us_totp_secrets = Column(Text, nullable=True)
42 us_phone_number = Column(String(128), nullable=True)
43
44 # This is repeated since I couldn't figure out how to have it reference the
45 # new version of FsModels.
46 @declared_attr
47 def roles(cls):
48 return FsModels.db.relationship(
49 "Role",
50 secondary=FsModels.roles_users,
51 backref=FsModels.db.backref("users", lazy="dynamic"),
52 )
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.passwordless
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security passwordless module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :license: MIT, see LICENSE for more details.
9 """
10
11 from flask import current_app as app
12 from werkzeug.local import LocalProxy
13
14 from .signals import login_instructions_sent
15 from .utils import config_value, get_token_status, url_for_security
16
17 # Convenient references
18 _security = LocalProxy(lambda: app.extensions["security"])
19
20 _datastore = LocalProxy(lambda: _security.datastore)
21
22
23 def send_login_instructions(user):
24 """Sends the login instructions email for the specified user.
25
26 :param user: The user to send the instructions to
27 :param token: The login token
28 """
29 token = generate_login_token(user)
30 login_link = url_for_security("token_login", token=token, _external=True)
31
32 _security._send_mail(
33 config_value("EMAIL_SUBJECT_PASSWORDLESS"),
34 user.email,
35 "login_instructions",
36 user=user,
37 login_link=login_link,
38 )
39
40 login_instructions_sent.send(
41 app._get_current_object(), user=user, login_token=token
42 )
43
44
45 def generate_login_token(user):
46 """Generates a unique login token for the specified user.
47
48 :param user: The user the token belongs to
49 """
50 return _security.login_serializer.dumps([str(user.id)])
51
52
53 def login_token_status(token):
54 """Returns the expired status, invalid status, and user of a login token.
55 For example::
56
57 expired, invalid, user = login_token_status('...')
58
59 :param token: The login token
60 """
61 return get_token_status(token, "login", "LOGIN")
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.phone_util
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Utility class for managing phone numbers
6
7 :copyright: (c) 2020 by J. Christopher Wagner (jwag).
8 :license: MIT, see LICENSE for more details.
9
10 Avoid making 'phonenumbers' a required package unless needed.
11 """
12
13 from .utils import config_value, get_message
14
15
16 class PhoneUtil(object):
17 """
18 Provide parsing and validation for user inputted phone numbers.
19 Subclass this to use a different underlying phone number parsing library.
20
21 To provide your own implementation, pass in the class as ``phone_util_cls``
22 at init time. Your class will be instantiated once prior to the first
23 request being handled.
24
25 .. versionadded:: 3.4.0
26 """
27
28 def validate_phone_number(self, input_data):
29 """ Return ``None`` if a valid phone number else an error message. """
30 import phonenumbers
31
32 try:
33 z = phonenumbers.parse(
34 input_data, region=config_value("PHONE_REGION_DEFAULT")
35 )
36 if phonenumbers.is_valid_number(z):
37 return None
38 except phonenumbers.phonenumberutil.NumberParseException:
39 pass
40 return get_message("PHONE_INVALID")[0]
41
42 def get_canonical_form(self, input_data):
43 """ Validate and return a canonical form to be stored in DB
44 and compared against.
45 Returns ``None`` if input isn't a valid phone number.
46 """
47 import phonenumbers
48
49 try:
50 z = phonenumbers.parse(
51 input_data, region=config_value("PHONE_REGION_DEFAULT")
52 )
53 if phonenumbers.is_valid_number(z):
54 return phonenumbers.format_number(
55 z, phonenumbers.PhoneNumberFormat.E164
56 )
57 return None
58 except phonenumbers.phonenumberutil.NumberParseException:
59 return None
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.quart_compat
3 ~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security quart compatibility modiles
6
7 :copyright: (c) 2019 by Shinon.
8 :license: MIT, see LICENSE for more details.
9
10 This modules tests whether we are using quart or not
11 we can test if the name of the imported flask is: quart.flask_patch
12 """
13 import flask
14
15 if "quart." in flask.__name__ or hasattr(flask, "_quart_patched"): # pragma: no cover
16 is_quart = True
17 else:
18 is_quart = False
19
20
21 @property
22 def best(self): # pragma: no cover
23 options = sorted(
24 self.options,
25 key=lambda option: (option.value != "*", option.quality, option.value),
26 reverse=True,
27 )
28 return options[0].value
29
30
31 def get_quart_status():
32 """
33 Tests if we are using Quart Patched Flask or Vanilla Flask.
34 :return: boolean value determining if it is quart patched flask or not
35 """
36 return is_quart
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.recoverable
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security recoverable module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :license: MIT, see LICENSE for more details.
9 """
10
11 from flask import current_app as app
12 from werkzeug.local import LocalProxy
13
14 from .signals import password_reset, reset_password_instructions_sent
15 from .utils import (
16 config_value,
17 get_token_status,
18 hash_data,
19 hash_password,
20 url_for_security,
21 verify_hash,
22 )
23
24 # Convenient references
25 _security = LocalProxy(lambda: app.extensions["security"])
26
27 _datastore = LocalProxy(lambda: _security.datastore)
28
29
30 def send_reset_password_instructions(user):
31 """Sends the reset password instructions email for the specified user.
32
33 :param user: The user to send the instructions to
34 """
35 token = generate_reset_password_token(user)
36 reset_link = url_for_security("reset_password", token=token, _external=True)
37
38 if config_value("SEND_PASSWORD_RESET_EMAIL"):
39 _security._send_mail(
40 config_value("EMAIL_SUBJECT_PASSWORD_RESET"),
41 user.email,
42 "reset_instructions",
43 user=user,
44 reset_link=reset_link,
45 )
46
47 reset_password_instructions_sent.send(
48 app._get_current_object(), user=user, token=token
49 )
50
51
52 def send_password_reset_notice(user):
53 """Sends the password reset notice email for the specified user.
54
55 :param user: The user to send the notice to
56 """
57 if config_value("SEND_PASSWORD_RESET_NOTICE_EMAIL"):
58 _security._send_mail(
59 config_value("EMAIL_SUBJECT_PASSWORD_NOTICE"),
60 user.email,
61 "reset_notice",
62 user=user,
63 )
64
65
66 def generate_reset_password_token(user):
67 """Generates a unique reset password token for the specified user.
68
69 :param user: The user to work with
70 """
71 password_hash = hash_data(user.password) if user.password else None
72 data = [str(user.id), password_hash]
73 return _security.reset_serializer.dumps(data)
74
75
76 def reset_password_token_status(token):
77 """Returns the expired status, invalid status, and user of a password reset
78 token. For example::
79
80 expired, invalid, user, data = reset_password_token_status('...')
81
82 :param token: The password reset token
83 """
84 expired, invalid, user, data = get_token_status(
85 token, "reset", "RESET_PASSWORD", return_data=True
86 )
87 if not invalid and user:
88 if user.password:
89 if not verify_hash(data[1], user.password):
90 invalid = True
91
92 return expired, invalid, user
93
94
95 def update_password(user, password):
96 """Update the specified user's password
97
98 :param user: The user to update_password
99 :param password: The unhashed new password
100 """
101 user.password = hash_password(password)
102 if config_value("BACKWARDS_COMPAT_AUTH_TOKEN_INVALID"):
103 _datastore.set_uniquifier(user)
104 _datastore.put(user)
105 send_password_reset_notice(user)
106 password_reset.send(app._get_current_object(), user=user)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.registerable
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security registerable module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10 """
11
12 import uuid
13
14 from flask import current_app as app
15 from werkzeug.local import LocalProxy
16
17 from .confirmable import generate_confirmation_link
18 from .signals import user_registered
19 from .utils import config_value, do_flash, get_message, hash_password
20
21 # Convenient references
22 _security = LocalProxy(lambda: app.extensions["security"])
23
24 _datastore = LocalProxy(lambda: _security.datastore)
25
26
27 def register_user(registration_form):
28 """
29 Calls datastore to create user, triggers post-registration logic
30 (e.g. sending confirmation link, sending registration mail)
31 :param registration_form: form with user registration data
32 :return: user instance
33 """
34
35 user_model_kwargs = registration_form.to_dict(only_user=True)
36
37 if not user_model_kwargs["password"]:
38 # For no password - set an unguessable password.
39 # Since we still allow 'plaintext' as a password scheme - can't use a simple
40 # sentinel.
41 user_model_kwargs["password"] = "NoPassword-" + uuid.uuid4().hex
42
43 user_model_kwargs["password"] = hash_password(user_model_kwargs["password"])
44 user = _datastore.create_user(**user_model_kwargs)
45 # This has always been here - but should probably be removed since in all other
46 # cases we use a 'after_this_request(commit)'. Seems like this would break quart
47 # compat as well?
48 _datastore.commit()
49
50 confirmation_link, token = None, None
51 if _security.confirmable:
52 confirmation_link, token = generate_confirmation_link(user)
53 do_flash(*get_message("CONFIRM_REGISTRATION", email=user.email))
54
55 user_registered.send(
56 app._get_current_object(),
57 user=user,
58 confirm_token=token,
59 form_data=registration_form.to_dict(only_user=False),
60 )
61
62 if config_value("SEND_REGISTER_EMAIL"):
63 _security._send_mail(
64 config_value("EMAIL_SUBJECT_REGISTER"),
65 user.email,
66 "welcome",
67 user=user,
68 confirmation_link=confirmation_link,
69 )
70
71 return user
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.signals
3 ~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security signals module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10 """
11
12 import blinker
13
14 signals = blinker.Namespace()
15
16 user_authenticated = signals.signal("user-authenticated")
17
18 user_registered = signals.signal("user-registered")
19
20 user_confirmed = signals.signal("user-confirmed")
21
22 confirm_instructions_sent = signals.signal("confirm-instructions-sent")
23
24 login_instructions_sent = signals.signal("login-instructions-sent")
25
26 password_reset = signals.signal("password-reset")
27
28 password_changed = signals.signal("password-changed")
29
30 reset_password_instructions_sent = signals.signal("password-reset-instructions-sent")
31
32 tf_code_confirmed = signals.signal("tf-code-confirmed")
33
34 tf_profile_changed = signals.signal("tf-profile-changed")
35
36 tf_security_token_sent = signals.signal("tf-security-token-sent")
37
38 tf_disabled = signals.signal("tf-disabled")
39
40 us_security_token_sent = signals.signal("us-security-token-sent")
41
42 us_profile_changed = signals.signal("us-profile-changed")
0 {% macro render_field_with_errors(field) %}
1 <p>
2 {{ field.label }} {{ field(**kwargs)|safe }}
3 {% if field.errors %}
4 <ul>
5 {% for error in field.errors %}
6 <li>{{ error }}</li>
7 {% endfor %}
8 </ul>
9 {% endif %}
10 </p>
11 {% endmacro %}
12
13 {% macro render_field(field) %}
14 <p>{{ field(**kwargs)|safe }}</p>
15 {% endmacro %}
16
17 {% macro render_field_errors(field) %}
18 <p>
19 {% if field and field.errors %}
20 <ul>
21 {% for error in field.errors %}
22 <li>{{ error }}</li>
23 {% endfor %}
24 </ul>
25 {% endif %}
26 </p>
27 {% endmacro %}
0 {% if security.registerable or security.recoverable or security.confirmable or security.unified_signin %}
1 <h2>{{ _('Menu') }}</h2>
2 <ul>
3 {% if not skip_login_menu %}
4 <li><a href="{{ url_for_security('login') }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}">{{ _('Login') }}</a></li>
5 {% endif %}
6 {% if security.unified_signin and not skip_login_menu %}
7 <li><a href="{{ url_for_security('us_signin') }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}">{{ _("Unified Sign In") }}</a><br/></li>
8 {% endif %}
9 {% if security.registerable %}
10 <li><a href="{{ url_for_security('register') }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}">{{ _('Register') }}</a><br/></li>
11 {% endif %}
12 {% if security.recoverable %}
13 <li><a href="{{ url_for_security('forgot_password') }}">{{ _('Forgot password') }}</a><br/></li>
14 {% endif %}
15 {% if security.confirmable %}
16 <li><a href="{{ url_for_security('send_confirmation') }}">{{ _('Confirm account') }}</a></li>
17 {% endif %}
18 </ul>
19 {% endif %}
0 {%- with messages = get_flashed_messages(with_categories=true) -%}
1 {% if messages %}
2 <ul class="flashes">
3 {% for category, message in messages %}
4 <li class="{{ category }}">{{ message }}</li>
5 {% endfor %}
6 </ul>
7 {% endif %}
8 {%- endwith %}
0 {% block doc -%}
1 <!DOCTYPE html>
2 <html{% block html_attribs %}{% endblock html_attribs %}>
3 {%- block html %}
4 <head>
5 {%- block head %}
6 <title>{% block title %}{{ title|default }}{% endblock title %}</title>
7
8 {%- block metas %}
9 <meta name="viewport" content="width=device-width, initial-scale=1.0">
10 {%- endblock metas %}
11
12 {%- block styles %}
13 {%- endblock styles %}
14 {%- endblock head %}
15 </head>
16 <body{% block body_attribs %}{% endblock body_attribs %}>
17 {% block body -%}
18 {% block navbar %}
19 {%- endblock navbar %}
20 {% block content -%}
21 {%- endblock content %}
22
23 {% block scripts %}
24 {%- endblock scripts %}
25 {%- endblock body %}
26 </body>
27 {%- endblock html %}
28 </html>
29 {% endblock doc -%}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Change password') }}</h1>
6 <form action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
7 {{ change_password_form.hidden_tag() }}
8 {{ render_field_with_errors(change_password_form.password) }}
9 {{ render_field_with_errors(change_password_form.new_password) }}
10 {{ render_field_with_errors(change_password_form.new_password_confirm) }}
11 {{ render_field(change_password_form.submit) }}
12 </form>
13 {% endblock %}
0 <p>{{ _('Your password has been changed.') }}</p>
1 {% if security.recoverable %}
2 <p>{{ _('If you did not change your password,') }} <a href="{{ url_for_security('forgot_password', _external=True) }}">{{ _('click here to reset it') }}</a>.</p>
3 {% endif %}
0 {{ _('Your password has been changed') }}
1 {% if security.recoverable %}
2 {{ _('If you did not change your password, click the link below to reset it.') }}
3 {{ url_for_security('forgot_password', _external=True) }}
4 {% endif %}
0 <p>{{ _('Please confirm your email through the link below:') }}</p>
1
2 <p><a href="{{ confirmation_link }}">{{ _('Confirm my account') }}</a></p>
0 {{ _('Please confirm your email through the link below:') }}
1
2 {{ confirmation_link }}
0 <p>{{ _('Welcome %(email)s!', email=user.email) }}</p>
1
2 <p>{{ _('You can log into your account through the link below:') }}</p>
3
4 <p><a href="{{ login_link }}">{{ _('Login now') }}</a></p>
0 {{ _('Welcome %(email)s!', email=user.email) }}
1
2 {{ _('You can log into your account through the link below:') }}
3
4 {{ login_link }}
0 <p><a href="{{ reset_link }}">{{ _('Click here to reset your password') }}</a></p>
0 {{ _('Click the link below to reset your password:') }}
1
2 {{ reset_link }}
0 <p>{{ _("Welcome") }} {{ username }}!</p>
1
2 <p>{{ _("You can log into your account using the following code:") }} {{ token }}</p>
0 {{ _("Welcome") }} {{ username }}!
1
2 {{ _("You can log into your account using the following code:") }} {{ token }}
0 <p> {{ user.email }} {{ _("can not access mail account") }}</p>
0 {{ user.email }} {{ _("can not access mail account") }}
0 <p>{{ _("Welcome") }} {{ username }}!</p>
1
2 <p>{{ _("You can sign into your account using the following code:") }} {{ token }}</p>
3
4 {% if login_link %}
5 <p>{{ _("Or use the the link below:") }}</p>
6
7 <p><a href="{{ login_link }}">{{ _("Sign In") }}</a></p>
8 {% endif %}
0 {{ _("Welcome") }} {{ username }}!
1
2 {{ _("You can sign into your account using the following code:") }} {{ token }}
3
4 {% if login_link %}
5
6 {{ _("Or use the link below:") }}
7
8 {{ login_link }}
9 {% endif %}
0 <p>{{ _('Welcome %(email)s!', email=user.email) }}</p>
1
2 {% if security.confirmable %}
3 <p>{{ _('You can confirm your email through the link below:') }}</p>
4
5 <p><a href="{{ confirmation_link }}">{{ _('Confirm my account') }}</a></p>
6 {% endif %}
0 {{ _('Welcome %(email)s!', email=user.email) }}
1
2 {% if security.confirmable %}
3 {{ _('You can confirm your email through the link below:') }}
4
5 {{ confirmation_link }}
6 {% endif %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Send password reset instructions') }}</h1>
6 <form action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
7 {{ forgot_password_form.hidden_tag() }}
8 {{ render_field_with_errors(forgot_password_form.email) }}
9 {{ render_field(forgot_password_form.submit) }}
10 </form>
11 {% include "security/_menu.html" %}
12 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field, render_field_errors %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Login') }}</h1>
6 <form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
7 {{ login_user_form.hidden_tag() }}
8 {{ render_field_with_errors(login_user_form.email) }}
9 {{ render_field_with_errors(login_user_form.password) }}
10 {{ render_field_with_errors(login_user_form.remember) }}
11 {{ render_field_errors(login_user_form.csrf_token) }}
12 {{ render_field(login_user_form.submit) }}
13 </form>
14 {% include "security/_menu.html" %}
15 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Register') }}</h1>
6 <form action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
7 {{ register_user_form.hidden_tag() }}
8 {{ render_field_with_errors(register_user_form.email) }}
9 {{ render_field_with_errors(register_user_form.password) }}
10 {% if register_user_form.password_confirm %}
11 {{ render_field_with_errors(register_user_form.password_confirm) }}
12 {% endif %}
13 {{ render_field(register_user_form.submit) }}
14 </form>
15 {% include "security/_menu.html" %}
16 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Reset password') }}</h1>
6 <form action="{{ url_for_security('reset_password', token=reset_password_token) }}" method="POST" name="reset_password_form">
7 {{ reset_password_form.hidden_tag() }}
8 {{ render_field_with_errors(reset_password_form.password) }}
9 {{ render_field_with_errors(reset_password_form.password_confirm) }}
10 {{ render_field(reset_password_form.submit) }}
11 </form>
12 {% include "security/_menu.html" %}
13 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Resend confirmation instructions') }}</h1>
6 <form action="{{ url_for_security('send_confirmation') }}" method="POST" name="send_confirmation_form">
7 {{ send_confirmation_form.hidden_tag() }}
8 {{ render_field_with_errors(send_confirmation_form.email) }}
9 {{ render_field(send_confirmation_form.submit) }}
10 </form>
11 {% include "security/_menu.html" %}
12 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _('Login') }}</h1>
6 <form action="{{ url_for_security('login') }}" method="POST" name="send_login_form">
7 {{ send_login_form.hidden_tag() }}
8 {{ render_field_with_errors(send_login_form.email) }}
9 {{ render_field(send_login_form.submit) }}
10 </form>
11 {% include "security/_menu.html" %}
12 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field, render_field_no_label, render_field_errors %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Two-factor authentication adds an extra layer of security to your account") }}</h1>
6 <h2>{{ _("In addition to your username and password, you'll need to use a code that we will send you") }}</h2>
7 <form action="{{ url_for_security("two_factor_setup") }}" method="POST" name="two_factor_setup_form">
8 {{ two_factor_setup_form.hidden_tag() }}
9 {% for subfield in two_factor_setup_form.setup %}
10 {% if subfield.data in choices %}
11 {{ render_field_with_errors(subfield) }}
12 {% endif %}
13 {% endfor %}
14 {{ render_field_errors(two_factor_setup_form.setup) }}
15 {{ render_field(two_factor_setup_form.submit) }}
16 {% if chosen_method=="email" and chosen_method in choices %}
17 <p>{{ _("To complete logging in, please enter the code sent to your mail") }}</p>
18 {% endif %}
19 {% if chosen_method=="authenticator" and chosen_method in choices %}
20 <p>{{ _("Open your authenticator app on your device and scan the following qrcode to start receiving codes:") }}</p>
21 <p><img alt="{{ _("Two factor authentication code") }}" id="qrcode" src="{{ url_for_security("two_factor_qrcode") }}"></p>
22 {% endif %}
23 {% if chosen_method=="sms" and chosen_method in choices %}
24 <p>{{ _("To Which Phone Number Should We Send Code To?") }}</p>
25 {{ two_factor_setup_form.hidden_tag() }}
26 {{ render_field_with_errors(two_factor_setup_form.phone, placeholder="enter phone number") }}
27 {{ render_field(two_factor_setup_form.submit) }}
28 {% endif %}
29 </form>
30 <form action="{{ url_for_security("two_factor_token_validation") }}" method="POST"
31 name="two_factor_verify_code_form">
32 {{ two_factor_verify_code_form.hidden_tag() }}
33 {{ render_field_with_errors(two_factor_verify_code_form.code) }}
34 {{ render_field(two_factor_verify_code_form.submit) }}
35 </form>
36 {% include "security/_menu.html" %}
37 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Two-factor Authentication") }}</h1>
6 <h2>{{ _("Please enter your authentication code") }}</h2>
7 <form action="{{ url_for_security("two_factor_token_validation") }}" method="POST"
8 name="two_factor_verify_code_form">
9 {{ two_factor_verify_code_form.hidden_tag() }}
10 {{ render_field_with_errors(two_factor_verify_code_form.code, placeholder="enter code") }}
11 {{ render_field(two_factor_verify_code_form.submit) }}
12 </form>
13 <form action="{{ url_for_security("two_factor_rescue") }}" method="POST" name="two_factor_rescue_form">
14 {{ two_factor_rescue_form.hidden_tag() }}
15 {{ render_field_with_errors(two_factor_rescue_form.help_setup) }}
16 {% if problem=="lost_device" %}
17 <p>{{ _("The code for authentication was sent to your email address") }}</p>
18 {% endif %}
19 {% if problem=="no_mail_access" %}
20 <p>{{ _("A mail was sent to us in order to reset your application account") }}</p>
21 {% endif %}
22 {{ render_field(two_factor_rescue_form.submit) }}
23 </form>
24 {% include "security/_menu.html" %}
25 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Please Enter Your Password") }}</h1>
6 <form action="{{ url_for_security("two_factor_verify_password") }}" method="POST"
7 name="two_factor_verify_password_form">
8 {{ two_factor_verify_password_form.hidden_tag() }}
9 {{ render_field_with_errors(two_factor_verify_password_form.password, placeholder="enter password") }}
10 {{ render_field(two_factor_verify_password_form.submit) }}
11 </form>
12 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field, render_field_errors %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Setup Unified Sign In options") }}</h1>
6 <form action="{{ url_for_security("us_setup") }}" method="POST"
7 name="us_setup_form">
8 {{ us_setup_form.hidden_tag() }}
9 {% if setup_methods %}
10 <p>Currently Active options:
11 {% if active_methods %}
12 {{ ", ".join(active_methods) }}
13 {% else %}
14 None.
15 {% endif %}
16 </p>
17 {% for subfield in us_setup_form.chosen_method %}
18 {% if subfield.data in available_methods %}
19 {{ render_field_with_errors(subfield) }}
20 {% endif %}
21 {% endfor %}
22 {{ render_field_errors(us_setup_form.chosen_method) }}
23 {% if "sms" in available_methods %}
24 {{ render_field_with_errors(us_setup_form.phone) }}
25 {% endif %}
26 {% if chosen_method == "authenticator" %}
27 <p>{{ _("Open your authenticator app on your device and scan the following qrcode to start receiving codes:") }}</p>
28 <p><img alt="{{ _("Passwordless QRCode") }}" id="qrcode" src="{{ url_for_security("us_qrcode", token=state) }}"></p>
29 {% endif %}
30 {% if code_sent %}
31 <p>{{ _("Code has been sent") }}
32 {% endif %}
33 {{ render_field(us_setup_form.submit) }}
34 {% else %}
35 <h3>{{ _("No methods have been enabled - nothing to setup") }}</h3>
36 {% endif %}
37 </form>
38 {% if state %}
39 <form action="{{ url_for_security("us_setup_validate", token=state) }}" method="POST"
40 name="us_setup_validate_form">
41 {{ us_setup_validate_form.hidden_tag() }}
42 {{ render_field_with_errors(us_setup_validate_form.passcode) }}
43 {{ render_field(us_setup_validate_form.submit) }}
44 </form>
45 {% endif %}
46 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field, render_field_errors %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Sign In") }}</h1>
6 <form action="{{ url_for_security("us_signin") }}" method="POST"
7 name="us_signin_form">
8 {{ us_signin_form.hidden_tag() }}
9 {{ render_field_with_errors(us_signin_form.identity) }}
10 {{ render_field_with_errors(us_signin_form.passcode) }}
11 {{ render_field_with_errors(us_signin_form.remember) }}
12 {{ render_field(us_signin_form.submit) }}
13 {% if code_methods %}
14 <h4>{{ _("Request one-time code be sent") }}</h4>
15 {% for subfield in us_signin_form.chosen_method %}
16 {% if subfield.data in code_methods %}
17 {{ render_field_with_errors(subfield) }}
18 {% endif %}
19 {% endfor %}
20 {{ render_field_errors(us_signin_form.chosen_method) }}
21 {% if code_sent %}
22 <p>{{ _("Code has been sent") }}
23 {% endif %}
24 {{ render_field(us_signin_form.submit_send_code, formaction=url_for_security('us_signin_send_code')) }}
25 {% endif %}
26 </form>
27 {% include "security/_menu.html" %}
28 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field, render_field_errors %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Please re-authenticate") }}</h1>
6 <form action="{{ url_for_security("us_verify") }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}" method="POST"
7 name="us_verify_form">
8 {{ us_verify_form.hidden_tag() }}
9 {{ render_field_with_errors(us_verify_form.passcode) }}
10 {{ render_field(us_verify_form.submit) }}
11 {% if code_methods %}
12 <h4>{{ _("Request one-time code be sent") }}</h4>
13 {% for subfield in us_verify_form.chosen_method %}
14 {% if subfield.data in code_methods %}
15 {{ render_field_with_errors(subfield) }}
16 {% endif %}
17 {% endfor %}
18 {{ render_field_errors(us_verify_form.chosen_method) }}
19 {% if code_sent %}
20 <p>{{ _("Code has been sent") }}
21 {% endif %}
22 {{ render_field(us_verify_form.submit_send_code, formaction=send_code_to) }}
23 {% endif %}
24 </form>
25 {% include "security/_menu.html" %}
26 {% endblock %}
0 {% extends "security/base.html" %}
1 {% from "security/_macros.html" import render_field_with_errors, render_field %}
2
3 {% block content %}
4 {% include "security/_messages.html" %}
5 <h1>{{ _("Please Enter Your Password") }}</h1>
6 <form action="{{ url_for_security("verify") }}{% if 'next' in request.args %}?next={{ request.args.next|urlencode }}{% endif %}" method="POST"
7 name="verify_form">
8 {{ verify_form.hidden_tag() }}
9 {{ render_field_with_errors(verify_form.password) }}
10 {{ render_field(verify_form.submit) }}
11 </form>
12 {% endblock %}
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.totp
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security TOTP (Timed-One-Time-Passwords) module
6
7 :copyright: (c) 2019 by J. Christopher Wagner (jwag).
8 :license: MIT, see LICENSE for more details.
9 """
10
11 from passlib.totp import TOTP, TokenError
12
13
14 class Totp(object):
15 """ Encapsulate usage of Passlib TOTP functionality.
16
17 Flask-Security doesn't implement any replay-attack protection out of the box
18 as suggested by:
19 https://passlib.readthedocs.io/en/stable/narr/totp-tutorial.html#match-verify
20
21 Subclass this and implement the get/set last_counter methods. Your subclass can
22 be registered at Flask-Security creation/initialization time.
23
24 .. versionadded:: 3.4.0
25
26 """
27
28 def __init__(self, secrets, issuer):
29 """ Initialize a totp factory.
30 secrets are used to encrypt the per-user totp_secret on disk.
31 """
32 # This should be a dict with at least one entry
33 if not isinstance(secrets, dict) or len(secrets) < 1:
34 raise ValueError("secrets needs to be a dict with at least one entry")
35 self._totp = TOTP.using(issuer=issuer, secrets=secrets)
36
37 def generate_totp_password(self, totp_secret):
38 """Get time-based one-time password on the basis of given secret and time
39 :param totp_secret: the unique shared secret of the user
40 """
41 return self._totp.from_source(totp_secret).generate().token
42
43 def generate_totp_secret(self):
44 """ Create new user-unique totp_secret.
45
46 We return an encrypted json string so that when sent in a cookie or
47 sent to DB - it is encrypted.
48
49 """
50 return self._totp.new().to_json(encrypt=True)
51
52 def verify_totp(self, token, totp_secret, user, window=0):
53 """ Verifies token for specific user.
54
55 :param token: token to be check against user's secret
56 :param totp_secret: the unique shared secret of the user
57 :param user: User model
58 :param window: optional. How far backward and forward in time to search
59 for a match. Measured in seconds.
60 :return: True if match
61 """
62
63 # TODO - in old implementation using onetimepass window was described
64 # as 'compensate for clock skew) and 'interval_length' would say how long
65 # the token is good for.
66 # In passlib - 'window' means how far back and forward to look and 'clock_skew'
67 # is specifically for well, clock slew.
68 try:
69 tmatch = self._totp.verify(
70 token,
71 totp_secret,
72 window=window,
73 last_counter=self.get_last_counter(user),
74 )
75 self.set_last_counter(user, tmatch)
76 return True
77
78 except TokenError:
79 return False
80
81 def get_totp_uri(self, username, totp_secret):
82 """ Generate provisioning url for use with the qrcode
83 scanner built into the app
84
85 :param username: username/email of the current user
86 :param totp_secret: a unique shared secret of the user
87 """
88 tp = self._totp.from_source(totp_secret)
89 return tp.to_uri(username)
90
91 def get_last_counter(self, user):
92 """ Implement this to fetch stored last_counter from cache.
93
94 :param user: User model
95 :return: last_counter as stored in set_last_counter()
96 """
97 return None
98
99 def set_last_counter(self, user, tmatch):
100 """ Implement this to cache last_counter.
101
102 :param user: User model
103 :param tmatch: a TotpMatch as returned from totp.verify()
104 """
105 pass
0 # Catalan (Spain) translations for Flask-Security.
1 # Copyright (C) 2017 DINSIC
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # Orestes Sanchez <[email protected]>, 2018.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 3.1.0\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2019-06-16 00:12+0200\n"
12 "Last-Translator: Orestes Sanchez <[email protected]>\n"
13 "Language: ca_ES\n"
14 "Language-Team: \n"
15 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Per poder veure la pàgina sol·licitada és necessari iniciar la sessió"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Benvingut"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Si us plau, confirmeu el vostre correu electrònic"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Instruccions d'inici de la sessió"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "S'ha restablit la teva contrasenya"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "S'ha canviat la teva contrasenya"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instruccions de recuperació de la contrasenya"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "No tens permís d'accés per a consultar aquest recurs."
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Has d'iniciar una nova sessió per tal d'accedir a aquesta pàgina."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr ""
85 "Moltes gràcies. S'ha enviat un correu electrònic a %(email)s amb "
86 "instruccions per confirmar el teu compte."
87
88 #: flask_security/core.py:296
89 msgid "Thank you. Your email has been confirmed."
90 msgstr "Moltes gràcies. S'ha confirmat el teu correu electrònic."
91
92 #: flask_security/core.py:297
93 msgid "Your email has already been confirmed."
94 msgstr "El teu correu electrònic ja s'havia confirmat."
95
96 #: flask_security/core.py:298
97 msgid "Invalid confirmation token."
98 msgstr "Token de confirmació no vàlid."
99
100 #: flask_security/core.py:300
101 #, python-format
102 msgid "%(email)s is already associated with an account."
103 msgstr "%(email)s ja es associat amb un compte."
104
105 #: flask_security/core.py:303
106 msgid "Password does not match"
107 msgstr "La contrasenya no coincideix"
108
109 #: flask_security/core.py:304
110 msgid "Passwords do not match"
111 msgstr "Les contrasenyes no coincideixen"
112
113 #: flask_security/core.py:305
114 msgid "Redirections outside the domain are forbidden"
115 msgstr "Les redireccions a llocs web externes s'han prohibit"
116
117 #: flask_security/core.py:307
118 #, python-format
119 msgid "Instructions to reset your password have been sent to %(email)s."
120 msgstr ""
121 "Les instruccions per restablir la teva contrasenya s'han enviat a "
122 "%(email)s."
123
124 #: flask_security/core.py:311
125 #, python-format
126 msgid ""
127 "You did not reset your password within %(within)s. New instructions have "
128 "been sent to %(email)s."
129 msgstr ""
130 "No vas restablir la teva contrasenya abans de %(within)s. S'han enviat "
131 "noves instruccions a %(email)s."
132
133 #: flask_security/core.py:317
134 msgid "Invalid reset password token."
135 msgstr "El token per restablir la contrasenya no és vàlid."
136
137 #: flask_security/core.py:318
138 msgid "Email requires confirmation."
139 msgstr "El correu electrònic requereix d'una confirmació."
140
141 #: flask_security/core.py:320
142 #, python-format
143 msgid "Confirmation instructions have been sent to %(email)s."
144 msgstr "Les instruccions de confirmació s'han enviat a %(email)s."
145
146 #: flask_security/core.py:324
147 #, python-format
148 msgid ""
149 "You did not confirm your email within %(within)s. New instructions to "
150 "confirm your email have been sent to %(email)s."
151 msgstr ""
152 "No vas confirmar el teu correu electrònic abans de %(within)s. S'han "
153 "enviat noves instruccions a %(email)s."
154
155 #: flask_security/core.py:332
156 #, python-format
157 msgid ""
158 "You did not login within %(within)s. New instructions to login have been "
159 "sent to %(email)s."
160 msgstr ""
161 "No vas iniciar la sessió abans de %(within)s. S'han enviat noves "
162 "instruccions a %(email)s."
163
164 #: flask_security/core.py:339
165 #, python-format
166 msgid "Instructions to login have been sent to %(email)s."
167 msgstr "S'han enviat instruccions per l'inici de sessió a %(email)s."
168
169 #: flask_security/core.py:342
170 msgid "Invalid login token."
171 msgstr "Token de d'inici de sessió no vàlid."
172
173 #: flask_security/core.py:343
174 msgid "Account is disabled."
175 msgstr "el compte està desactivat."
176
177 #: flask_security/core.py:344
178 msgid "Email not provided"
179 msgstr "No s'ha inclòs el correu electrònic"
180
181 #: flask_security/core.py:345
182 msgid "Invalid email address"
183 msgstr "Adreça de correu electrònic no vàlida"
184
185 #: flask_security/core.py:346
186 #, fuzzy
187 msgid "Invalid code"
188 msgstr "Contrasenya no vàlida"
189
190 #: flask_security/core.py:347
191 msgid "Password not provided"
192 msgstr "No s'ha inclòs la contrasenya"
193
194 #: flask_security/core.py:348
195 msgid "No password is set for this user"
196 msgstr "No hi ha cap contrasenya per a l'usuari"
197
198 #: flask_security/core.py:350
199 #, fuzzy, python-format
200 msgid "Password must be at least %(length)s characters"
201 msgstr "La contrasenya ha de tenir al menys 6 caràcters"
202
203 #: flask_security/core.py:353
204 msgid "Password not complex enough"
205 msgstr ""
206
207 #: flask_security/core.py:354
208 msgid "Password on breached list"
209 msgstr ""
210
211 #: flask_security/core.py:356
212 msgid "Failed to contact breached passwords site"
213 msgstr ""
214
215 #: flask_security/core.py:359
216 msgid "Phone number not valid e.g. missing country code"
217 msgstr ""
218
219 #: flask_security/core.py:360
220 msgid "Specified user does not exist"
221 msgstr "L'usuari no existeix"
222
223 #: flask_security/core.py:361
224 msgid "Invalid password"
225 msgstr "Contrasenya no vàlida"
226
227 #: flask_security/core.py:362
228 msgid "Password or code submitted is not valid"
229 msgstr ""
230
231 #: flask_security/core.py:363
232 msgid "You have successfully logged in."
233 msgstr "La sessió s'ha iniciat amb èxit."
234
235 #: flask_security/core.py:364
236 msgid "Forgot password?"
237 msgstr "Has oblidat la teva contrasenya?"
238
239 #: flask_security/core.py:366
240 msgid ""
241 "You successfully reset your password and you have been logged in "
242 "automatically."
243 msgstr ""
244 "Has restablert la teva contrasenya amb èxit i s'ha iniciat la sessió "
245 "automàticament."
246
247 #: flask_security/core.py:373
248 msgid "Your new password must be different than your previous password."
249 msgstr "La nova contrasenya ha de ser diferent de l'anterior."
250
251 #: flask_security/core.py:376
252 msgid "You successfully changed your password."
253 msgstr "La teva contrasenya s'ha modificat amb èxit."
254
255 #: flask_security/core.py:377
256 msgid "Please log in to access this page."
257 msgstr "Has d'iniciar sessió per tal d'accedir a aquesta pàgina."
258
259 #: flask_security/core.py:378
260 msgid "Please reauthenticate to access this page."
261 msgstr "Has d'iniciar una nova sessió per tal d'accedir a aquesta pàgina."
262
263 #: flask_security/core.py:379
264 msgid "Reauthentication successful"
265 msgstr ""
266
267 #: flask_security/core.py:381
268 msgid "You can only access this endpoint when not logged in."
269 msgstr ""
270
271 #: flask_security/core.py:384
272 msgid "Failed to send code. Please try again later"
273 msgstr ""
274
275 #: flask_security/core.py:385
276 msgid "Invalid Token"
277 msgstr ""
278
279 #: flask_security/core.py:386
280 msgid "Your token has been confirmed"
281 msgstr ""
282
283 #: flask_security/core.py:388
284 msgid "You successfully changed your two-factor method."
285 msgstr ""
286
287 #: flask_security/core.py:392
288 msgid "You successfully confirmed password"
289 msgstr ""
290
291 #: flask_security/core.py:396
292 msgid "Password confirmation is needed in order to access page"
293 msgstr ""
294
295 #: flask_security/core.py:400
296 msgid "You currently do not have permissions to access this page"
297 msgstr ""
298
299 #: flask_security/core.py:403
300 msgid "Marked method is not valid"
301 msgstr ""
302
303 #: flask_security/core.py:405
304 msgid "You successfully disabled two factor authorization."
305 msgstr ""
306
307 #: flask_security/core.py:408
308 msgid "Requested method is not valid"
309 msgstr ""
310
311 #: flask_security/core.py:410
312 #, python-format
313 msgid "Setup must be completed within %(within)s. Please start over."
314 msgstr ""
315
316 #: flask_security/core.py:413
317 msgid "Unified sign in setup successful"
318 msgstr ""
319
320 #: flask_security/core.py:414
321 msgid "You must specify a valid identity to sign in"
322 msgstr ""
323
324 #: flask_security/core.py:415
325 #, python-format
326 msgid "Use this code to sign in: %(code)s."
327 msgstr ""
328
329 #: flask_security/forms.py:50
330 msgid "Email Address"
331 msgstr "Correu electrònic"
332
333 #: flask_security/forms.py:51
334 msgid "Password"
335 msgstr "Contrasenya"
336
337 #: flask_security/forms.py:52
338 msgid "Remember Me"
339 msgstr "Recorda'm"
340
341 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
342 #: flask_security/templates/security/login_user.html:6
343 #: flask_security/templates/security/send_login.html:6
344 msgid "Login"
345 msgstr "Iniciar sessió"
346
347 #: flask_security/forms.py:54
348 #: flask_security/templates/security/email/us_instructions.html:8
349 #: flask_security/templates/security/us_signin.html:6
350 msgid "Sign In"
351 msgstr ""
352
353 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
354 #: flask_security/templates/security/register_user.html:6
355 msgid "Register"
356 msgstr "Registrar-se"
357
358 #: flask_security/forms.py:56
359 msgid "Resend Confirmation Instructions"
360 msgstr "Reenviar les instruccions de confirmació"
361
362 #: flask_security/forms.py:57
363 msgid "Recover Password"
364 msgstr "Restablir la contrasenya"
365
366 #: flask_security/forms.py:58
367 msgid "Reset Password"
368 msgstr "Restablir la contrasenya"
369
370 #: flask_security/forms.py:59
371 msgid "Retype Password"
372 msgstr "Escriu la contrasenya una altra vegada"
373
374 #: flask_security/forms.py:60
375 msgid "New Password"
376 msgstr "Nova contrasenya"
377
378 #: flask_security/forms.py:61
379 msgid "Change Password"
380 msgstr "Canvi de contrasenya"
381
382 #: flask_security/forms.py:62
383 msgid "Send Login Link"
384 msgstr "Enviar l'enllaç d'inici de sessió"
385
386 #: flask_security/forms.py:63
387 msgid "Verify Password"
388 msgstr ""
389
390 #: flask_security/forms.py:64
391 msgid "Change Method"
392 msgstr ""
393
394 #: flask_security/forms.py:65
395 msgid "Phone Number"
396 msgstr ""
397
398 #: flask_security/forms.py:66
399 msgid "Authentication Code"
400 msgstr ""
401
402 #: flask_security/forms.py:67
403 msgid "Submit"
404 msgstr ""
405
406 #: flask_security/forms.py:68
407 msgid "Submit Code"
408 msgstr ""
409
410 #: flask_security/forms.py:69
411 msgid "Error(s)"
412 msgstr ""
413
414 #: flask_security/forms.py:70
415 msgid "Identity"
416 msgstr ""
417
418 #: flask_security/forms.py:71
419 msgid "Send Code"
420 msgstr ""
421
422 #: flask_security/forms.py:72
423 #, fuzzy
424 msgid "Passcode"
425 msgstr "Contrasenya"
426
427 #: flask_security/unified_signin.py:145
428 #, fuzzy
429 msgid "Code or Password"
430 msgstr "Restablir la contrasenya"
431
432 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
433 msgid "Available Methods"
434 msgstr ""
435
436 #: flask_security/unified_signin.py:151
437 msgid "Via email"
438 msgstr ""
439
440 #: flask_security/unified_signin.py:151
441 msgid "Via SMS"
442 msgstr ""
443
444 #: flask_security/unified_signin.py:272
445 msgid "Set up using email"
446 msgstr ""
447
448 #: flask_security/unified_signin.py:275
449 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
450 msgstr ""
451
452 #: flask_security/unified_signin.py:277
453 msgid "Set up using SMS"
454 msgstr ""
455
456 #: flask_security/templates/security/_menu.html:2
457 msgid "Menu"
458 msgstr "Menú"
459
460 #: flask_security/templates/security/_menu.html:8
461 msgid "Unified Sign In"
462 msgstr ""
463
464 #: flask_security/templates/security/_menu.html:14
465 msgid "Forgot password"
466 msgstr "Contrasenya oblidada"
467
468 #: flask_security/templates/security/_menu.html:17
469 msgid "Confirm account"
470 msgstr "Confirmació de compte"
471
472 #: flask_security/templates/security/change_password.html:6
473 msgid "Change password"
474 msgstr "Canviar la contrasenya"
475
476 #: flask_security/templates/security/forgot_password.html:6
477 msgid "Send password reset instructions"
478 msgstr "Enviar instruccions per restablir la contrasenya"
479
480 #: flask_security/templates/security/reset_password.html:6
481 msgid "Reset password"
482 msgstr "Restablir la contrasenya"
483
484 #: flask_security/templates/security/send_confirmation.html:6
485 msgid "Resend confirmation instructions"
486 msgstr "Reenviar instruccions de confirmació"
487
488 #: flask_security/templates/security/two_factor_setup.html:6
489 msgid "Two-factor authentication adds an extra layer of security to your account"
490 msgstr ""
491
492 #: flask_security/templates/security/two_factor_setup.html:7
493 msgid ""
494 "In addition to your username and password, you'll need to use a code that"
495 " we will send you"
496 msgstr ""
497
498 #: flask_security/templates/security/two_factor_setup.html:18
499 msgid "To complete logging in, please enter the code sent to your mail"
500 msgstr ""
501
502 #: flask_security/templates/security/two_factor_setup.html:21
503 #: flask_security/templates/security/us_setup.html:21
504 msgid ""
505 "Open your authenticator app on your device and scan the following qrcode "
506 "to start receiving codes:"
507 msgstr ""
508
509 #: flask_security/templates/security/two_factor_setup.html:22
510 msgid "Two factor authentication code"
511 msgstr ""
512
513 #: flask_security/templates/security/two_factor_setup.html:25
514 msgid "To Which Phone Number Should We Send Code To?"
515 msgstr ""
516
517 #: flask_security/templates/security/two_factor_verify_code.html:6
518 msgid "Two-factor Authentication"
519 msgstr ""
520
521 #: flask_security/templates/security/two_factor_verify_code.html:7
522 msgid "Please enter your authentication code"
523 msgstr ""
524
525 #: flask_security/templates/security/two_factor_verify_code.html:18
526 msgid "The code for authentication was sent to your email address"
527 msgstr ""
528
529 #: flask_security/templates/security/two_factor_verify_code.html:21
530 msgid "A mail was sent to us in order to reset your application account"
531 msgstr ""
532
533 #: flask_security/templates/security/two_factor_verify_password.html:6
534 #: flask_security/templates/security/verify.html:6
535 msgid "Please Enter Your Password"
536 msgstr ""
537
538 #: flask_security/templates/security/us_setup.html:6
539 msgid "Setup Unified Sign In options"
540 msgstr ""
541
542 #: flask_security/templates/security/us_setup.html:22
543 msgid "Passwordless QRCode"
544 msgstr ""
545
546 #: flask_security/templates/security/us_setup.html:25
547 #: flask_security/templates/security/us_signin.html:23
548 #: flask_security/templates/security/us_verify.html:21
549 #, fuzzy
550 msgid "Code has been sent"
551 msgstr "S'ha restablit la teva contrasenya"
552
553 #: flask_security/templates/security/us_setup.html:29
554 msgid "No methods have been enabled - nothing to setup"
555 msgstr ""
556
557 #: flask_security/templates/security/us_signin.html:15
558 #: flask_security/templates/security/us_verify.html:13
559 msgid "Request one-time code be sent"
560 msgstr ""
561
562 #: flask_security/templates/security/us_verify.html:6
563 #, fuzzy
564 msgid "Please re-authenticate"
565 msgstr "Has d'iniciar una nova sessió per tal d'accedir a aquesta pàgina."
566
567 #: flask_security/templates/security/email/change_notice.html:1
568 msgid "Your password has been changed."
569 msgstr "S'ha canviat la teva contrasenya."
570
571 #: flask_security/templates/security/email/change_notice.html:3
572 msgid "If you did not change your password,"
573 msgstr "Si no has canviat la teva contrasenya,"
574
575 #: flask_security/templates/security/email/change_notice.html:3
576 msgid "click here to reset it"
577 msgstr "fes clic aquí per a restablir-la"
578
579 #: flask_security/templates/security/email/confirmation_instructions.html:1
580 msgid "Please confirm your email through the link below:"
581 msgstr "Confirma el teu correu electrònic fent clic aquí:"
582
583 #: flask_security/templates/security/email/confirmation_instructions.html:3
584 #: flask_security/templates/security/email/welcome.html:6
585 msgid "Confirm my account"
586 msgstr "Confirmeu el compte"
587
588 #: flask_security/templates/security/email/login_instructions.html:1
589 #: flask_security/templates/security/email/welcome.html:1
590 #, python-format
591 msgid "Welcome %(email)s!"
592 msgstr "Benvingut %(email)s!"
593
594 #: flask_security/templates/security/email/login_instructions.html:3
595 msgid "You can log into your account through the link below:"
596 msgstr "Inicia la sessió fent clic aquí:"
597
598 #: flask_security/templates/security/email/login_instructions.html:5
599 msgid "Login now"
600 msgstr "Iniciar sessió ara"
601
602 #: flask_security/templates/security/email/reset_instructions.html:1
603 msgid "Click here to reset your password"
604 msgstr "Feu clic aquí per restablir la contrasenya"
605
606 #: flask_security/templates/security/email/two_factor_instructions.html:3
607 msgid "You can log into your account using the following code:"
608 msgstr ""
609
610 #: flask_security/templates/security/email/two_factor_rescue.html:1
611 msgid "can not access mail account"
612 msgstr ""
613
614 #: flask_security/templates/security/email/us_instructions.html:3
615 #, fuzzy
616 msgid "You can sign into your account using the following code:"
617 msgstr "Inicia la sessió fent clic aquí:"
618
619 #: flask_security/templates/security/email/us_instructions.html:6
620 #, fuzzy
621 msgid "Or use the the link below:"
622 msgstr "Confirmeu el vostre correu electrònic fent clic a continuació:"
623
624 #: flask_security/templates/security/email/welcome.html:4
625 msgid "You can confirm your email through the link below:"
626 msgstr "Confirmeu el vostre correu electrònic fent clic a continuació:"
627
0 # Danish (Denmark) translations for Flask-Security.
1 # Copyright (C) 2017 ORGANIZATION
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.1.0\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-03-23 14:04+0100\n"
12 "Last-Translator: Leonhard Printz <[email protected]>\n"
13 "Language: da_DK\n"
14 "Language-Team: da_DK <[email protected]>\n"
15 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Login påkræveet"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Velkommen"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Bekræft venligst din email"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Logininstruktioner"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "Din adgangskode er blevet nulstillet"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "Din adgangskode er blevet ændret"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instruktioner til nulstilling af adganskode"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "Du har ikke adgang til denne resource."
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Bekræft identitet for at få adgang til denne side."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "Mange tak. Bekræftelsesinstruktioner er blevet sendt til %(email)s."
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "Mange Tak. Din email er blevet bekræftet."
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "Din email er allerede blevet bekræftet."
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "Ugyldig bekræftigelsestoken."
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "%(email)s er allerede brugt af en anden konto."
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "Adgangskode passer ikke"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "Adgangskoderne passer ikke"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "Omdirigering udenfor domænet er forbudt"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr ""
119 "Instruktioner til nulstilling af din adgangskode er blevet sendt til "
120 "%(email)s."
121
122 #: flask_security/core.py:311
123 #, python-format
124 msgid ""
125 "You did not reset your password within %(within)s. New instructions have "
126 "been sent to %(email)s."
127 msgstr ""
128 "Du har ikke nulstillet din adgangskode indenfor %(within)s. Nye "
129 "instruktioner er sendt til %(email)s."
130
131 #: flask_security/core.py:317
132 msgid "Invalid reset password token."
133 msgstr "Ugyldig nulstillingstoken."
134
135 #: flask_security/core.py:318
136 msgid "Email requires confirmation."
137 msgstr "Email kræver bekræftigelse."
138
139 #: flask_security/core.py:320
140 #, python-format
141 msgid "Confirmation instructions have been sent to %(email)s."
142 msgstr "Bekræftigelsesinstruktioner er blevet sendt til %(email)s."
143
144 #: flask_security/core.py:324
145 #, python-format
146 msgid ""
147 "You did not confirm your email within %(within)s. New instructions to "
148 "confirm your email have been sent to %(email)s."
149 msgstr ""
150 "Du har ikke bekræftet din email indenfor %(within)s. Nye instruktioner er"
151 " blevet sendt til %(email)s."
152
153 #: flask_security/core.py:332
154 #, python-format
155 msgid ""
156 "You did not login within %(within)s. New instructions to login have been "
157 "sent to %(email)s."
158 msgstr ""
159 "Du har ikke logget in indenfor %(within)s. Nye logininstruktioner er "
160 "blevet sendt til %(email)s."
161
162 #: flask_security/core.py:339
163 #, python-format
164 msgid "Instructions to login have been sent to %(email)s."
165 msgstr "Logininstruktioner er blevet sendt til %(email)s."
166
167 #: flask_security/core.py:342
168 msgid "Invalid login token."
169 msgstr "Ugyldig logintoken."
170
171 #: flask_security/core.py:343
172 msgid "Account is disabled."
173 msgstr "Kontoen er deaktiveret."
174
175 #: flask_security/core.py:344
176 msgid "Email not provided"
177 msgstr "Email ikke angivet"
178
179 #: flask_security/core.py:345
180 msgid "Invalid email address"
181 msgstr "Ugyldig email adresse"
182
183 #: flask_security/core.py:346
184 #, fuzzy
185 msgid "Invalid code"
186 msgstr "Ugyldig adgangskode"
187
188 #: flask_security/core.py:347
189 msgid "Password not provided"
190 msgstr "Adgangskode ikke angivet"
191
192 #: flask_security/core.py:348
193 msgid "No password is set for this user"
194 msgstr "Denne bruger har ingen adganskode"
195
196 #: flask_security/core.py:350
197 #, fuzzy, python-format
198 msgid "Password must be at least %(length)s characters"
199 msgstr "Adgangskoden skal indeholde mindst 6 tegn"
200
201 #: flask_security/core.py:353
202 msgid "Password not complex enough"
203 msgstr ""
204
205 #: flask_security/core.py:354
206 msgid "Password on breached list"
207 msgstr ""
208
209 #: flask_security/core.py:356
210 msgid "Failed to contact breached passwords site"
211 msgstr ""
212
213 #: flask_security/core.py:359
214 msgid "Phone number not valid e.g. missing country code"
215 msgstr ""
216
217 #: flask_security/core.py:360
218 msgid "Specified user does not exist"
219 msgstr "Denne bruger findes ikke"
220
221 #: flask_security/core.py:361
222 msgid "Invalid password"
223 msgstr "Ugyldig adgangskode"
224
225 #: flask_security/core.py:362
226 msgid "Password or code submitted is not valid"
227 msgstr ""
228
229 #: flask_security/core.py:363
230 msgid "You have successfully logged in."
231 msgstr "Du er hermed blevet logget ind."
232
233 #: flask_security/core.py:364
234 msgid "Forgot password?"
235 msgstr "Glemt adgangskode?"
236
237 #: flask_security/core.py:366
238 msgid ""
239 "You successfully reset your password and you have been logged in "
240 "automatically."
241 msgstr ""
242 "Du har hermed nulstillet din adgangskode og er blevet automatisk logget "
243 "ind."
244
245 #: flask_security/core.py:373
246 msgid "Your new password must be different than your previous password."
247 msgstr "Din nye adgangskode skal være anderledes end din tidligere adgangskode."
248
249 #: flask_security/core.py:376
250 msgid "You successfully changed your password."
251 msgstr "Du har hermed ændret din adgangskode."
252
253 #: flask_security/core.py:377
254 msgid "Please log in to access this page."
255 msgstr "Log in for at få adgang til denne side."
256
257 #: flask_security/core.py:378
258 msgid "Please reauthenticate to access this page."
259 msgstr "Bekræft identitet for at få adgang til denne side."
260
261 #: flask_security/core.py:379
262 msgid "Reauthentication successful"
263 msgstr ""
264
265 #: flask_security/core.py:381
266 msgid "You can only access this endpoint when not logged in."
267 msgstr ""
268
269 #: flask_security/core.py:384
270 msgid "Failed to send code. Please try again later"
271 msgstr ""
272
273 #: flask_security/core.py:385
274 msgid "Invalid Token"
275 msgstr ""
276
277 #: flask_security/core.py:386
278 msgid "Your token has been confirmed"
279 msgstr ""
280
281 #: flask_security/core.py:388
282 msgid "You successfully changed your two-factor method."
283 msgstr ""
284
285 #: flask_security/core.py:392
286 msgid "You successfully confirmed password"
287 msgstr ""
288
289 #: flask_security/core.py:396
290 msgid "Password confirmation is needed in order to access page"
291 msgstr ""
292
293 #: flask_security/core.py:400
294 msgid "You currently do not have permissions to access this page"
295 msgstr ""
296
297 #: flask_security/core.py:403
298 msgid "Marked method is not valid"
299 msgstr ""
300
301 #: flask_security/core.py:405
302 msgid "You successfully disabled two factor authorization."
303 msgstr ""
304
305 #: flask_security/core.py:408
306 msgid "Requested method is not valid"
307 msgstr ""
308
309 #: flask_security/core.py:410
310 #, python-format
311 msgid "Setup must be completed within %(within)s. Please start over."
312 msgstr ""
313
314 #: flask_security/core.py:413
315 msgid "Unified sign in setup successful"
316 msgstr ""
317
318 #: flask_security/core.py:414
319 msgid "You must specify a valid identity to sign in"
320 msgstr ""
321
322 #: flask_security/core.py:415
323 #, python-format
324 msgid "Use this code to sign in: %(code)s."
325 msgstr ""
326
327 #: flask_security/forms.py:50
328 msgid "Email Address"
329 msgstr "Email adresse"
330
331 #: flask_security/forms.py:51
332 msgid "Password"
333 msgstr "Adgangskode"
334
335 #: flask_security/forms.py:52
336 msgid "Remember Me"
337 msgstr "Husk"
338
339 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
340 #: flask_security/templates/security/login_user.html:6
341 #: flask_security/templates/security/send_login.html:6
342 msgid "Login"
343 msgstr "Login"
344
345 #: flask_security/forms.py:54
346 #: flask_security/templates/security/email/us_instructions.html:8
347 #: flask_security/templates/security/us_signin.html:6
348 msgid "Sign In"
349 msgstr ""
350
351 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
352 #: flask_security/templates/security/register_user.html:6
353 msgid "Register"
354 msgstr "Registrer"
355
356 #: flask_security/forms.py:56
357 msgid "Resend Confirmation Instructions"
358 msgstr "Gensend bekræftelsesinstruktioner"
359
360 #: flask_security/forms.py:57
361 msgid "Recover Password"
362 msgstr "Genopret adgangskode"
363
364 #: flask_security/forms.py:58
365 msgid "Reset Password"
366 msgstr "Nulstil adgangskode"
367
368 #: flask_security/forms.py:59
369 msgid "Retype Password"
370 msgstr "Gentast adgangskode"
371
372 #: flask_security/forms.py:60
373 msgid "New Password"
374 msgstr "Ny adgangskode"
375
376 #: flask_security/forms.py:61
377 msgid "Change Password"
378 msgstr "Ændre adgangskode"
379
380 #: flask_security/forms.py:62
381 msgid "Send Login Link"
382 msgstr "Send login link"
383
384 #: flask_security/forms.py:63
385 msgid "Verify Password"
386 msgstr ""
387
388 #: flask_security/forms.py:64
389 msgid "Change Method"
390 msgstr ""
391
392 #: flask_security/forms.py:65
393 msgid "Phone Number"
394 msgstr ""
395
396 #: flask_security/forms.py:66
397 msgid "Authentication Code"
398 msgstr ""
399
400 #: flask_security/forms.py:67
401 msgid "Submit"
402 msgstr ""
403
404 #: flask_security/forms.py:68
405 msgid "Submit Code"
406 msgstr ""
407
408 #: flask_security/forms.py:69
409 msgid "Error(s)"
410 msgstr ""
411
412 #: flask_security/forms.py:70
413 msgid "Identity"
414 msgstr ""
415
416 #: flask_security/forms.py:71
417 msgid "Send Code"
418 msgstr ""
419
420 #: flask_security/forms.py:72
421 #, fuzzy
422 msgid "Passcode"
423 msgstr "Adgangskode"
424
425 #: flask_security/unified_signin.py:145
426 #, fuzzy
427 msgid "Code or Password"
428 msgstr "Genopret adgangskode"
429
430 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
431 msgid "Available Methods"
432 msgstr ""
433
434 #: flask_security/unified_signin.py:151
435 msgid "Via email"
436 msgstr ""
437
438 #: flask_security/unified_signin.py:151
439 msgid "Via SMS"
440 msgstr ""
441
442 #: flask_security/unified_signin.py:272
443 msgid "Set up using email"
444 msgstr ""
445
446 #: flask_security/unified_signin.py:275
447 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
448 msgstr ""
449
450 #: flask_security/unified_signin.py:277
451 msgid "Set up using SMS"
452 msgstr ""
453
454 #: flask_security/templates/security/_menu.html:2
455 msgid "Menu"
456 msgstr "Menu"
457
458 #: flask_security/templates/security/_menu.html:8
459 msgid "Unified Sign In"
460 msgstr ""
461
462 #: flask_security/templates/security/_menu.html:14
463 msgid "Forgot password"
464 msgstr "Glemt din adgangskode"
465
466 #: flask_security/templates/security/_menu.html:17
467 msgid "Confirm account"
468 msgstr "Bekræft konto"
469
470 #: flask_security/templates/security/change_password.html:6
471 msgid "Change password"
472 msgstr "Ændre adgangskode"
473
474 #: flask_security/templates/security/forgot_password.html:6
475 msgid "Send password reset instructions"
476 msgstr "Send adgangskode nulstillingsinstruktioner"
477
478 #: flask_security/templates/security/reset_password.html:6
479 msgid "Reset password"
480 msgstr "Nulstil adgangskode"
481
482 #: flask_security/templates/security/send_confirmation.html:6
483 msgid "Resend confirmation instructions"
484 msgstr "Gensend bekræftelsesinstruktioner"
485
486 #: flask_security/templates/security/two_factor_setup.html:6
487 msgid "Two-factor authentication adds an extra layer of security to your account"
488 msgstr ""
489
490 #: flask_security/templates/security/two_factor_setup.html:7
491 msgid ""
492 "In addition to your username and password, you'll need to use a code that"
493 " we will send you"
494 msgstr ""
495
496 #: flask_security/templates/security/two_factor_setup.html:18
497 msgid "To complete logging in, please enter the code sent to your mail"
498 msgstr ""
499
500 #: flask_security/templates/security/two_factor_setup.html:21
501 #: flask_security/templates/security/us_setup.html:21
502 msgid ""
503 "Open your authenticator app on your device and scan the following qrcode "
504 "to start receiving codes:"
505 msgstr ""
506
507 #: flask_security/templates/security/two_factor_setup.html:22
508 msgid "Two factor authentication code"
509 msgstr ""
510
511 #: flask_security/templates/security/two_factor_setup.html:25
512 msgid "To Which Phone Number Should We Send Code To?"
513 msgstr ""
514
515 #: flask_security/templates/security/two_factor_verify_code.html:6
516 msgid "Two-factor Authentication"
517 msgstr ""
518
519 #: flask_security/templates/security/two_factor_verify_code.html:7
520 msgid "Please enter your authentication code"
521 msgstr ""
522
523 #: flask_security/templates/security/two_factor_verify_code.html:18
524 msgid "The code for authentication was sent to your email address"
525 msgstr ""
526
527 #: flask_security/templates/security/two_factor_verify_code.html:21
528 msgid "A mail was sent to us in order to reset your application account"
529 msgstr ""
530
531 #: flask_security/templates/security/two_factor_verify_password.html:6
532 #: flask_security/templates/security/verify.html:6
533 msgid "Please Enter Your Password"
534 msgstr ""
535
536 #: flask_security/templates/security/us_setup.html:6
537 msgid "Setup Unified Sign In options"
538 msgstr ""
539
540 #: flask_security/templates/security/us_setup.html:22
541 msgid "Passwordless QRCode"
542 msgstr ""
543
544 #: flask_security/templates/security/us_setup.html:25
545 #: flask_security/templates/security/us_signin.html:23
546 #: flask_security/templates/security/us_verify.html:21
547 #, fuzzy
548 msgid "Code has been sent"
549 msgstr "Din adgangskode er blevet nulstillet"
550
551 #: flask_security/templates/security/us_setup.html:29
552 msgid "No methods have been enabled - nothing to setup"
553 msgstr ""
554
555 #: flask_security/templates/security/us_signin.html:15
556 #: flask_security/templates/security/us_verify.html:13
557 msgid "Request one-time code be sent"
558 msgstr ""
559
560 #: flask_security/templates/security/us_verify.html:6
561 #, fuzzy
562 msgid "Please re-authenticate"
563 msgstr "Bekræft identitet for at få adgang til denne side."
564
565 #: flask_security/templates/security/email/change_notice.html:1
566 msgid "Your password has been changed."
567 msgstr "Din adgangskode er blevet ændret."
568
569 #: flask_security/templates/security/email/change_notice.html:3
570 msgid "If you did not change your password,"
571 msgstr "Hvis du ikke har ændret din adgangskode,"
572
573 #: flask_security/templates/security/email/change_notice.html:3
574 msgid "click here to reset it"
575 msgstr "klik her for at ændre den"
576
577 #: flask_security/templates/security/email/confirmation_instructions.html:1
578 msgid "Please confirm your email through the link below:"
579 msgstr "Bekræft venligst din email gennem nedenstående link:"
580
581 #: flask_security/templates/security/email/confirmation_instructions.html:3
582 #: flask_security/templates/security/email/welcome.html:6
583 msgid "Confirm my account"
584 msgstr "Bekræft ny konto"
585
586 #: flask_security/templates/security/email/login_instructions.html:1
587 #: flask_security/templates/security/email/welcome.html:1
588 #, python-format
589 msgid "Welcome %(email)s!"
590 msgstr "Velkommen %(email)s!"
591
592 #: flask_security/templates/security/email/login_instructions.html:3
593 msgid "You can log into your account through the link below:"
594 msgstr "Du kan logge ind gennem nedenstående link:"
595
596 #: flask_security/templates/security/email/login_instructions.html:5
597 msgid "Login now"
598 msgstr "Login"
599
600 #: flask_security/templates/security/email/reset_instructions.html:1
601 msgid "Click here to reset your password"
602 msgstr "Klik her for at nulstille din adgangskode"
603
604 #: flask_security/templates/security/email/two_factor_instructions.html:3
605 msgid "You can log into your account using the following code:"
606 msgstr ""
607
608 #: flask_security/templates/security/email/two_factor_rescue.html:1
609 msgid "can not access mail account"
610 msgstr ""
611
612 #: flask_security/templates/security/email/us_instructions.html:3
613 #, fuzzy
614 msgid "You can sign into your account using the following code:"
615 msgstr "Du kan logge ind gennem nedenstående link:"
616
617 #: flask_security/templates/security/email/us_instructions.html:6
618 #, fuzzy
619 msgid "Or use the the link below:"
620 msgstr "Bekræft venligst din email gennem nedenstående link:"
621
622 #: flask_security/templates/security/email/welcome.html:4
623 msgid "You can confirm your email through the link below:"
624 msgstr "Bekræft venligst din email gennem nedenstående link:"
625
0 # German translation for Flask-Security (Du/Sie distinction has been
1 # avoided)
2 # Copyright (C) 2017 ORGANIZATION
3 # This file is distributed under the same license as the Flask-Security
4 # project.
5 # Ingo Kleiber <[email protected]>, 2017,
6 # Erich Seifert <[email protected]>, 2017.
7 #
8 msgid ""
9 msgstr ""
10 "Project-Id-Version: Flask-Security 2.0.1\n"
11 "Report-Msgid-Bugs-To: [email protected]\n"
12 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
13 "PO-Revision-Date: 2017-09-25 09:14+0200\n"
14 "Last-Translator: Erich Seifert <[email protected]>\n"
15 "Language: de_DE\n"
16 "Language-Team: de_DE <[email protected]>\n"
17 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
18 "MIME-Version: 1.0\n"
19 "Content-Type: text/plain; charset=utf-8\n"
20 "Content-Transfer-Encoding: 8bit\n"
21 "Generated-By: Babel 2.8.0\n"
22
23 #: flask_security/core.py:207
24 msgid "Login Required"
25 msgstr "Anmeldung erforderlich"
26
27 #: flask_security/core.py:208
28 #: flask_security/templates/security/email/two_factor_instructions.html:1
29 #: flask_security/templates/security/email/us_instructions.html:1
30 msgid "Welcome"
31 msgstr "Willkommen"
32
33 #: flask_security/core.py:209
34 msgid "Please confirm your email"
35 msgstr "Bitte E-Mail-Adresse bestätigen"
36
37 #: flask_security/core.py:210
38 msgid "Login instructions"
39 msgstr "Anmeldeanleitung"
40
41 #: flask_security/core.py:211
42 #: flask_security/templates/security/email/reset_notice.html:1
43 msgid "Your password has been reset"
44 msgstr "Das Passwort wurde zurückgesetzt"
45
46 #: flask_security/core.py:212
47 msgid "Your password has been changed"
48 msgstr "Das Passwort wurde geändert"
49
50 #: flask_security/core.py:213
51 msgid "Password reset instructions"
52 msgstr "Anleitung zur Passwortwiederherstellung"
53
54 #: flask_security/core.py:216
55 msgid "Two-factor Login"
56 msgstr ""
57
58 #: flask_security/core.py:217
59 msgid "Two-factor Rescue"
60 msgstr ""
61
62 #: flask_security/core.py:266
63 msgid "Verification Code"
64 msgstr ""
65
66 #: flask_security/core.py:282
67 msgid "Input not appropriate for requested API"
68 msgstr ""
69
70 #: flask_security/core.py:283
71 msgid "You do not have permission to view this resource."
72 msgstr "Keine Berechtigung um diese Ressource zu sehen."
73
74 #: flask_security/core.py:285
75 msgid "You are not authenticated. Please supply the correct credentials."
76 msgstr ""
77
78 #: flask_security/core.py:289
79 #, fuzzy
80 msgid "You must re-authenticate to access this endpoint"
81 msgstr "Bitte neu authentifizieren, um auf diese Seite zuzugreifen."
82
83 #: flask_security/core.py:293
84 #, python-format
85 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
86 msgstr "Vielen Dank. Bestätigungsanleitung wurde an %(email)s gesendet."
87
88 #: flask_security/core.py:296
89 msgid "Thank you. Your email has been confirmed."
90 msgstr "Vielen Dank. Die E-Mail-Adresse wurde bestätigt."
91
92 #: flask_security/core.py:297
93 msgid "Your email has already been confirmed."
94 msgstr "Die E-Mail-Adresse wurde bereits bestätigt."
95
96 #: flask_security/core.py:298
97 msgid "Invalid confirmation token."
98 msgstr "Ungültiger Bestätigungscode."
99
100 #: flask_security/core.py:300
101 #, python-format
102 msgid "%(email)s is already associated with an account."
103 msgstr "%(email)s ist bereits mit einem Konto verknüpft."
104
105 #: flask_security/core.py:303
106 msgid "Password does not match"
107 msgstr "Das Passwort stimmt nicht überein"
108
109 #: flask_security/core.py:304
110 msgid "Passwords do not match"
111 msgstr "Die Passwörter stimmen nicht überein"
112
113 #: flask_security/core.py:305
114 msgid "Redirections outside the domain are forbidden"
115 msgstr "Weiterleitungen außerhalb der Domain sind verboten"
116
117 #: flask_security/core.py:307
118 #, python-format
119 msgid "Instructions to reset your password have been sent to %(email)s."
120 msgstr ""
121 "Eine Anleitung, um das Passwort wiederherzustellen wurde an %(email)s "
122 "gesendet."
123
124 #: flask_security/core.py:311
125 #, python-format
126 msgid ""
127 "You did not reset your password within %(within)s. New instructions have "
128 "been sent to %(email)s."
129 msgstr ""
130 "Das Passwort wurde nicht innerhalb von %(within)s zurückgesetzt. Eine "
131 "neue Anleitung wurde an %(email)s gesendet."
132
133 #: flask_security/core.py:317
134 msgid "Invalid reset password token."
135 msgstr "Ungültiger Passwortwiederherstellungscode."
136
137 #: flask_security/core.py:318
138 msgid "Email requires confirmation."
139 msgstr "Die E-Mail-Adresse muss bestätigt werden."
140
141 #: flask_security/core.py:320
142 #, python-format
143 msgid "Confirmation instructions have been sent to %(email)s."
144 msgstr "Bestätigungsanleitung wurde an %(email)s gesendet."
145
146 #: flask_security/core.py:324
147 #, python-format
148 msgid ""
149 "You did not confirm your email within %(within)s. New instructions to "
150 "confirm your email have been sent to %(email)s."
151 msgstr ""
152 "Die E-Mail-Adresse wurden nicht innerhalb von %(within)s bestätigt. Neue "
153 "Instruktionen wurden an %(email)s gesendet."
154
155 #: flask_security/core.py:332
156 #, python-format
157 msgid ""
158 "You did not login within %(within)s. New instructions to login have been "
159 "sent to %(email)s."
160 msgstr ""
161 "Die Anmeldung erfolgte nicht in %(within)s. Eine neue Anleitung wurde an "
162 "%(email)s gesendet."
163
164 #: flask_security/core.py:339
165 #, python-format
166 msgid "Instructions to login have been sent to %(email)s."
167 msgstr "Eine Anleitung zur Anmeldung wurde an %(email)s gesendet."
168
169 #: flask_security/core.py:342
170 msgid "Invalid login token."
171 msgstr "Ungültiger Anmeldecode."
172
173 #: flask_security/core.py:343
174 msgid "Account is disabled."
175 msgstr "Konto ist deaktiviert."
176
177 #: flask_security/core.py:344
178 msgid "Email not provided"
179 msgstr "Keine E-Mail-Adresse angegeben"
180
181 #: flask_security/core.py:345
182 msgid "Invalid email address"
183 msgstr "Ungültige E-Mail-Adresse"
184
185 #: flask_security/core.py:346
186 #, fuzzy
187 msgid "Invalid code"
188 msgstr "Ungültiges Passwort"
189
190 #: flask_security/core.py:347
191 msgid "Password not provided"
192 msgstr "Kein Passwort angegeben"
193
194 #: flask_security/core.py:348
195 msgid "No password is set for this user"
196 msgstr "Für diesen Benutzer ist kein Passwort gesetzt"
197
198 #: flask_security/core.py:350
199 #, fuzzy, python-format
200 msgid "Password must be at least %(length)s characters"
201 msgstr "Das Passwort muss mindestens 6 Zeichen lang sein"
202
203 #: flask_security/core.py:353
204 msgid "Password not complex enough"
205 msgstr ""
206
207 #: flask_security/core.py:354
208 msgid "Password on breached list"
209 msgstr ""
210
211 #: flask_security/core.py:356
212 msgid "Failed to contact breached passwords site"
213 msgstr ""
214
215 #: flask_security/core.py:359
216 msgid "Phone number not valid e.g. missing country code"
217 msgstr ""
218
219 #: flask_security/core.py:360
220 msgid "Specified user does not exist"
221 msgstr "Angegebener Benutzer existiert nicht"
222
223 #: flask_security/core.py:361
224 msgid "Invalid password"
225 msgstr "Ungültiges Passwort"
226
227 #: flask_security/core.py:362
228 msgid "Password or code submitted is not valid"
229 msgstr ""
230
231 #: flask_security/core.py:363
232 msgid "You have successfully logged in."
233 msgstr "Die Anmeldung war erfolgreich."
234
235 #: flask_security/core.py:364
236 msgid "Forgot password?"
237 msgstr "Passwort vergessen?"
238
239 #: flask_security/core.py:366
240 msgid ""
241 "You successfully reset your password and you have been logged in "
242 "automatically."
243 msgstr ""
244 "Das Passwort wurde erfolgreich wiederhergestellt und die Anmeldung "
245 "erfolgte automatisch."
246
247 #: flask_security/core.py:373
248 msgid "Your new password must be different than your previous password."
249 msgstr "Das neue Passwort muss sich vom vorherigen unterscheiden."
250
251 #: flask_security/core.py:376
252 msgid "You successfully changed your password."
253 msgstr "Das Passwort wurde erfolgreich geändert."
254
255 #: flask_security/core.py:377
256 msgid "Please log in to access this page."
257 msgstr "Bitte anmelden, um diese Seite zu sehen."
258
259 #: flask_security/core.py:378
260 msgid "Please reauthenticate to access this page."
261 msgstr "Bitte neu authentifizieren, um auf diese Seite zuzugreifen."
262
263 #: flask_security/core.py:379
264 msgid "Reauthentication successful"
265 msgstr ""
266
267 #: flask_security/core.py:381
268 msgid "You can only access this endpoint when not logged in."
269 msgstr ""
270
271 #: flask_security/core.py:384
272 msgid "Failed to send code. Please try again later"
273 msgstr ""
274
275 #: flask_security/core.py:385
276 msgid "Invalid Token"
277 msgstr ""
278
279 #: flask_security/core.py:386
280 msgid "Your token has been confirmed"
281 msgstr ""
282
283 #: flask_security/core.py:388
284 msgid "You successfully changed your two-factor method."
285 msgstr ""
286
287 #: flask_security/core.py:392
288 msgid "You successfully confirmed password"
289 msgstr ""
290
291 #: flask_security/core.py:396
292 msgid "Password confirmation is needed in order to access page"
293 msgstr ""
294
295 #: flask_security/core.py:400
296 msgid "You currently do not have permissions to access this page"
297 msgstr ""
298
299 #: flask_security/core.py:403
300 msgid "Marked method is not valid"
301 msgstr ""
302
303 #: flask_security/core.py:405
304 msgid "You successfully disabled two factor authorization."
305 msgstr ""
306
307 #: flask_security/core.py:408
308 msgid "Requested method is not valid"
309 msgstr ""
310
311 #: flask_security/core.py:410
312 #, python-format
313 msgid "Setup must be completed within %(within)s. Please start over."
314 msgstr ""
315
316 #: flask_security/core.py:413
317 msgid "Unified sign in setup successful"
318 msgstr ""
319
320 #: flask_security/core.py:414
321 msgid "You must specify a valid identity to sign in"
322 msgstr ""
323
324 #: flask_security/core.py:415
325 #, python-format
326 msgid "Use this code to sign in: %(code)s."
327 msgstr ""
328
329 #: flask_security/forms.py:50
330 msgid "Email Address"
331 msgstr "E-Mail-Adresse"
332
333 #: flask_security/forms.py:51
334 msgid "Password"
335 msgstr "Passwort"
336
337 #: flask_security/forms.py:52
338 msgid "Remember Me"
339 msgstr "Erinnern"
340
341 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
342 #: flask_security/templates/security/login_user.html:6
343 #: flask_security/templates/security/send_login.html:6
344 msgid "Login"
345 msgstr "Anmelden"
346
347 #: flask_security/forms.py:54
348 #: flask_security/templates/security/email/us_instructions.html:8
349 #: flask_security/templates/security/us_signin.html:6
350 msgid "Sign In"
351 msgstr ""
352
353 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
354 #: flask_security/templates/security/register_user.html:6
355 msgid "Register"
356 msgstr "Registrieren"
357
358 #: flask_security/forms.py:56
359 msgid "Resend Confirmation Instructions"
360 msgstr "Bestätigungsanleitung neu senden"
361
362 #: flask_security/forms.py:57
363 msgid "Recover Password"
364 msgstr "Passwort wiederherstellen"
365
366 #: flask_security/forms.py:58
367 msgid "Reset Password"
368 msgstr "Passwort zurücksetzen"
369
370 #: flask_security/forms.py:59
371 msgid "Retype Password"
372 msgstr "Passwort neu eingeben"
373
374 #: flask_security/forms.py:60
375 msgid "New Password"
376 msgstr "Neues Passwort"
377
378 #: flask_security/forms.py:61
379 msgid "Change Password"
380 msgstr "Passwort ändern"
381
382 #: flask_security/forms.py:62
383 msgid "Send Login Link"
384 msgstr "Anmelde-Link versenden"
385
386 #: flask_security/forms.py:63
387 msgid "Verify Password"
388 msgstr ""
389
390 #: flask_security/forms.py:64
391 msgid "Change Method"
392 msgstr ""
393
394 #: flask_security/forms.py:65
395 msgid "Phone Number"
396 msgstr ""
397
398 #: flask_security/forms.py:66
399 msgid "Authentication Code"
400 msgstr ""
401
402 #: flask_security/forms.py:67
403 msgid "Submit"
404 msgstr ""
405
406 #: flask_security/forms.py:68
407 msgid "Submit Code"
408 msgstr ""
409
410 #: flask_security/forms.py:69
411 msgid "Error(s)"
412 msgstr ""
413
414 #: flask_security/forms.py:70
415 msgid "Identity"
416 msgstr ""
417
418 #: flask_security/forms.py:71
419 msgid "Send Code"
420 msgstr ""
421
422 #: flask_security/forms.py:72
423 #, fuzzy
424 msgid "Passcode"
425 msgstr "Passwort"
426
427 #: flask_security/unified_signin.py:145
428 #, fuzzy
429 msgid "Code or Password"
430 msgstr "Passwort wiederherstellen"
431
432 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
433 msgid "Available Methods"
434 msgstr ""
435
436 #: flask_security/unified_signin.py:151
437 msgid "Via email"
438 msgstr ""
439
440 #: flask_security/unified_signin.py:151
441 msgid "Via SMS"
442 msgstr ""
443
444 #: flask_security/unified_signin.py:272
445 msgid "Set up using email"
446 msgstr ""
447
448 #: flask_security/unified_signin.py:275
449 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
450 msgstr ""
451
452 #: flask_security/unified_signin.py:277
453 msgid "Set up using SMS"
454 msgstr ""
455
456 #: flask_security/templates/security/_menu.html:2
457 msgid "Menu"
458 msgstr "Menü"
459
460 #: flask_security/templates/security/_menu.html:8
461 msgid "Unified Sign In"
462 msgstr ""
463
464 #: flask_security/templates/security/_menu.html:14
465 msgid "Forgot password"
466 msgstr "Passwort vergessen"
467
468 #: flask_security/templates/security/_menu.html:17
469 msgid "Confirm account"
470 msgstr "Konto bestätigen"
471
472 #: flask_security/templates/security/change_password.html:6
473 msgid "Change password"
474 msgstr "Passwort ändern"
475
476 #: flask_security/templates/security/forgot_password.html:6
477 msgid "Send password reset instructions"
478 msgstr "Anleitung zur Passwortzurücksetzung versenden"
479
480 #: flask_security/templates/security/reset_password.html:6
481 msgid "Reset password"
482 msgstr "Passwort zurücksetzen"
483
484 #: flask_security/templates/security/send_confirmation.html:6
485 msgid "Resend confirmation instructions"
486 msgstr "Bestätigungsanleitung erneut versenden"
487
488 #: flask_security/templates/security/two_factor_setup.html:6
489 msgid "Two-factor authentication adds an extra layer of security to your account"
490 msgstr ""
491
492 #: flask_security/templates/security/two_factor_setup.html:7
493 msgid ""
494 "In addition to your username and password, you'll need to use a code that"
495 " we will send you"
496 msgstr ""
497
498 #: flask_security/templates/security/two_factor_setup.html:18
499 msgid "To complete logging in, please enter the code sent to your mail"
500 msgstr ""
501
502 #: flask_security/templates/security/two_factor_setup.html:21
503 #: flask_security/templates/security/us_setup.html:21
504 msgid ""
505 "Open your authenticator app on your device and scan the following qrcode "
506 "to start receiving codes:"
507 msgstr ""
508
509 #: flask_security/templates/security/two_factor_setup.html:22
510 msgid "Two factor authentication code"
511 msgstr ""
512
513 #: flask_security/templates/security/two_factor_setup.html:25
514 msgid "To Which Phone Number Should We Send Code To?"
515 msgstr ""
516
517 #: flask_security/templates/security/two_factor_verify_code.html:6
518 msgid "Two-factor Authentication"
519 msgstr ""
520
521 #: flask_security/templates/security/two_factor_verify_code.html:7
522 msgid "Please enter your authentication code"
523 msgstr ""
524
525 #: flask_security/templates/security/two_factor_verify_code.html:18
526 msgid "The code for authentication was sent to your email address"
527 msgstr ""
528
529 #: flask_security/templates/security/two_factor_verify_code.html:21
530 msgid "A mail was sent to us in order to reset your application account"
531 msgstr ""
532
533 #: flask_security/templates/security/two_factor_verify_password.html:6
534 #: flask_security/templates/security/verify.html:6
535 msgid "Please Enter Your Password"
536 msgstr ""
537
538 #: flask_security/templates/security/us_setup.html:6
539 msgid "Setup Unified Sign In options"
540 msgstr ""
541
542 #: flask_security/templates/security/us_setup.html:22
543 msgid "Passwordless QRCode"
544 msgstr ""
545
546 #: flask_security/templates/security/us_setup.html:25
547 #: flask_security/templates/security/us_signin.html:23
548 #: flask_security/templates/security/us_verify.html:21
549 #, fuzzy
550 msgid "Code has been sent"
551 msgstr "Das Passwort wurde zurückgesetzt"
552
553 #: flask_security/templates/security/us_setup.html:29
554 msgid "No methods have been enabled - nothing to setup"
555 msgstr ""
556
557 #: flask_security/templates/security/us_signin.html:15
558 #: flask_security/templates/security/us_verify.html:13
559 msgid "Request one-time code be sent"
560 msgstr ""
561
562 #: flask_security/templates/security/us_verify.html:6
563 #, fuzzy
564 msgid "Please re-authenticate"
565 msgstr "Bitte neu authentifizieren, um auf diese Seite zuzugreifen."
566
567 #: flask_security/templates/security/email/change_notice.html:1
568 msgid "Your password has been changed."
569 msgstr "Das Passwort wurde geändert."
570
571 #: flask_security/templates/security/email/change_notice.html:3
572 msgid "If you did not change your password,"
573 msgstr "Falls das Passwort nicht geändert wurde"
574
575 #: flask_security/templates/security/email/change_notice.html:3
576 msgid "click here to reset it"
577 msgstr "hier klicken, um es zurückzusetzen"
578
579 #: flask_security/templates/security/email/confirmation_instructions.html:1
580 msgid "Please confirm your email through the link below:"
581 msgstr "Bitte die E-Mail-Adresse durch den Link unten bestätigen:"
582
583 #: flask_security/templates/security/email/confirmation_instructions.html:3
584 #: flask_security/templates/security/email/welcome.html:6
585 msgid "Confirm my account"
586 msgstr "Das Konto bestätigen"
587
588 #: flask_security/templates/security/email/login_instructions.html:1
589 #: flask_security/templates/security/email/welcome.html:1
590 #, python-format
591 msgid "Welcome %(email)s!"
592 msgstr "Willkommen %(email)s!"
593
594 #: flask_security/templates/security/email/login_instructions.html:3
595 msgid "You can log into your account through the link below:"
596 msgstr "Die Anmeldung kann über den Link unten erfolgen:"
597
598 #: flask_security/templates/security/email/login_instructions.html:5
599 msgid "Login now"
600 msgstr "Jetzt anmelden"
601
602 #: flask_security/templates/security/email/reset_instructions.html:1
603 msgid "Click here to reset your password"
604 msgstr "Hier klicken, um das Passwort zurückzusetzen"
605
606 #: flask_security/templates/security/email/two_factor_instructions.html:3
607 msgid "You can log into your account using the following code:"
608 msgstr ""
609
610 #: flask_security/templates/security/email/two_factor_rescue.html:1
611 msgid "can not access mail account"
612 msgstr ""
613
614 #: flask_security/templates/security/email/us_instructions.html:3
615 #, fuzzy
616 msgid "You can sign into your account using the following code:"
617 msgstr "Die Anmeldung kann über den Link unten erfolgen:"
618
619 #: flask_security/templates/security/email/us_instructions.html:6
620 #, fuzzy
621 msgid "Or use the the link below:"
622 msgstr "Die E-Mail-Adresse kann über den Link unten bestätigt werden"
623
624 #: flask_security/templates/security/email/welcome.html:4
625 msgid "You can confirm your email through the link below:"
626 msgstr "Die E-Mail-Adresse kann über den Link unten bestätigt werden"
627
0 # Spanish (Spain) translations for Flask-Security.
1 # Copyright (C) 2017 DINSIC
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # Mauko Quiroga <[email protected]>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-08-25 17:21+0200\n"
12 "Last-Translator: Mauko Quiroga <[email protected]>\n"
13 "Language: es_ES\n"
14 "Language-Team: \n"
15 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Inicio de sesión necesario"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Bienvenido"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Por favor, confirma tu correo electrónico"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Instrucciones para iniciar sesión"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "Tu contraseña ha sido restablecida"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "Tu contraseña ha sido cambiada"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instrucciones de recuperación de contraseña"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "No tienes permiso para consultar este recurso."
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Deber iniciar sesión nuevamente para poder acceder a esta página."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr ""
85 "Gracias. Un correo con instrucciones sobre cómo confirmar tu cuenta ha "
86 "sido enviado a %(email)s."
87
88 #: flask_security/core.py:296
89 msgid "Thank you. Your email has been confirmed."
90 msgstr "Gracias. Tu correo electrónico ha sido confirmado."
91
92 #: flask_security/core.py:297
93 msgid "Your email has already been confirmed."
94 msgstr "Tu correo electrónico ya ha sido confirmado."
95
96 #: flask_security/core.py:298
97 msgid "Invalid confirmation token."
98 msgstr "Autentificador de confirmación inválido."
99
100 #: flask_security/core.py:300
101 #, python-format
102 msgid "%(email)s is already associated with an account."
103 msgstr "%(email)s ya está asociado a una cuenta."
104
105 #: flask_security/core.py:303
106 msgid "Password does not match"
107 msgstr "La contraseña no coincide"
108
109 #: flask_security/core.py:304
110 msgid "Passwords do not match"
111 msgstr "Las contraseñas no coinciden"
112
113 #: flask_security/core.py:305
114 msgid "Redirections outside the domain are forbidden"
115 msgstr "Las redirecciones a sitios web externos están prohibidas"
116
117 #: flask_security/core.py:307
118 #, python-format
119 msgid "Instructions to reset your password have been sent to %(email)s."
120 msgstr ""
121 "Las instrucciones para restablecer tu contraseña han sido enviadas a "
122 "%(email)s."
123
124 #: flask_security/core.py:311
125 #, python-format
126 msgid ""
127 "You did not reset your password within %(within)s. New instructions have "
128 "been sent to %(email)s."
129 msgstr ""
130 "No restableciste tu contraseña antes de %(within)s. Nuevas instrucciones "
131 "han sido enviadas a %(email)s."
132
133 #: flask_security/core.py:317
134 msgid "Invalid reset password token."
135 msgstr "Autentificador de restablecimiento de contraseña inválido."
136
137 #: flask_security/core.py:318
138 msgid "Email requires confirmation."
139 msgstr "El correo electrónico requiere confirmación."
140
141 #: flask_security/core.py:320
142 #, python-format
143 msgid "Confirmation instructions have been sent to %(email)s."
144 msgstr "Las instrucciones de confirmación se han enviado a %(email)s."
145
146 #: flask_security/core.py:324
147 #, python-format
148 msgid ""
149 "You did not confirm your email within %(within)s. New instructions to "
150 "confirm your email have been sent to %(email)s."
151 msgstr ""
152 "No confirmaste tu correo electrónico antes de %(within)s. Nuevas "
153 "instrucciones para confirmar tu correo electrónico han sido enviadas a "
154 "%(email)s."
155
156 #: flask_security/core.py:332
157 #, python-format
158 msgid ""
159 "You did not login within %(within)s. New instructions to login have been "
160 "sent to %(email)s."
161 msgstr ""
162 "No iniciaste sesión antes de %(within)s. Nuevas instrucciones para "
163 "iniciar sesión han sido enviadas a %(email)s."
164
165 #: flask_security/core.py:339
166 #, python-format
167 msgid "Instructions to login have been sent to %(email)s."
168 msgstr "Instrucciones para iniciar sesión han sido enviadas a %(email)s."
169
170 #: flask_security/core.py:342
171 msgid "Invalid login token."
172 msgstr "Autenticador de inicio de sesión inválido."
173
174 #: flask_security/core.py:343
175 msgid "Account is disabled."
176 msgstr "Cuenta deshabilitada."
177
178 #: flask_security/core.py:344
179 msgid "Email not provided"
180 msgstr "Correo electrónico no indicado"
181
182 #: flask_security/core.py:345
183 msgid "Invalid email address"
184 msgstr "Dirección de correo electrónico inválida"
185
186 #: flask_security/core.py:346
187 #, fuzzy
188 msgid "Invalid code"
189 msgstr "Contraseña inválida"
190
191 #: flask_security/core.py:347
192 msgid "Password not provided"
193 msgstr "Contraseña no indicada"
194
195 #: flask_security/core.py:348
196 msgid "No password is set for this user"
197 msgstr "Ninguna contraseña ha sido definida para este·a usuario·a"
198
199 #: flask_security/core.py:350
200 #, fuzzy, python-format
201 msgid "Password must be at least %(length)s characters"
202 msgstr "La contraseña debe contar al menos con 6 caracteres"
203
204 #: flask_security/core.py:353
205 msgid "Password not complex enough"
206 msgstr ""
207
208 #: flask_security/core.py:354
209 msgid "Password on breached list"
210 msgstr ""
211
212 #: flask_security/core.py:356
213 msgid "Failed to contact breached passwords site"
214 msgstr ""
215
216 #: flask_security/core.py:359
217 msgid "Phone number not valid e.g. missing country code"
218 msgstr ""
219
220 #: flask_security/core.py:360
221 msgid "Specified user does not exist"
222 msgstr "Usuario·a especificado·a no existe"
223
224 #: flask_security/core.py:361
225 msgid "Invalid password"
226 msgstr "Contraseña inválida"
227
228 #: flask_security/core.py:362
229 msgid "Password or code submitted is not valid"
230 msgstr ""
231
232 #: flask_security/core.py:363
233 msgid "You have successfully logged in."
234 msgstr "Has iniciado sesión con éxito."
235
236 #: flask_security/core.py:364
237 msgid "Forgot password?"
238 msgstr "¿Has olvidado tu contraseña?"
239
240 #: flask_security/core.py:366
241 msgid ""
242 "You successfully reset your password and you have been logged in "
243 "automatically."
244 msgstr ""
245 "Has restablecido tu contraseña con éxito y has iniciado sesión "
246 "automáticamente."
247
248 #: flask_security/core.py:373
249 msgid "Your new password must be different than your previous password."
250 msgstr "Tu nueva contraseña debe ser diferente de la antigua."
251
252 #: flask_security/core.py:376
253 msgid "You successfully changed your password."
254 msgstr "Has cambiado tu contraseña con éxito."
255
256 #: flask_security/core.py:377
257 msgid "Please log in to access this page."
258 msgstr "Debes iniciar sesión para poder acceder a esta página."
259
260 #: flask_security/core.py:378
261 msgid "Please reauthenticate to access this page."
262 msgstr "Deber iniciar sesión nuevamente para poder acceder a esta página."
263
264 #: flask_security/core.py:379
265 msgid "Reauthentication successful"
266 msgstr ""
267
268 #: flask_security/core.py:381
269 msgid "You can only access this endpoint when not logged in."
270 msgstr ""
271
272 #: flask_security/core.py:384
273 msgid "Failed to send code. Please try again later"
274 msgstr ""
275
276 #: flask_security/core.py:385
277 msgid "Invalid Token"
278 msgstr ""
279
280 #: flask_security/core.py:386
281 msgid "Your token has been confirmed"
282 msgstr ""
283
284 #: flask_security/core.py:388
285 msgid "You successfully changed your two-factor method."
286 msgstr ""
287
288 #: flask_security/core.py:392
289 msgid "You successfully confirmed password"
290 msgstr ""
291
292 #: flask_security/core.py:396
293 msgid "Password confirmation is needed in order to access page"
294 msgstr ""
295
296 #: flask_security/core.py:400
297 msgid "You currently do not have permissions to access this page"
298 msgstr ""
299
300 #: flask_security/core.py:403
301 msgid "Marked method is not valid"
302 msgstr ""
303
304 #: flask_security/core.py:405
305 msgid "You successfully disabled two factor authorization."
306 msgstr ""
307
308 #: flask_security/core.py:408
309 msgid "Requested method is not valid"
310 msgstr ""
311
312 #: flask_security/core.py:410
313 #, python-format
314 msgid "Setup must be completed within %(within)s. Please start over."
315 msgstr ""
316
317 #: flask_security/core.py:413
318 msgid "Unified sign in setup successful"
319 msgstr ""
320
321 #: flask_security/core.py:414
322 msgid "You must specify a valid identity to sign in"
323 msgstr ""
324
325 #: flask_security/core.py:415
326 #, python-format
327 msgid "Use this code to sign in: %(code)s."
328 msgstr ""
329
330 #: flask_security/forms.py:50
331 msgid "Email Address"
332 msgstr "Correo electrónico"
333
334 #: flask_security/forms.py:51
335 msgid "Password"
336 msgstr "Contraseña"
337
338 #: flask_security/forms.py:52
339 msgid "Remember Me"
340 msgstr "Recordarme"
341
342 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
343 #: flask_security/templates/security/login_user.html:6
344 #: flask_security/templates/security/send_login.html:6
345 msgid "Login"
346 msgstr "Iniciar sesión"
347
348 #: flask_security/forms.py:54
349 #: flask_security/templates/security/email/us_instructions.html:8
350 #: flask_security/templates/security/us_signin.html:6
351 msgid "Sign In"
352 msgstr ""
353
354 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
355 #: flask_security/templates/security/register_user.html:6
356 msgid "Register"
357 msgstr "Registrarse"
358
359 #: flask_security/forms.py:56
360 msgid "Resend Confirmation Instructions"
361 msgstr "Reenviar instrucciones de confirmación"
362
363 #: flask_security/forms.py:57
364 msgid "Recover Password"
365 msgstr "Recuperar contraseña"
366
367 #: flask_security/forms.py:58
368 msgid "Reset Password"
369 msgstr "Restablecer contraseña"
370
371 #: flask_security/forms.py:59
372 msgid "Retype Password"
373 msgstr "Escribir contraseña nuevamente"
374
375 #: flask_security/forms.py:60
376 msgid "New Password"
377 msgstr "Nueva contraseña"
378
379 #: flask_security/forms.py:61
380 msgid "Change Password"
381 msgstr "Cambiar la contraseña"
382
383 #: flask_security/forms.py:62
384 msgid "Send Login Link"
385 msgstr "Enviar enlace para iniciar sesión"
386
387 #: flask_security/forms.py:63
388 msgid "Verify Password"
389 msgstr ""
390
391 #: flask_security/forms.py:64
392 msgid "Change Method"
393 msgstr ""
394
395 #: flask_security/forms.py:65
396 msgid "Phone Number"
397 msgstr ""
398
399 #: flask_security/forms.py:66
400 msgid "Authentication Code"
401 msgstr ""
402
403 #: flask_security/forms.py:67
404 msgid "Submit"
405 msgstr ""
406
407 #: flask_security/forms.py:68
408 msgid "Submit Code"
409 msgstr ""
410
411 #: flask_security/forms.py:69
412 msgid "Error(s)"
413 msgstr ""
414
415 #: flask_security/forms.py:70
416 msgid "Identity"
417 msgstr ""
418
419 #: flask_security/forms.py:71
420 msgid "Send Code"
421 msgstr ""
422
423 #: flask_security/forms.py:72
424 #, fuzzy
425 msgid "Passcode"
426 msgstr "Contraseña"
427
428 #: flask_security/unified_signin.py:145
429 #, fuzzy
430 msgid "Code or Password"
431 msgstr "Recuperar contraseña"
432
433 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
434 msgid "Available Methods"
435 msgstr ""
436
437 #: flask_security/unified_signin.py:151
438 msgid "Via email"
439 msgstr ""
440
441 #: flask_security/unified_signin.py:151
442 msgid "Via SMS"
443 msgstr ""
444
445 #: flask_security/unified_signin.py:272
446 msgid "Set up using email"
447 msgstr ""
448
449 #: flask_security/unified_signin.py:275
450 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
451 msgstr ""
452
453 #: flask_security/unified_signin.py:277
454 msgid "Set up using SMS"
455 msgstr ""
456
457 #: flask_security/templates/security/_menu.html:2
458 msgid "Menu"
459 msgstr "Menú"
460
461 #: flask_security/templates/security/_menu.html:8
462 msgid "Unified Sign In"
463 msgstr ""
464
465 #: flask_security/templates/security/_menu.html:14
466 msgid "Forgot password"
467 msgstr "Olvidé mi contraseña"
468
469 #: flask_security/templates/security/_menu.html:17
470 msgid "Confirm account"
471 msgstr "Confirmar cuenta"
472
473 #: flask_security/templates/security/change_password.html:6
474 msgid "Change password"
475 msgstr "Cambiar la contraseña"
476
477 #: flask_security/templates/security/forgot_password.html:6
478 msgid "Send password reset instructions"
479 msgstr "Enviar instrucciones para restablecer la contraseña"
480
481 #: flask_security/templates/security/reset_password.html:6
482 msgid "Reset password"
483 msgstr "Restablecer contraseña"
484
485 #: flask_security/templates/security/send_confirmation.html:6
486 msgid "Resend confirmation instructions"
487 msgstr "Reenviar instrucciones de confirmación"
488
489 #: flask_security/templates/security/two_factor_setup.html:6
490 msgid "Two-factor authentication adds an extra layer of security to your account"
491 msgstr ""
492
493 #: flask_security/templates/security/two_factor_setup.html:7
494 msgid ""
495 "In addition to your username and password, you'll need to use a code that"
496 " we will send you"
497 msgstr ""
498
499 #: flask_security/templates/security/two_factor_setup.html:18
500 msgid "To complete logging in, please enter the code sent to your mail"
501 msgstr ""
502
503 #: flask_security/templates/security/two_factor_setup.html:21
504 #: flask_security/templates/security/us_setup.html:21
505 msgid ""
506 "Open your authenticator app on your device and scan the following qrcode "
507 "to start receiving codes:"
508 msgstr ""
509
510 #: flask_security/templates/security/two_factor_setup.html:22
511 msgid "Two factor authentication code"
512 msgstr ""
513
514 #: flask_security/templates/security/two_factor_setup.html:25
515 msgid "To Which Phone Number Should We Send Code To?"
516 msgstr ""
517
518 #: flask_security/templates/security/two_factor_verify_code.html:6
519 msgid "Two-factor Authentication"
520 msgstr ""
521
522 #: flask_security/templates/security/two_factor_verify_code.html:7
523 msgid "Please enter your authentication code"
524 msgstr ""
525
526 #: flask_security/templates/security/two_factor_verify_code.html:18
527 msgid "The code for authentication was sent to your email address"
528 msgstr ""
529
530 #: flask_security/templates/security/two_factor_verify_code.html:21
531 msgid "A mail was sent to us in order to reset your application account"
532 msgstr ""
533
534 #: flask_security/templates/security/two_factor_verify_password.html:6
535 #: flask_security/templates/security/verify.html:6
536 msgid "Please Enter Your Password"
537 msgstr ""
538
539 #: flask_security/templates/security/us_setup.html:6
540 msgid "Setup Unified Sign In options"
541 msgstr ""
542
543 #: flask_security/templates/security/us_setup.html:22
544 msgid "Passwordless QRCode"
545 msgstr ""
546
547 #: flask_security/templates/security/us_setup.html:25
548 #: flask_security/templates/security/us_signin.html:23
549 #: flask_security/templates/security/us_verify.html:21
550 #, fuzzy
551 msgid "Code has been sent"
552 msgstr "Tu contraseña ha sido restablecida"
553
554 #: flask_security/templates/security/us_setup.html:29
555 msgid "No methods have been enabled - nothing to setup"
556 msgstr ""
557
558 #: flask_security/templates/security/us_signin.html:15
559 #: flask_security/templates/security/us_verify.html:13
560 msgid "Request one-time code be sent"
561 msgstr ""
562
563 #: flask_security/templates/security/us_verify.html:6
564 #, fuzzy
565 msgid "Please re-authenticate"
566 msgstr "Deber iniciar sesión nuevamente para poder acceder a esta página."
567
568 #: flask_security/templates/security/email/change_notice.html:1
569 msgid "Your password has been changed."
570 msgstr "Tu contraseña ha sido cambiada."
571
572 #: flask_security/templates/security/email/change_notice.html:3
573 msgid "If you did not change your password,"
574 msgstr "Si no has cambiado tu contraseña,"
575
576 #: flask_security/templates/security/email/change_notice.html:3
577 msgid "click here to reset it"
578 msgstr "haz clic aquí para restablecerla"
579
580 #: flask_security/templates/security/email/confirmation_instructions.html:1
581 msgid "Please confirm your email through the link below:"
582 msgstr "Confirma tu correo electrónico haciendo clic aquí:"
583
584 #: flask_security/templates/security/email/confirmation_instructions.html:3
585 #: flask_security/templates/security/email/welcome.html:6
586 msgid "Confirm my account"
587 msgstr "Confirmar mi cuenta"
588
589 #: flask_security/templates/security/email/login_instructions.html:1
590 #: flask_security/templates/security/email/welcome.html:1
591 #, python-format
592 msgid "Welcome %(email)s!"
593 msgstr "¡Bienvenido %(email)s!"
594
595 #: flask_security/templates/security/email/login_instructions.html:3
596 msgid "You can log into your account through the link below:"
597 msgstr "Inicia sesión haciendo clic aquí:"
598
599 #: flask_security/templates/security/email/login_instructions.html:5
600 msgid "Login now"
601 msgstr "Iniciar sesión ahora"
602
603 #: flask_security/templates/security/email/reset_instructions.html:1
604 msgid "Click here to reset your password"
605 msgstr "Haz clic aquí para restablecer la contraseña"
606
607 #: flask_security/templates/security/email/two_factor_instructions.html:3
608 msgid "You can log into your account using the following code:"
609 msgstr ""
610
611 #: flask_security/templates/security/email/two_factor_rescue.html:1
612 msgid "can not access mail account"
613 msgstr ""
614
615 #: flask_security/templates/security/email/us_instructions.html:3
616 #, fuzzy
617 msgid "You can sign into your account using the following code:"
618 msgstr "Inicia sesión haciendo clic aquí:"
619
620 #: flask_security/templates/security/email/us_instructions.html:6
621 #, fuzzy
622 msgid "Or use the the link below:"
623 msgstr "Confirma tu correo electrónico haciendo clic aquí:"
624
625 #: flask_security/templates/security/email/welcome.html:4
626 msgid "You can confirm your email through the link below:"
627 msgstr "Confirma tu correo electrónico haciendo clic aquí:"
628
0 # Translations template for Flask-Security.
1 # Copyright (C) 2020 ORGANIZATION
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
5 #
6 #, fuzzy
7 msgid ""
8 msgstr ""
9 "Project-Id-Version: Flask-Security 3.4.0\n"
10 "Report-Msgid-Bugs-To: [email protected]\n"
11 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 "Language-Team: LANGUAGE <[email protected]>\n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=utf-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Generated-By: Babel 2.8.0\n"
19
20 #: flask_security/core.py:207
21 msgid "Login Required"
22 msgstr ""
23
24 #: flask_security/core.py:208
25 #: flask_security/templates/security/email/two_factor_instructions.html:1
26 #: flask_security/templates/security/email/us_instructions.html:1
27 msgid "Welcome"
28 msgstr ""
29
30 #: flask_security/core.py:209
31 msgid "Please confirm your email"
32 msgstr ""
33
34 #: flask_security/core.py:210
35 msgid "Login instructions"
36 msgstr ""
37
38 #: flask_security/core.py:211
39 #: flask_security/templates/security/email/reset_notice.html:1
40 msgid "Your password has been reset"
41 msgstr ""
42
43 #: flask_security/core.py:212
44 msgid "Your password has been changed"
45 msgstr ""
46
47 #: flask_security/core.py:213
48 msgid "Password reset instructions"
49 msgstr ""
50
51 #: flask_security/core.py:216
52 msgid "Two-factor Login"
53 msgstr ""
54
55 #: flask_security/core.py:217
56 msgid "Two-factor Rescue"
57 msgstr ""
58
59 #: flask_security/core.py:266
60 msgid "Verification Code"
61 msgstr ""
62
63 #: flask_security/core.py:282
64 msgid "Input not appropriate for requested API"
65 msgstr ""
66
67 #: flask_security/core.py:283
68 msgid "You do not have permission to view this resource."
69 msgstr ""
70
71 #: flask_security/core.py:285
72 msgid "You are not authenticated. Please supply the correct credentials."
73 msgstr ""
74
75 #: flask_security/core.py:289
76 msgid "You must re-authenticate to access this endpoint"
77 msgstr ""
78
79 #: flask_security/core.py:293
80 #, python-format
81 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
82 msgstr ""
83
84 #: flask_security/core.py:296
85 msgid "Thank you. Your email has been confirmed."
86 msgstr ""
87
88 #: flask_security/core.py:297
89 msgid "Your email has already been confirmed."
90 msgstr ""
91
92 #: flask_security/core.py:298
93 msgid "Invalid confirmation token."
94 msgstr ""
95
96 #: flask_security/core.py:300
97 #, python-format
98 msgid "%(email)s is already associated with an account."
99 msgstr ""
100
101 #: flask_security/core.py:303
102 msgid "Password does not match"
103 msgstr ""
104
105 #: flask_security/core.py:304
106 msgid "Passwords do not match"
107 msgstr ""
108
109 #: flask_security/core.py:305
110 msgid "Redirections outside the domain are forbidden"
111 msgstr ""
112
113 #: flask_security/core.py:307
114 #, python-format
115 msgid "Instructions to reset your password have been sent to %(email)s."
116 msgstr ""
117
118 #: flask_security/core.py:311
119 #, python-format
120 msgid ""
121 "You did not reset your password within %(within)s. New instructions have "
122 "been sent to %(email)s."
123 msgstr ""
124
125 #: flask_security/core.py:317
126 msgid "Invalid reset password token."
127 msgstr ""
128
129 #: flask_security/core.py:318
130 msgid "Email requires confirmation."
131 msgstr ""
132
133 #: flask_security/core.py:320
134 #, python-format
135 msgid "Confirmation instructions have been sent to %(email)s."
136 msgstr ""
137
138 #: flask_security/core.py:324
139 #, python-format
140 msgid ""
141 "You did not confirm your email within %(within)s. New instructions to "
142 "confirm your email have been sent to %(email)s."
143 msgstr ""
144
145 #: flask_security/core.py:332
146 #, python-format
147 msgid ""
148 "You did not login within %(within)s. New instructions to login have been "
149 "sent to %(email)s."
150 msgstr ""
151
152 #: flask_security/core.py:339
153 #, python-format
154 msgid "Instructions to login have been sent to %(email)s."
155 msgstr ""
156
157 #: flask_security/core.py:342
158 msgid "Invalid login token."
159 msgstr ""
160
161 #: flask_security/core.py:343
162 msgid "Account is disabled."
163 msgstr ""
164
165 #: flask_security/core.py:344
166 msgid "Email not provided"
167 msgstr ""
168
169 #: flask_security/core.py:345
170 msgid "Invalid email address"
171 msgstr ""
172
173 #: flask_security/core.py:346
174 msgid "Invalid code"
175 msgstr ""
176
177 #: flask_security/core.py:347
178 msgid "Password not provided"
179 msgstr ""
180
181 #: flask_security/core.py:348
182 msgid "No password is set for this user"
183 msgstr ""
184
185 #: flask_security/core.py:350
186 #, python-format
187 msgid "Password must be at least %(length)s characters"
188 msgstr ""
189
190 #: flask_security/core.py:353
191 msgid "Password not complex enough"
192 msgstr ""
193
194 #: flask_security/core.py:354
195 msgid "Password on breached list"
196 msgstr ""
197
198 #: flask_security/core.py:356
199 msgid "Failed to contact breached passwords site"
200 msgstr ""
201
202 #: flask_security/core.py:359
203 msgid "Phone number not valid e.g. missing country code"
204 msgstr ""
205
206 #: flask_security/core.py:360
207 msgid "Specified user does not exist"
208 msgstr ""
209
210 #: flask_security/core.py:361
211 msgid "Invalid password"
212 msgstr ""
213
214 #: flask_security/core.py:362
215 msgid "Password or code submitted is not valid"
216 msgstr ""
217
218 #: flask_security/core.py:363
219 msgid "You have successfully logged in."
220 msgstr ""
221
222 #: flask_security/core.py:364
223 msgid "Forgot password?"
224 msgstr ""
225
226 #: flask_security/core.py:366
227 msgid ""
228 "You successfully reset your password and you have been logged in "
229 "automatically."
230 msgstr ""
231
232 #: flask_security/core.py:373
233 msgid "Your new password must be different than your previous password."
234 msgstr ""
235
236 #: flask_security/core.py:376
237 msgid "You successfully changed your password."
238 msgstr ""
239
240 #: flask_security/core.py:377
241 msgid "Please log in to access this page."
242 msgstr ""
243
244 #: flask_security/core.py:378
245 msgid "Please reauthenticate to access this page."
246 msgstr ""
247
248 #: flask_security/core.py:379
249 msgid "Reauthentication successful"
250 msgstr ""
251
252 #: flask_security/core.py:381
253 msgid "You can only access this endpoint when not logged in."
254 msgstr ""
255
256 #: flask_security/core.py:384
257 msgid "Failed to send code. Please try again later"
258 msgstr ""
259
260 #: flask_security/core.py:385
261 msgid "Invalid Token"
262 msgstr ""
263
264 #: flask_security/core.py:386
265 msgid "Your token has been confirmed"
266 msgstr ""
267
268 #: flask_security/core.py:388
269 msgid "You successfully changed your two-factor method."
270 msgstr ""
271
272 #: flask_security/core.py:392
273 msgid "You successfully confirmed password"
274 msgstr ""
275
276 #: flask_security/core.py:396
277 msgid "Password confirmation is needed in order to access page"
278 msgstr ""
279
280 #: flask_security/core.py:400
281 msgid "You currently do not have permissions to access this page"
282 msgstr ""
283
284 #: flask_security/core.py:403
285 msgid "Marked method is not valid"
286 msgstr ""
287
288 #: flask_security/core.py:405
289 msgid "You successfully disabled two factor authorization."
290 msgstr ""
291
292 #: flask_security/core.py:408
293 msgid "Requested method is not valid"
294 msgstr ""
295
296 #: flask_security/core.py:410
297 #, python-format
298 msgid "Setup must be completed within %(within)s. Please start over."
299 msgstr ""
300
301 #: flask_security/core.py:413
302 msgid "Unified sign in setup successful"
303 msgstr ""
304
305 #: flask_security/core.py:414
306 msgid "You must specify a valid identity to sign in"
307 msgstr ""
308
309 #: flask_security/core.py:415
310 #, python-format
311 msgid "Use this code to sign in: %(code)s."
312 msgstr ""
313
314 #: flask_security/forms.py:50
315 msgid "Email Address"
316 msgstr ""
317
318 #: flask_security/forms.py:51
319 msgid "Password"
320 msgstr ""
321
322 #: flask_security/forms.py:52
323 msgid "Remember Me"
324 msgstr ""
325
326 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
327 #: flask_security/templates/security/login_user.html:6
328 #: flask_security/templates/security/send_login.html:6
329 msgid "Login"
330 msgstr ""
331
332 #: flask_security/forms.py:54
333 #: flask_security/templates/security/email/us_instructions.html:8
334 #: flask_security/templates/security/us_signin.html:6
335 msgid "Sign In"
336 msgstr ""
337
338 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
339 #: flask_security/templates/security/register_user.html:6
340 msgid "Register"
341 msgstr ""
342
343 #: flask_security/forms.py:56
344 msgid "Resend Confirmation Instructions"
345 msgstr ""
346
347 #: flask_security/forms.py:57
348 msgid "Recover Password"
349 msgstr ""
350
351 #: flask_security/forms.py:58
352 msgid "Reset Password"
353 msgstr ""
354
355 #: flask_security/forms.py:59
356 msgid "Retype Password"
357 msgstr ""
358
359 #: flask_security/forms.py:60
360 msgid "New Password"
361 msgstr ""
362
363 #: flask_security/forms.py:61
364 msgid "Change Password"
365 msgstr ""
366
367 #: flask_security/forms.py:62
368 msgid "Send Login Link"
369 msgstr ""
370
371 #: flask_security/forms.py:63
372 msgid "Verify Password"
373 msgstr ""
374
375 #: flask_security/forms.py:64
376 msgid "Change Method"
377 msgstr ""
378
379 #: flask_security/forms.py:65
380 msgid "Phone Number"
381 msgstr ""
382
383 #: flask_security/forms.py:66
384 msgid "Authentication Code"
385 msgstr ""
386
387 #: flask_security/forms.py:67
388 msgid "Submit"
389 msgstr ""
390
391 #: flask_security/forms.py:68
392 msgid "Submit Code"
393 msgstr ""
394
395 #: flask_security/forms.py:69
396 msgid "Error(s)"
397 msgstr ""
398
399 #: flask_security/forms.py:70
400 msgid "Identity"
401 msgstr ""
402
403 #: flask_security/forms.py:71
404 msgid "Send Code"
405 msgstr ""
406
407 #: flask_security/forms.py:72
408 msgid "Passcode"
409 msgstr ""
410
411 #: flask_security/unified_signin.py:145
412 msgid "Code or Password"
413 msgstr ""
414
415 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
416 msgid "Available Methods"
417 msgstr ""
418
419 #: flask_security/unified_signin.py:151
420 msgid "Via email"
421 msgstr ""
422
423 #: flask_security/unified_signin.py:151
424 msgid "Via SMS"
425 msgstr ""
426
427 #: flask_security/unified_signin.py:272
428 msgid "Set up using email"
429 msgstr ""
430
431 #: flask_security/unified_signin.py:275
432 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
433 msgstr ""
434
435 #: flask_security/unified_signin.py:277
436 msgid "Set up using SMS"
437 msgstr ""
438
439 #: flask_security/templates/security/_menu.html:2
440 msgid "Menu"
441 msgstr ""
442
443 #: flask_security/templates/security/_menu.html:8
444 msgid "Unified Sign In"
445 msgstr ""
446
447 #: flask_security/templates/security/_menu.html:14
448 msgid "Forgot password"
449 msgstr ""
450
451 #: flask_security/templates/security/_menu.html:17
452 msgid "Confirm account"
453 msgstr ""
454
455 #: flask_security/templates/security/change_password.html:6
456 msgid "Change password"
457 msgstr ""
458
459 #: flask_security/templates/security/forgot_password.html:6
460 msgid "Send password reset instructions"
461 msgstr ""
462
463 #: flask_security/templates/security/reset_password.html:6
464 msgid "Reset password"
465 msgstr ""
466
467 #: flask_security/templates/security/send_confirmation.html:6
468 msgid "Resend confirmation instructions"
469 msgstr ""
470
471 #: flask_security/templates/security/two_factor_setup.html:6
472 msgid "Two-factor authentication adds an extra layer of security to your account"
473 msgstr ""
474
475 #: flask_security/templates/security/two_factor_setup.html:7
476 msgid ""
477 "In addition to your username and password, you'll need to use a code that"
478 " we will send you"
479 msgstr ""
480
481 #: flask_security/templates/security/two_factor_setup.html:18
482 msgid "To complete logging in, please enter the code sent to your mail"
483 msgstr ""
484
485 #: flask_security/templates/security/two_factor_setup.html:21
486 #: flask_security/templates/security/us_setup.html:21
487 msgid ""
488 "Open your authenticator app on your device and scan the following qrcode "
489 "to start receiving codes:"
490 msgstr ""
491
492 #: flask_security/templates/security/two_factor_setup.html:22
493 msgid "Two factor authentication code"
494 msgstr ""
495
496 #: flask_security/templates/security/two_factor_setup.html:25
497 msgid "To Which Phone Number Should We Send Code To?"
498 msgstr ""
499
500 #: flask_security/templates/security/two_factor_verify_code.html:6
501 msgid "Two-factor Authentication"
502 msgstr ""
503
504 #: flask_security/templates/security/two_factor_verify_code.html:7
505 msgid "Please enter your authentication code"
506 msgstr ""
507
508 #: flask_security/templates/security/two_factor_verify_code.html:18
509 msgid "The code for authentication was sent to your email address"
510 msgstr ""
511
512 #: flask_security/templates/security/two_factor_verify_code.html:21
513 msgid "A mail was sent to us in order to reset your application account"
514 msgstr ""
515
516 #: flask_security/templates/security/two_factor_verify_password.html:6
517 #: flask_security/templates/security/verify.html:6
518 msgid "Please Enter Your Password"
519 msgstr ""
520
521 #: flask_security/templates/security/us_setup.html:6
522 msgid "Setup Unified Sign In options"
523 msgstr ""
524
525 #: flask_security/templates/security/us_setup.html:22
526 msgid "Passwordless QRCode"
527 msgstr ""
528
529 #: flask_security/templates/security/us_setup.html:25
530 #: flask_security/templates/security/us_signin.html:23
531 #: flask_security/templates/security/us_verify.html:21
532 msgid "Code has been sent"
533 msgstr ""
534
535 #: flask_security/templates/security/us_setup.html:29
536 msgid "No methods have been enabled - nothing to setup"
537 msgstr ""
538
539 #: flask_security/templates/security/us_signin.html:15
540 #: flask_security/templates/security/us_verify.html:13
541 msgid "Request one-time code be sent"
542 msgstr ""
543
544 #: flask_security/templates/security/us_verify.html:6
545 msgid "Please re-authenticate"
546 msgstr ""
547
548 #: flask_security/templates/security/email/change_notice.html:1
549 msgid "Your password has been changed."
550 msgstr ""
551
552 #: flask_security/templates/security/email/change_notice.html:3
553 msgid "If you did not change your password,"
554 msgstr ""
555
556 #: flask_security/templates/security/email/change_notice.html:3
557 msgid "click here to reset it"
558 msgstr ""
559
560 #: flask_security/templates/security/email/confirmation_instructions.html:1
561 msgid "Please confirm your email through the link below:"
562 msgstr ""
563
564 #: flask_security/templates/security/email/confirmation_instructions.html:3
565 #: flask_security/templates/security/email/welcome.html:6
566 msgid "Confirm my account"
567 msgstr ""
568
569 #: flask_security/templates/security/email/login_instructions.html:1
570 #: flask_security/templates/security/email/welcome.html:1
571 #, python-format
572 msgid "Welcome %(email)s!"
573 msgstr ""
574
575 #: flask_security/templates/security/email/login_instructions.html:3
576 msgid "You can log into your account through the link below:"
577 msgstr ""
578
579 #: flask_security/templates/security/email/login_instructions.html:5
580 msgid "Login now"
581 msgstr ""
582
583 #: flask_security/templates/security/email/reset_instructions.html:1
584 msgid "Click here to reset your password"
585 msgstr ""
586
587 #: flask_security/templates/security/email/two_factor_instructions.html:3
588 msgid "You can log into your account using the following code:"
589 msgstr ""
590
591 #: flask_security/templates/security/email/two_factor_rescue.html:1
592 msgid "can not access mail account"
593 msgstr ""
594
595 #: flask_security/templates/security/email/us_instructions.html:3
596 msgid "You can sign into your account using the following code:"
597 msgstr ""
598
599 #: flask_security/templates/security/email/us_instructions.html:6
600 msgid "Or use the the link below:"
601 msgstr ""
602
603 #: flask_security/templates/security/email/welcome.html:4
604 msgid "You can confirm your email through the link below:"
605 msgstr ""
606
0 # French (France) translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # Alexandre Bulté <[email protected]>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-06-08 10:13+0200\n"
12 "Last-Translator: Alexandre Bulté <[email protected]>\n"
13 "Language: fr_FR\n"
14 "Language-Team: fr_FR <[email protected]>\n"
15 "Plural-Forms: nplurals=2; plural=(n > 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Connexion requise"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Bienvenue"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Merci de confirmer votre adresse email"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Instructions de connexion"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "Votre mot de passe a été réinitialisé"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "Votre mot de passe a été changé"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instructions de réinitialisation de votre mot de passe"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "Vous n'avez pas l'autorisation d'accéder à cette ressource."
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Merci de vous reconnecter pour accéder à cette page."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "Merci. Les instructions de confirmation ont été envoyées à %(email)s."
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "Merci. Votre adresse email a été confirmée."
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "Votre adresse email a déjà été confirmée."
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "Token de confirmation non valide."
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "L'adresse %(email)s est déjà utilisée."
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "Le mot de passe ne correspond pas"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "Les mots de passe ne correspondent pas"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "Les redirections en dehors du domaine sont interdites"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr ""
119 "Les instructions de réinitialisation de votre mot de passe ont été "
120 "envoyées à %(email)s."
121
122 #: flask_security/core.py:311
123 #, python-format
124 msgid ""
125 "You did not reset your password within %(within)s. New instructions have "
126 "been sent to %(email)s."
127 msgstr ""
128 "Vous n'avez pas réinitialisé votre mot de passe dans l'intervalle requis "
129 "(%(within)s)De nouvelles instructions ont été envoyées à %(email)s."
130
131 #: flask_security/core.py:317
132 msgid "Invalid reset password token."
133 msgstr "Token de réinitialisation non valide."
134
135 #: flask_security/core.py:318
136 msgid "Email requires confirmation."
137 msgstr "Une confirmation de l'adresse email est requise."
138
139 #: flask_security/core.py:320
140 #, python-format
141 msgid "Confirmation instructions have been sent to %(email)s."
142 msgstr "Les instructions de confirmation ont été envoyées à %(email)s."
143
144 #: flask_security/core.py:324
145 #, python-format
146 msgid ""
147 "You did not confirm your email within %(within)s. New instructions to "
148 "confirm your email have been sent to %(email)s."
149 msgstr ""
150 "Vous n'avez pas confirmé votre adresse email dans l'intervalle requis "
151 "(%(within)s)De nouvelles instructions ont été envoyées à %(email)s."
152
153 #: flask_security/core.py:332
154 #, python-format
155 msgid ""
156 "You did not login within %(within)s. New instructions to login have been "
157 "sent to %(email)s."
158 msgstr ""
159 "Vous ne vous êtes pas connecté dans l'intervalle requis (%(within)s)De "
160 "nouvelles instructions ont été envoyées à %(email)s."
161
162 #: flask_security/core.py:339
163 #, python-format
164 msgid "Instructions to login have been sent to %(email)s."
165 msgstr "Les instructions de connexion ont été envoyées à %(email)s."
166
167 #: flask_security/core.py:342
168 msgid "Invalid login token."
169 msgstr "Token de connexion non valide."
170
171 #: flask_security/core.py:343
172 msgid "Account is disabled."
173 msgstr "Le compte est désactivé."
174
175 #: flask_security/core.py:344
176 msgid "Email not provided"
177 msgstr "Merci d'indiquer une adresse email"
178
179 #: flask_security/core.py:345
180 msgid "Invalid email address"
181 msgstr "Adresse email non valide"
182
183 #: flask_security/core.py:346
184 #, fuzzy
185 msgid "Invalid code"
186 msgstr "Mot de passe non valide"
187
188 #: flask_security/core.py:347
189 msgid "Password not provided"
190 msgstr "Merci d'indiquer un mot de passe"
191
192 #: flask_security/core.py:348
193 msgid "No password is set for this user"
194 msgstr "Cet utilisateur n'a pas de mot de passe"
195
196 #: flask_security/core.py:350
197 #, fuzzy, python-format
198 msgid "Password must be at least %(length)s characters"
199 msgstr "Le mot de passe doit comporter au moins 6 caractères"
200
201 #: flask_security/core.py:353
202 msgid "Password not complex enough"
203 msgstr ""
204
205 #: flask_security/core.py:354
206 msgid "Password on breached list"
207 msgstr ""
208
209 #: flask_security/core.py:356
210 msgid "Failed to contact breached passwords site"
211 msgstr ""
212
213 #: flask_security/core.py:359
214 msgid "Phone number not valid e.g. missing country code"
215 msgstr ""
216
217 #: flask_security/core.py:360
218 msgid "Specified user does not exist"
219 msgstr "Cet utilisateur n'existe pas"
220
221 #: flask_security/core.py:361
222 msgid "Invalid password"
223 msgstr "Mot de passe non valide"
224
225 #: flask_security/core.py:362
226 msgid "Password or code submitted is not valid"
227 msgstr ""
228
229 #: flask_security/core.py:363
230 msgid "You have successfully logged in."
231 msgstr "Vous êtes bien connecté."
232
233 #: flask_security/core.py:364
234 msgid "Forgot password?"
235 msgstr "Mot de passe oublié&thinsp;?"
236
237 #: flask_security/core.py:366
238 msgid ""
239 "You successfully reset your password and you have been logged in "
240 "automatically."
241 msgstr ""
242 "Vous avez bien réinitialisé votre mot de passe et avez été "
243 "automatiquement connecté."
244
245 #: flask_security/core.py:373
246 msgid "Your new password must be different than your previous password."
247 msgstr "Votre nouveau mot de passe doit être différent du précédent."
248
249 #: flask_security/core.py:376
250 msgid "You successfully changed your password."
251 msgstr "Vous avez bien changé votre mot de passe."
252
253 #: flask_security/core.py:377
254 msgid "Please log in to access this page."
255 msgstr "Merci de vous connecter pour accéder à cette page."
256
257 #: flask_security/core.py:378
258 msgid "Please reauthenticate to access this page."
259 msgstr "Merci de vous reconnecter pour accéder à cette page."
260
261 #: flask_security/core.py:379
262 msgid "Reauthentication successful"
263 msgstr ""
264
265 #: flask_security/core.py:381
266 msgid "You can only access this endpoint when not logged in."
267 msgstr ""
268
269 #: flask_security/core.py:384
270 msgid "Failed to send code. Please try again later"
271 msgstr ""
272
273 #: flask_security/core.py:385
274 msgid "Invalid Token"
275 msgstr ""
276
277 #: flask_security/core.py:386
278 msgid "Your token has been confirmed"
279 msgstr ""
280
281 #: flask_security/core.py:388
282 msgid "You successfully changed your two-factor method."
283 msgstr ""
284
285 #: flask_security/core.py:392
286 msgid "You successfully confirmed password"
287 msgstr ""
288
289 #: flask_security/core.py:396
290 msgid "Password confirmation is needed in order to access page"
291 msgstr ""
292
293 #: flask_security/core.py:400
294 msgid "You currently do not have permissions to access this page"
295 msgstr ""
296
297 #: flask_security/core.py:403
298 msgid "Marked method is not valid"
299 msgstr ""
300
301 #: flask_security/core.py:405
302 msgid "You successfully disabled two factor authorization."
303 msgstr ""
304
305 #: flask_security/core.py:408
306 msgid "Requested method is not valid"
307 msgstr ""
308
309 #: flask_security/core.py:410
310 #, python-format
311 msgid "Setup must be completed within %(within)s. Please start over."
312 msgstr ""
313
314 #: flask_security/core.py:413
315 msgid "Unified sign in setup successful"
316 msgstr ""
317
318 #: flask_security/core.py:414
319 msgid "You must specify a valid identity to sign in"
320 msgstr ""
321
322 #: flask_security/core.py:415
323 #, python-format
324 msgid "Use this code to sign in: %(code)s."
325 msgstr ""
326
327 #: flask_security/forms.py:50
328 msgid "Email Address"
329 msgstr "Adresse email"
330
331 #: flask_security/forms.py:51
332 msgid "Password"
333 msgstr "Mot de passe"
334
335 #: flask_security/forms.py:52
336 msgid "Remember Me"
337 msgstr "Se souvenir de moi"
338
339 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
340 #: flask_security/templates/security/login_user.html:6
341 #: flask_security/templates/security/send_login.html:6
342 msgid "Login"
343 msgstr "Connexion"
344
345 #: flask_security/forms.py:54
346 #: flask_security/templates/security/email/us_instructions.html:8
347 #: flask_security/templates/security/us_signin.html:6
348 msgid "Sign In"
349 msgstr ""
350
351 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
352 #: flask_security/templates/security/register_user.html:6
353 msgid "Register"
354 msgstr "Inscription"
355
356 #: flask_security/forms.py:56
357 msgid "Resend Confirmation Instructions"
358 msgstr "Renvoyer les instructions de confirmation"
359
360 #: flask_security/forms.py:57
361 msgid "Recover Password"
362 msgstr "Récupérer le mot de passe"
363
364 #: flask_security/forms.py:58
365 msgid "Reset Password"
366 msgstr "Réinitialiser le mot de passe"
367
368 #: flask_security/forms.py:59
369 msgid "Retype Password"
370 msgstr "Confirmer le mot de passe"
371
372 #: flask_security/forms.py:60
373 msgid "New Password"
374 msgstr "Nouveau mot de passe"
375
376 #: flask_security/forms.py:61
377 msgid "Change Password"
378 msgstr "Changer le mot de passe"
379
380 #: flask_security/forms.py:62
381 msgid "Send Login Link"
382 msgstr "Envoyer le lien de connexion"
383
384 #: flask_security/forms.py:63
385 msgid "Verify Password"
386 msgstr ""
387
388 #: flask_security/forms.py:64
389 msgid "Change Method"
390 msgstr ""
391
392 #: flask_security/forms.py:65
393 msgid "Phone Number"
394 msgstr ""
395
396 #: flask_security/forms.py:66
397 msgid "Authentication Code"
398 msgstr ""
399
400 #: flask_security/forms.py:67
401 msgid "Submit"
402 msgstr ""
403
404 #: flask_security/forms.py:68
405 msgid "Submit Code"
406 msgstr ""
407
408 #: flask_security/forms.py:69
409 msgid "Error(s)"
410 msgstr ""
411
412 #: flask_security/forms.py:70
413 msgid "Identity"
414 msgstr ""
415
416 #: flask_security/forms.py:71
417 msgid "Send Code"
418 msgstr ""
419
420 #: flask_security/forms.py:72
421 #, fuzzy
422 msgid "Passcode"
423 msgstr "Mot de passe"
424
425 #: flask_security/unified_signin.py:145
426 #, fuzzy
427 msgid "Code or Password"
428 msgstr "Récupérer le mot de passe"
429
430 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
431 msgid "Available Methods"
432 msgstr ""
433
434 #: flask_security/unified_signin.py:151
435 msgid "Via email"
436 msgstr ""
437
438 #: flask_security/unified_signin.py:151
439 msgid "Via SMS"
440 msgstr ""
441
442 #: flask_security/unified_signin.py:272
443 msgid "Set up using email"
444 msgstr ""
445
446 #: flask_security/unified_signin.py:275
447 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
448 msgstr ""
449
450 #: flask_security/unified_signin.py:277
451 msgid "Set up using SMS"
452 msgstr ""
453
454 #: flask_security/templates/security/_menu.html:2
455 msgid "Menu"
456 msgstr "Menu"
457
458 #: flask_security/templates/security/_menu.html:8
459 msgid "Unified Sign In"
460 msgstr ""
461
462 #: flask_security/templates/security/_menu.html:14
463 msgid "Forgot password"
464 msgstr "Mot de passe oublié"
465
466 #: flask_security/templates/security/_menu.html:17
467 msgid "Confirm account"
468 msgstr "Confirmer le compte"
469
470 #: flask_security/templates/security/change_password.html:6
471 msgid "Change password"
472 msgstr "Changer de mot de passe"
473
474 #: flask_security/templates/security/forgot_password.html:6
475 msgid "Send password reset instructions"
476 msgstr "Envoyer les instructions de réinitialisation de mot de passe"
477
478 #: flask_security/templates/security/reset_password.html:6
479 msgid "Reset password"
480 msgstr "Réinitialiser le mot de passe"
481
482 #: flask_security/templates/security/send_confirmation.html:6
483 msgid "Resend confirmation instructions"
484 msgstr "Renvoyer les instructions de confirmation"
485
486 #: flask_security/templates/security/two_factor_setup.html:6
487 msgid "Two-factor authentication adds an extra layer of security to your account"
488 msgstr ""
489
490 #: flask_security/templates/security/two_factor_setup.html:7
491 msgid ""
492 "In addition to your username and password, you'll need to use a code that"
493 " we will send you"
494 msgstr ""
495
496 #: flask_security/templates/security/two_factor_setup.html:18
497 msgid "To complete logging in, please enter the code sent to your mail"
498 msgstr ""
499
500 #: flask_security/templates/security/two_factor_setup.html:21
501 #: flask_security/templates/security/us_setup.html:21
502 msgid ""
503 "Open your authenticator app on your device and scan the following qrcode "
504 "to start receiving codes:"
505 msgstr ""
506
507 #: flask_security/templates/security/two_factor_setup.html:22
508 msgid "Two factor authentication code"
509 msgstr ""
510
511 #: flask_security/templates/security/two_factor_setup.html:25
512 msgid "To Which Phone Number Should We Send Code To?"
513 msgstr ""
514
515 #: flask_security/templates/security/two_factor_verify_code.html:6
516 msgid "Two-factor Authentication"
517 msgstr ""
518
519 #: flask_security/templates/security/two_factor_verify_code.html:7
520 msgid "Please enter your authentication code"
521 msgstr ""
522
523 #: flask_security/templates/security/two_factor_verify_code.html:18
524 msgid "The code for authentication was sent to your email address"
525 msgstr ""
526
527 #: flask_security/templates/security/two_factor_verify_code.html:21
528 msgid "A mail was sent to us in order to reset your application account"
529 msgstr ""
530
531 #: flask_security/templates/security/two_factor_verify_password.html:6
532 #: flask_security/templates/security/verify.html:6
533 msgid "Please Enter Your Password"
534 msgstr ""
535
536 #: flask_security/templates/security/us_setup.html:6
537 msgid "Setup Unified Sign In options"
538 msgstr ""
539
540 #: flask_security/templates/security/us_setup.html:22
541 msgid "Passwordless QRCode"
542 msgstr ""
543
544 #: flask_security/templates/security/us_setup.html:25
545 #: flask_security/templates/security/us_signin.html:23
546 #: flask_security/templates/security/us_verify.html:21
547 #, fuzzy
548 msgid "Code has been sent"
549 msgstr "Votre mot de passe a été réinitialisé"
550
551 #: flask_security/templates/security/us_setup.html:29
552 msgid "No methods have been enabled - nothing to setup"
553 msgstr ""
554
555 #: flask_security/templates/security/us_signin.html:15
556 #: flask_security/templates/security/us_verify.html:13
557 msgid "Request one-time code be sent"
558 msgstr ""
559
560 #: flask_security/templates/security/us_verify.html:6
561 #, fuzzy
562 msgid "Please re-authenticate"
563 msgstr "Merci de vous reconnecter pour accéder à cette page."
564
565 #: flask_security/templates/security/email/change_notice.html:1
566 msgid "Your password has been changed."
567 msgstr "Votre mot de passe a été changé."
568
569 #: flask_security/templates/security/email/change_notice.html:3
570 msgid "If you did not change your password,"
571 msgstr "Si vous n'avez pas changé votre mot de passe,"
572
573 #: flask_security/templates/security/email/change_notice.html:3
574 msgid "click here to reset it"
575 msgstr "cliquez ici pour le réinitialiser"
576
577 #: flask_security/templates/security/email/confirmation_instructions.html:1
578 msgid "Please confirm your email through the link below:"
579 msgstr "Merci de confirmer votre adresse email via le lien ci-dessous&thinsp;:"
580
581 #: flask_security/templates/security/email/confirmation_instructions.html:3
582 #: flask_security/templates/security/email/welcome.html:6
583 msgid "Confirm my account"
584 msgstr "Confirmer mon compte"
585
586 #: flask_security/templates/security/email/login_instructions.html:1
587 #: flask_security/templates/security/email/welcome.html:1
588 #, python-format
589 msgid "Welcome %(email)s!"
590 msgstr "Bienvenue %(email)s&thinsp;!"
591
592 #: flask_security/templates/security/email/login_instructions.html:3
593 msgid "You can log into your account through the link below:"
594 msgstr "Vous pouvez vous connecter via le lien ci-dessous&thinsp;:"
595
596 #: flask_security/templates/security/email/login_instructions.html:5
597 msgid "Login now"
598 msgstr "Se connecter maintenant"
599
600 #: flask_security/templates/security/email/reset_instructions.html:1
601 msgid "Click here to reset your password"
602 msgstr "Cliquez pour réinitialiser votre mot de passe"
603
604 #: flask_security/templates/security/email/two_factor_instructions.html:3
605 msgid "You can log into your account using the following code:"
606 msgstr ""
607
608 #: flask_security/templates/security/email/two_factor_rescue.html:1
609 msgid "can not access mail account"
610 msgstr ""
611
612 #: flask_security/templates/security/email/us_instructions.html:3
613 #, fuzzy
614 msgid "You can sign into your account using the following code:"
615 msgstr "Vous pouvez vous connecter via le lien ci-dessous&thinsp;:"
616
617 #: flask_security/templates/security/email/us_instructions.html:6
618 #, fuzzy
619 msgid "Or use the the link below:"
620 msgstr ""
621 "Vous pouvez confirmer votre votre adresse email via le lien ci-"
622 "dessous&thinsp;:"
623
624 #: flask_security/templates/security/email/welcome.html:4
625 msgid "You can confirm your email through the link below:"
626 msgstr ""
627 "Vous pouvez confirmer votre votre adresse email via le lien ci-"
628 "dessous&thinsp;:"
629
0 # Japanese translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2018-01-25 14:12+0900\n"
12 "Last-Translator: \n"
13 "Language: ja\n"
14 "Language-Team: \n"
15 "Plural-Forms: nplurals=1; plural=0\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "ログインが必要です"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "ようこそ"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "メール アドレスの検証"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "ログイン手順"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "パスワード変更"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "パスワードが変更されました。"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "パスワード再設定手順"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "アクセス権がありません"
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "再度ログインしてください"
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "ご登録ありがとうございます。%(email)sにメール アドレス検証手順が送信されました。"
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "ありがとうございます。メール アドレスが検証されました。"
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "メール アドレスは検証済みです"
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "リンクが無効です"
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "%(email)s のアカウントは既に作成されています"
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "パスワードが一致しません"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "入力したパスワードが一致していません"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "ドメイン外へのリダイレクトは禁止されています"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr "パスワードの再設定手順が %(email)s に送信されました"
119
120 #: flask_security/core.py:311
121 #, python-format
122 msgid ""
123 "You did not reset your password within %(within)s. New instructions have "
124 "been sent to %(email)s."
125 msgstr "%(within)s以内にパスワードを設定しませんでした。パスワード再設定手順を %(email)s に再度送信しました。"
126
127 #: flask_security/core.py:317
128 msgid "Invalid reset password token."
129 msgstr "リンクが無効です"
130
131 #: flask_security/core.py:318
132 msgid "Email requires confirmation."
133 msgstr "メール アドレスの検証が必要です"
134
135 #: flask_security/core.py:320
136 #, python-format
137 msgid "Confirmation instructions have been sent to %(email)s."
138 msgstr "%(email)sにメール アドレス検証手順が再送信されました"
139
140 #: flask_security/core.py:324
141 #, python-format
142 msgid ""
143 "You did not confirm your email within %(within)s. New instructions to "
144 "confirm your email have been sent to %(email)s."
145 msgstr "%(within)s以内にメール アドレスが検証されませんでした。新しい検証手順を %(email)s に送信しました。"
146
147 #: flask_security/core.py:332
148 #, python-format
149 msgid ""
150 "You did not login within %(within)s. New instructions to login have been "
151 "sent to %(email)s."
152 msgstr "%(within)s以内にログインしませんでした。ログイン手順を %(email)s に再度送信しました。"
153
154 #: flask_security/core.py:339
155 #, python-format
156 msgid "Instructions to login have been sent to %(email)s."
157 msgstr "%(email)sにログイン手順が送信されました"
158
159 #: flask_security/core.py:342
160 msgid "Invalid login token."
161 msgstr "リンクが無効です"
162
163 #: flask_security/core.py:343
164 msgid "Account is disabled."
165 msgstr "アカウントが無効になっています"
166
167 #: flask_security/core.py:344
168 msgid "Email not provided"
169 msgstr "メール アドレスを入力してください"
170
171 #: flask_security/core.py:345
172 msgid "Invalid email address"
173 msgstr "正しいメール アドレスを入力してください"
174
175 #: flask_security/core.py:346
176 #, fuzzy
177 msgid "Invalid code"
178 msgstr "入力を確認してください"
179
180 #: flask_security/core.py:347
181 msgid "Password not provided"
182 msgstr "パスワードを入力してください"
183
184 #: flask_security/core.py:348
185 msgid "No password is set for this user"
186 msgstr "パスワードが設定されていません"
187
188 #: flask_security/core.py:350
189 #, fuzzy, python-format
190 msgid "Password must be at least %(length)s characters"
191 msgstr "パスワードは6文字以上でなければなりません"
192
193 #: flask_security/core.py:353
194 msgid "Password not complex enough"
195 msgstr ""
196
197 #: flask_security/core.py:354
198 msgid "Password on breached list"
199 msgstr ""
200
201 #: flask_security/core.py:356
202 msgid "Failed to contact breached passwords site"
203 msgstr ""
204
205 #: flask_security/core.py:359
206 msgid "Phone number not valid e.g. missing country code"
207 msgstr ""
208
209 #: flask_security/core.py:360
210 msgid "Specified user does not exist"
211 msgstr "入力を確認してください"
212
213 #: flask_security/core.py:361
214 msgid "Invalid password"
215 msgstr "入力を確認してください"
216
217 #: flask_security/core.py:362
218 msgid "Password or code submitted is not valid"
219 msgstr ""
220
221 #: flask_security/core.py:363
222 msgid "You have successfully logged in."
223 msgstr "ログインしました"
224
225 #: flask_security/core.py:364
226 msgid "Forgot password?"
227 msgstr "パスワードを忘れた場合"
228
229 #: flask_security/core.py:366
230 msgid ""
231 "You successfully reset your password and you have been logged in "
232 "automatically."
233 msgstr "パスワードの再設定が完了しました。"
234
235 #: flask_security/core.py:373
236 msgid "Your new password must be different than your previous password."
237 msgstr "新旧パスワードが同じです"
238
239 #: flask_security/core.py:376
240 msgid "You successfully changed your password."
241 msgstr "パスワードが変更されました"
242
243 #: flask_security/core.py:377
244 msgid "Please log in to access this page."
245 msgstr "ログインしてください"
246
247 #: flask_security/core.py:378
248 msgid "Please reauthenticate to access this page."
249 msgstr "再度ログインしてください"
250
251 #: flask_security/core.py:379
252 msgid "Reauthentication successful"
253 msgstr ""
254
255 #: flask_security/core.py:381
256 msgid "You can only access this endpoint when not logged in."
257 msgstr ""
258
259 #: flask_security/core.py:384
260 msgid "Failed to send code. Please try again later"
261 msgstr ""
262
263 #: flask_security/core.py:385
264 msgid "Invalid Token"
265 msgstr ""
266
267 #: flask_security/core.py:386
268 msgid "Your token has been confirmed"
269 msgstr ""
270
271 #: flask_security/core.py:388
272 msgid "You successfully changed your two-factor method."
273 msgstr ""
274
275 #: flask_security/core.py:392
276 msgid "You successfully confirmed password"
277 msgstr ""
278
279 #: flask_security/core.py:396
280 msgid "Password confirmation is needed in order to access page"
281 msgstr ""
282
283 #: flask_security/core.py:400
284 msgid "You currently do not have permissions to access this page"
285 msgstr ""
286
287 #: flask_security/core.py:403
288 msgid "Marked method is not valid"
289 msgstr ""
290
291 #: flask_security/core.py:405
292 msgid "You successfully disabled two factor authorization."
293 msgstr ""
294
295 #: flask_security/core.py:408
296 msgid "Requested method is not valid"
297 msgstr ""
298
299 #: flask_security/core.py:410
300 #, python-format
301 msgid "Setup must be completed within %(within)s. Please start over."
302 msgstr ""
303
304 #: flask_security/core.py:413
305 msgid "Unified sign in setup successful"
306 msgstr ""
307
308 #: flask_security/core.py:414
309 msgid "You must specify a valid identity to sign in"
310 msgstr ""
311
312 #: flask_security/core.py:415
313 #, python-format
314 msgid "Use this code to sign in: %(code)s."
315 msgstr ""
316
317 #: flask_security/forms.py:50
318 msgid "Email Address"
319 msgstr "メール アドレス"
320
321 #: flask_security/forms.py:51
322 msgid "Password"
323 msgstr "パスワード"
324
325 #: flask_security/forms.py:52
326 msgid "Remember Me"
327 msgstr "次回以降ログインを省略する"
328
329 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
330 #: flask_security/templates/security/login_user.html:6
331 #: flask_security/templates/security/send_login.html:6
332 msgid "Login"
333 msgstr "ログイン"
334
335 #: flask_security/forms.py:54
336 #: flask_security/templates/security/email/us_instructions.html:8
337 #: flask_security/templates/security/us_signin.html:6
338 msgid "Sign In"
339 msgstr ""
340
341 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
342 #: flask_security/templates/security/register_user.html:6
343 msgid "Register"
344 msgstr "ユーザ登録"
345
346 #: flask_security/forms.py:56
347 msgid "Resend Confirmation Instructions"
348 msgstr "検証手順の再送信"
349
350 #: flask_security/forms.py:57
351 msgid "Recover Password"
352 msgstr "再設定手順を送信"
353
354 #: flask_security/forms.py:58
355 msgid "Reset Password"
356 msgstr "パスワード変更"
357
358 #: flask_security/forms.py:59
359 msgid "Retype Password"
360 msgstr "パスワード再入力"
361
362 #: flask_security/forms.py:60
363 msgid "New Password"
364 msgstr "新しいパスワード"
365
366 #: flask_security/forms.py:61
367 msgid "Change Password"
368 msgstr "変更"
369
370 #: flask_security/forms.py:62
371 msgid "Send Login Link"
372 msgstr "ログイン手順を送信"
373
374 #: flask_security/forms.py:63
375 msgid "Verify Password"
376 msgstr ""
377
378 #: flask_security/forms.py:64
379 msgid "Change Method"
380 msgstr ""
381
382 #: flask_security/forms.py:65
383 msgid "Phone Number"
384 msgstr ""
385
386 #: flask_security/forms.py:66
387 msgid "Authentication Code"
388 msgstr ""
389
390 #: flask_security/forms.py:67
391 msgid "Submit"
392 msgstr ""
393
394 #: flask_security/forms.py:68
395 msgid "Submit Code"
396 msgstr ""
397
398 #: flask_security/forms.py:69
399 msgid "Error(s)"
400 msgstr ""
401
402 #: flask_security/forms.py:70
403 msgid "Identity"
404 msgstr ""
405
406 #: flask_security/forms.py:71
407 msgid "Send Code"
408 msgstr ""
409
410 #: flask_security/forms.py:72
411 #, fuzzy
412 msgid "Passcode"
413 msgstr "パスワード"
414
415 #: flask_security/unified_signin.py:145
416 #, fuzzy
417 msgid "Code or Password"
418 msgstr "再設定手順を送信"
419
420 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
421 msgid "Available Methods"
422 msgstr ""
423
424 #: flask_security/unified_signin.py:151
425 msgid "Via email"
426 msgstr ""
427
428 #: flask_security/unified_signin.py:151
429 msgid "Via SMS"
430 msgstr ""
431
432 #: flask_security/unified_signin.py:272
433 msgid "Set up using email"
434 msgstr ""
435
436 #: flask_security/unified_signin.py:275
437 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
438 msgstr ""
439
440 #: flask_security/unified_signin.py:277
441 msgid "Set up using SMS"
442 msgstr ""
443
444 #: flask_security/templates/security/_menu.html:2
445 msgid "Menu"
446 msgstr "メニュー"
447
448 #: flask_security/templates/security/_menu.html:8
449 msgid "Unified Sign In"
450 msgstr ""
451
452 #: flask_security/templates/security/_menu.html:14
453 msgid "Forgot password"
454 msgstr "パスワードを忘れた場合"
455
456 #: flask_security/templates/security/_menu.html:17
457 msgid "Confirm account"
458 msgstr "メール アドレスの検証"
459
460 #: flask_security/templates/security/change_password.html:6
461 msgid "Change password"
462 msgstr "パスワードの変更"
463
464 #: flask_security/templates/security/forgot_password.html:6
465 msgid "Send password reset instructions"
466 msgstr "パスワード再設定手順の送信"
467
468 #: flask_security/templates/security/reset_password.html:6
469 msgid "Reset password"
470 msgstr "パスワード再設定"
471
472 #: flask_security/templates/security/send_confirmation.html:6
473 msgid "Resend confirmation instructions"
474 msgstr "検証手順の再送信"
475
476 #: flask_security/templates/security/two_factor_setup.html:6
477 msgid "Two-factor authentication adds an extra layer of security to your account"
478 msgstr ""
479
480 #: flask_security/templates/security/two_factor_setup.html:7
481 msgid ""
482 "In addition to your username and password, you'll need to use a code that"
483 " we will send you"
484 msgstr ""
485
486 #: flask_security/templates/security/two_factor_setup.html:18
487 msgid "To complete logging in, please enter the code sent to your mail"
488 msgstr ""
489
490 #: flask_security/templates/security/two_factor_setup.html:21
491 #: flask_security/templates/security/us_setup.html:21
492 msgid ""
493 "Open your authenticator app on your device and scan the following qrcode "
494 "to start receiving codes:"
495 msgstr ""
496
497 #: flask_security/templates/security/two_factor_setup.html:22
498 msgid "Two factor authentication code"
499 msgstr ""
500
501 #: flask_security/templates/security/two_factor_setup.html:25
502 msgid "To Which Phone Number Should We Send Code To?"
503 msgstr ""
504
505 #: flask_security/templates/security/two_factor_verify_code.html:6
506 msgid "Two-factor Authentication"
507 msgstr ""
508
509 #: flask_security/templates/security/two_factor_verify_code.html:7
510 msgid "Please enter your authentication code"
511 msgstr ""
512
513 #: flask_security/templates/security/two_factor_verify_code.html:18
514 msgid "The code for authentication was sent to your email address"
515 msgstr ""
516
517 #: flask_security/templates/security/two_factor_verify_code.html:21
518 msgid "A mail was sent to us in order to reset your application account"
519 msgstr ""
520
521 #: flask_security/templates/security/two_factor_verify_password.html:6
522 #: flask_security/templates/security/verify.html:6
523 msgid "Please Enter Your Password"
524 msgstr ""
525
526 #: flask_security/templates/security/us_setup.html:6
527 msgid "Setup Unified Sign In options"
528 msgstr ""
529
530 #: flask_security/templates/security/us_setup.html:22
531 msgid "Passwordless QRCode"
532 msgstr ""
533
534 #: flask_security/templates/security/us_setup.html:25
535 #: flask_security/templates/security/us_signin.html:23
536 #: flask_security/templates/security/us_verify.html:21
537 #, fuzzy
538 msgid "Code has been sent"
539 msgstr "パスワード変更"
540
541 #: flask_security/templates/security/us_setup.html:29
542 msgid "No methods have been enabled - nothing to setup"
543 msgstr ""
544
545 #: flask_security/templates/security/us_signin.html:15
546 #: flask_security/templates/security/us_verify.html:13
547 msgid "Request one-time code be sent"
548 msgstr ""
549
550 #: flask_security/templates/security/us_verify.html:6
551 #, fuzzy
552 msgid "Please re-authenticate"
553 msgstr "再度ログインしてください"
554
555 #: flask_security/templates/security/email/change_notice.html:1
556 msgid "Your password has been changed."
557 msgstr "パスワードが変更されました。"
558
559 #: flask_security/templates/security/email/change_notice.html:3
560 msgid "If you did not change your password,"
561 msgstr "パスワードを変更した覚えがない場合には、"
562
563 #: flask_security/templates/security/email/change_notice.html:3
564 msgid "click here to reset it"
565 msgstr "このリンクを開いてください。"
566
567 #: flask_security/templates/security/email/confirmation_instructions.html:1
568 msgid "Please confirm your email through the link below:"
569 msgstr "以下のリンクからメール アドレスを検証してください:"
570
571 #: flask_security/templates/security/email/confirmation_instructions.html:3
572 #: flask_security/templates/security/email/welcome.html:6
573 msgid "Confirm my account"
574 msgstr "メール アドレスの検証"
575
576 #: flask_security/templates/security/email/login_instructions.html:1
577 #: flask_security/templates/security/email/welcome.html:1
578 #, python-format
579 msgid "Welcome %(email)s!"
580 msgstr "ようこそ %(email)s !"
581
582 #: flask_security/templates/security/email/login_instructions.html:3
583 msgid "You can log into your account through the link below:"
584 msgstr "以下のリンクによりログインできます。"
585
586 #: flask_security/templates/security/email/login_instructions.html:5
587 msgid "Login now"
588 msgstr "ログイン"
589
590 #: flask_security/templates/security/email/reset_instructions.html:1
591 msgid "Click here to reset your password"
592 msgstr "パスワードを再設定するためにこのリンクを開いてください。"
593
594 #: flask_security/templates/security/email/two_factor_instructions.html:3
595 msgid "You can log into your account using the following code:"
596 msgstr ""
597
598 #: flask_security/templates/security/email/two_factor_rescue.html:1
599 msgid "can not access mail account"
600 msgstr ""
601
602 #: flask_security/templates/security/email/us_instructions.html:3
603 #, fuzzy
604 msgid "You can sign into your account using the following code:"
605 msgstr "以下のリンクによりログインできます。"
606
607 #: flask_security/templates/security/email/us_instructions.html:6
608 #, fuzzy
609 msgid "Or use the the link below:"
610 msgstr "以下のリンクによりメール アドレスを検証できます。"
611
612 #: flask_security/templates/security/email/welcome.html:4
613 msgid "You can confirm your email through the link below:"
614 msgstr "以下のリンクによりメール アドレスを検証できます。"
615
0 # Dutch (Netherlands) translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-05-01 17:52+0200\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language: nl_NL\n"
14 "Language-Team: nl_NL <[email protected]>\n"
15 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Inloggen Verplicht"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Welkom"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Gelieve uw e-mailadres te bevestigen"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Aanmeld instructies"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "Uw wachtwoord werd gereset"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "Uw wachtwoord werd gewijzigd"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Wachtwoord reset instructies"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr "Dubbele Authenticatie Aanmelding"
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr "Dubbele Authenticatie Herstellen"
59
60 #: flask_security/core.py:266
61 #, fuzzy
62 msgid "Verification Code"
63 msgstr "Authenticatie Code"
64
65 #: flask_security/core.py:282
66 msgid "Input not appropriate for requested API"
67 msgstr ""
68
69 #: flask_security/core.py:283
70 msgid "You do not have permission to view this resource."
71 msgstr "U heeft niet de nodige rechten om deze pagina te zien."
72
73 #: flask_security/core.py:285
74 msgid "You are not authenticated. Please supply the correct credentials."
75 msgstr "U bent niet aangemeld. Voer alstublieft de juiste gegevens in."
76
77 #: flask_security/core.py:289
78 #, fuzzy
79 msgid "You must re-authenticate to access this endpoint"
80 msgstr "Gelieve opnieuw in te loggen om deze pagina te zien."
81
82 #: flask_security/core.py:293
83 #, python-format
84 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
85 msgstr "Bedankt. Instructies voor bevestiging zijn verzonden naar %(email)s."
86
87 #: flask_security/core.py:296
88 msgid "Thank you. Your email has been confirmed."
89 msgstr "Bedankt. Uw e-mailadres werd bevestigd."
90
91 #: flask_security/core.py:297
92 msgid "Your email has already been confirmed."
93 msgstr "Uw e-mailadres werd reeds bevestigd."
94
95 #: flask_security/core.py:298
96 msgid "Invalid confirmation token."
97 msgstr "Ongeldige bevestiging token."
98
99 #: flask_security/core.py:300
100 #, python-format
101 msgid "%(email)s is already associated with an account."
102 msgstr "%(email)s is al gelinkt aan een ander account."
103
104 #: flask_security/core.py:303
105 msgid "Password does not match"
106 msgstr "Wachtwoord komt niet overeen"
107
108 #: flask_security/core.py:304
109 msgid "Passwords do not match"
110 msgstr "Wachtwoorden komen niet overeen"
111
112 #: flask_security/core.py:305
113 msgid "Redirections outside the domain are forbidden"
114 msgstr "Omleidingen buiten het domein zijn niet toegelaten"
115
116 #: flask_security/core.py:307
117 #, python-format
118 msgid "Instructions to reset your password have been sent to %(email)s."
119 msgstr "Instructies om uw wachtwoord te resetten werden verzonden naar %(email)s."
120
121 #: flask_security/core.py:311
122 #, python-format
123 msgid ""
124 "You did not reset your password within %(within)s. New instructions have "
125 "been sent to %(email)s."
126 msgstr ""
127 "U heeft uw wachtwoord niet gereset gedurende %(within)s. Nieuwe "
128 "instructies werden verzonden naar %(email)s."
129
130 #: flask_security/core.py:317
131 msgid "Invalid reset password token."
132 msgstr "Ongeldig wachtwoord reset token."
133
134 #: flask_security/core.py:318
135 msgid "Email requires confirmation."
136 msgstr "E-mailadres moet bevestigd worden."
137
138 #: flask_security/core.py:320
139 #, python-format
140 msgid "Confirmation instructions have been sent to %(email)s."
141 msgstr ""
142 "Instructies ter bevestiging van uw e-mailadres werden verzonden naar "
143 "%(email)s."
144
145 #: flask_security/core.py:324
146 #, python-format
147 msgid ""
148 "You did not confirm your email within %(within)s. New instructions to "
149 "confirm your email have been sent to %(email)s."
150 msgstr ""
151 "U heeft uw e-mailadres niet bevestigd in de voorziene %(within)s. Nieuwe "
152 "instructies ter bevestiging van uw e-mailadres werden verzonden naar "
153 "%(email)s."
154
155 #: flask_security/core.py:332
156 #, python-format
157 msgid ""
158 "You did not login within %(within)s. New instructions to login have been "
159 "sent to %(email)s."
160 msgstr ""
161 "Je bent niet ingelogd geweest gedurende %(within)s. Nieuwe instructies om"
162 " in te loggen werden verzonden naar%(email)s."
163
164 #: flask_security/core.py:339
165 #, python-format
166 msgid "Instructions to login have been sent to %(email)s."
167 msgstr "Instructies om in te loggen werden verzonden naar %(email)s."
168
169 #: flask_security/core.py:342
170 msgid "Invalid login token."
171 msgstr "Ongeldige aanmelding."
172
173 #: flask_security/core.py:343
174 msgid "Account is disabled."
175 msgstr "Account is geblokkeerd."
176
177 #: flask_security/core.py:344
178 msgid "Email not provided"
179 msgstr "Email niet ingevuld"
180
181 #: flask_security/core.py:345
182 msgid "Invalid email address"
183 msgstr "Ongeldig e-mailadres"
184
185 #: flask_security/core.py:346
186 #, fuzzy
187 msgid "Invalid code"
188 msgstr "Niet valide token"
189
190 #: flask_security/core.py:347
191 msgid "Password not provided"
192 msgstr "Wachtwoord niet ingevuld"
193
194 #: flask_security/core.py:348
195 msgid "No password is set for this user"
196 msgstr "Er is geen wachtwoord gezet voor deze gebruiker"
197
198 #: flask_security/core.py:350
199 #, fuzzy, python-format
200 msgid "Password must be at least %(length)s characters"
201 msgstr "Uw wachtwoord moet minstens 6 karakters bevatten"
202
203 #: flask_security/core.py:353
204 msgid "Password not complex enough"
205 msgstr ""
206
207 #: flask_security/core.py:354
208 msgid "Password on breached list"
209 msgstr ""
210
211 #: flask_security/core.py:356
212 msgid "Failed to contact breached passwords site"
213 msgstr ""
214
215 #: flask_security/core.py:359
216 msgid "Phone number not valid e.g. missing country code"
217 msgstr ""
218
219 #: flask_security/core.py:360
220 msgid "Specified user does not exist"
221 msgstr "Deze gebruiker bestaat niet"
222
223 #: flask_security/core.py:361
224 msgid "Invalid password"
225 msgstr "Ongeldig wachtwoord"
226
227 #: flask_security/core.py:362
228 #, fuzzy
229 msgid "Password or code submitted is not valid"
230 msgstr "De gemarkeerde methode is niet valide"
231
232 #: flask_security/core.py:363
233 msgid "You have successfully logged in."
234 msgstr "U bent succesvol ingelogd."
235
236 #: flask_security/core.py:364
237 msgid "Forgot password?"
238 msgstr "Wachtwoord vergeten?"
239
240 #: flask_security/core.py:366
241 msgid ""
242 "You successfully reset your password and you have been logged in "
243 "automatically."
244 msgstr "U heeft uw wachtwoord succesvol gereset en bent nu automatisch ingelogd."
245
246 #: flask_security/core.py:373
247 msgid "Your new password must be different than your previous password."
248 msgstr "Uw nieuw wachtwoord moet verschillend zijn van het voorgaande wachtwoord."
249
250 #: flask_security/core.py:376
251 msgid "You successfully changed your password."
252 msgstr "Uw wachtwoord werd met succes gewijzigd."
253
254 #: flask_security/core.py:377
255 msgid "Please log in to access this page."
256 msgstr "Gelieve in te loggen om deze pagina te zien."
257
258 #: flask_security/core.py:378
259 msgid "Please reauthenticate to access this page."
260 msgstr "Gelieve opnieuw in te loggen om deze pagina te zien."
261
262 #: flask_security/core.py:379
263 #, fuzzy
264 msgid "Reauthentication successful"
265 msgstr "Authenticatie Code"
266
267 #: flask_security/core.py:381
268 msgid "You can only access this endpoint when not logged in."
269 msgstr ""
270
271 #: flask_security/core.py:384
272 msgid "Failed to send code. Please try again later"
273 msgstr ""
274
275 #: flask_security/core.py:385
276 msgid "Invalid Token"
277 msgstr "Niet valide token"
278
279 #: flask_security/core.py:386
280 msgid "Your token has been confirmed"
281 msgstr "Uw token is bevestigd"
282
283 #: flask_security/core.py:388
284 msgid "You successfully changed your two-factor method."
285 msgstr "U heeft succesvol uw Dubbele Authenticatie methode veranderd."
286
287 #: flask_security/core.py:392
288 msgid "You successfully confirmed password"
289 msgstr "U heeft succesvol uw wachtwoord aangepast"
290
291 #: flask_security/core.py:396
292 msgid "Password confirmation is needed in order to access page"
293 msgstr "Wachtwoord bevestiging is nodig voor we deze pagina kunnen laten zien"
294
295 #: flask_security/core.py:400
296 msgid "You currently do not have permissions to access this page"
297 msgstr "U heeft niet de juiste permissies om deze pagina te laden"
298
299 #: flask_security/core.py:403
300 msgid "Marked method is not valid"
301 msgstr "De gemarkeerde methode is niet valide"
302
303 #: flask_security/core.py:405
304 msgid "You successfully disabled two factor authorization."
305 msgstr "U heeft succesvol Dubbele Authenticatie uitgeschakeld."
306
307 #: flask_security/core.py:408
308 #, fuzzy
309 msgid "Requested method is not valid"
310 msgstr "De gemarkeerde methode is niet valide"
311
312 #: flask_security/core.py:410
313 #, python-format
314 msgid "Setup must be completed within %(within)s. Please start over."
315 msgstr ""
316
317 #: flask_security/core.py:413
318 msgid "Unified sign in setup successful"
319 msgstr ""
320
321 #: flask_security/core.py:414
322 msgid "You must specify a valid identity to sign in"
323 msgstr ""
324
325 #: flask_security/core.py:415
326 #, python-format
327 msgid "Use this code to sign in: %(code)s."
328 msgstr ""
329
330 #: flask_security/forms.py:50
331 msgid "Email Address"
332 msgstr "E-mailadres"
333
334 #: flask_security/forms.py:51
335 msgid "Password"
336 msgstr "wachtwoord"
337
338 #: flask_security/forms.py:52
339 msgid "Remember Me"
340 msgstr "Ingelogd blijven"
341
342 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
343 #: flask_security/templates/security/login_user.html:6
344 #: flask_security/templates/security/send_login.html:6
345 msgid "Login"
346 msgstr "Aanmelden"
347
348 #: flask_security/forms.py:54
349 #: flask_security/templates/security/email/us_instructions.html:8
350 #: flask_security/templates/security/us_signin.html:6
351 msgid "Sign In"
352 msgstr ""
353
354 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
355 #: flask_security/templates/security/register_user.html:6
356 msgid "Register"
357 msgstr "Registreer"
358
359 #: flask_security/forms.py:56
360 msgid "Resend Confirmation Instructions"
361 msgstr "Verzend instructies om te bevestigen opnieuw"
362
363 #: flask_security/forms.py:57
364 msgid "Recover Password"
365 msgstr "Herstel wachtwoord"
366
367 #: flask_security/forms.py:58
368 msgid "Reset Password"
369 msgstr "reset wachtwoord"
370
371 #: flask_security/forms.py:59
372 msgid "Retype Password"
373 msgstr "Type wachtwoord opnieuw"
374
375 #: flask_security/forms.py:60
376 msgid "New Password"
377 msgstr "Nieuw wachtwoord"
378
379 #: flask_security/forms.py:61
380 msgid "Change Password"
381 msgstr "Verander wachtwoord"
382
383 #: flask_security/forms.py:62
384 msgid "Send Login Link"
385 msgstr "Verzend aanmeld link"
386
387 #: flask_security/forms.py:63
388 msgid "Verify Password"
389 msgstr "Wachtwoord Verificatie"
390
391 #: flask_security/forms.py:64
392 msgid "Change Method"
393 msgstr "Verander Methode"
394
395 #: flask_security/forms.py:65
396 msgid "Phone Number"
397 msgstr "Telefoonnummer"
398
399 #: flask_security/forms.py:66
400 msgid "Authentication Code"
401 msgstr "Authenticatie Code"
402
403 #: flask_security/forms.py:67
404 msgid "Submit"
405 msgstr ""
406
407 #: flask_security/forms.py:68
408 msgid "Submit Code"
409 msgstr ""
410
411 #: flask_security/forms.py:69
412 msgid "Error(s)"
413 msgstr ""
414
415 #: flask_security/forms.py:70
416 msgid "Identity"
417 msgstr ""
418
419 #: flask_security/forms.py:71
420 msgid "Send Code"
421 msgstr ""
422
423 #: flask_security/forms.py:72
424 #, fuzzy
425 msgid "Passcode"
426 msgstr "wachtwoord"
427
428 #: flask_security/unified_signin.py:145
429 #, fuzzy
430 msgid "Code or Password"
431 msgstr "Herstel wachtwoord"
432
433 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
434 msgid "Available Methods"
435 msgstr ""
436
437 #: flask_security/unified_signin.py:151
438 msgid "Via email"
439 msgstr ""
440
441 #: flask_security/unified_signin.py:151
442 msgid "Via SMS"
443 msgstr ""
444
445 #: flask_security/unified_signin.py:272
446 msgid "Set up using email"
447 msgstr ""
448
449 #: flask_security/unified_signin.py:275
450 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
451 msgstr ""
452
453 #: flask_security/unified_signin.py:277
454 msgid "Set up using SMS"
455 msgstr ""
456
457 #: flask_security/templates/security/_menu.html:2
458 msgid "Menu"
459 msgstr "Menu"
460
461 #: flask_security/templates/security/_menu.html:8
462 msgid "Unified Sign In"
463 msgstr ""
464
465 #: flask_security/templates/security/_menu.html:14
466 msgid "Forgot password"
467 msgstr "Wachtwoord vergeten"
468
469 #: flask_security/templates/security/_menu.html:17
470 msgid "Confirm account"
471 msgstr "Bevestig account"
472
473 #: flask_security/templates/security/change_password.html:6
474 msgid "Change password"
475 msgstr "Verander wachtwoord"
476
477 #: flask_security/templates/security/forgot_password.html:6
478 msgid "Send password reset instructions"
479 msgstr "Verzend wachtwoord reset instructies"
480
481 #: flask_security/templates/security/reset_password.html:6
482 msgid "Reset password"
483 msgstr "Reset wachtwoord"
484
485 #: flask_security/templates/security/send_confirmation.html:6
486 msgid "Resend confirmation instructions"
487 msgstr "Verzend bevestiging instructies opnieuw"
488
489 #: flask_security/templates/security/two_factor_setup.html:6
490 msgid "Two-factor authentication adds an extra layer of security to your account"
491 msgstr ""
492 "Dubbele Authenticatie voegt een extra laag van beveiliging toe aan uw "
493 "account"
494
495 #: flask_security/templates/security/two_factor_setup.html:7
496 msgid ""
497 "In addition to your username and password, you'll need to use a code that"
498 " we will send you"
499 msgstr ""
500 "Naast uw gebruikersnaam en wachtwoord, heeft u ook een code nodig dat we "
501 "u zullen toezenden"
502
503 #: flask_security/templates/security/two_factor_setup.html:18
504 msgid "To complete logging in, please enter the code sent to your mail"
505 msgstr ""
506 "Om verder in te loggen moet U de code die we naar uw e-mail hebben "
507 "gezonden invoeren"
508
509 #: flask_security/templates/security/two_factor_setup.html:21
510 #: flask_security/templates/security/us_setup.html:21
511 #, fuzzy
512 msgid ""
513 "Open your authenticator app on your device and scan the following qrcode "
514 "to start receiving codes:"
515 msgstr ""
516 "Open Google Authenticator op uw toestel en scan de volgende qrcode om "
517 "codes te kunnen ontvangen:"
518
519 #: flask_security/templates/security/two_factor_setup.html:22
520 msgid "Two factor authentication code"
521 msgstr "Dubbele Authenticatie code"
522
523 #: flask_security/templates/security/two_factor_setup.html:25
524 msgid "To Which Phone Number Should We Send Code To?"
525 msgstr "Naar welk telefoonnummer kunnen we code verzenden?"
526
527 #: flask_security/templates/security/two_factor_verify_code.html:6
528 msgid "Two-factor Authentication"
529 msgstr "Dubbele Authenticatie"
530
531 #: flask_security/templates/security/two_factor_verify_code.html:7
532 msgid "Please enter your authentication code"
533 msgstr "Voer uw authenticatie code in"
534
535 #: flask_security/templates/security/two_factor_verify_code.html:18
536 msgid "The code for authentication was sent to your email address"
537 msgstr "The code voor authenticatie is naar uw e-mail adres verzonden"
538
539 #: flask_security/templates/security/two_factor_verify_code.html:21
540 msgid "A mail was sent to us in order to reset your application account"
541 msgstr "Een bericht is naar uw e-mail adres verzonden om uw account te herstellen"
542
543 #: flask_security/templates/security/two_factor_verify_password.html:6
544 #: flask_security/templates/security/verify.html:6
545 msgid "Please Enter Your Password"
546 msgstr "Voer uw wachtwoord in"
547
548 #: flask_security/templates/security/us_setup.html:6
549 msgid "Setup Unified Sign In options"
550 msgstr ""
551
552 #: flask_security/templates/security/us_setup.html:22
553 msgid "Passwordless QRCode"
554 msgstr ""
555
556 #: flask_security/templates/security/us_setup.html:25
557 #: flask_security/templates/security/us_signin.html:23
558 #: flask_security/templates/security/us_verify.html:21
559 #, fuzzy
560 msgid "Code has been sent"
561 msgstr "Uw wachtwoord werd gereset"
562
563 #: flask_security/templates/security/us_setup.html:29
564 msgid "No methods have been enabled - nothing to setup"
565 msgstr ""
566
567 #: flask_security/templates/security/us_signin.html:15
568 #: flask_security/templates/security/us_verify.html:13
569 msgid "Request one-time code be sent"
570 msgstr ""
571
572 #: flask_security/templates/security/us_verify.html:6
573 #, fuzzy
574 msgid "Please re-authenticate"
575 msgstr "Voer uw authenticatie code in"
576
577 #: flask_security/templates/security/email/change_notice.html:1
578 msgid "Your password has been changed."
579 msgstr "Uw wachtwoord werd gewijzigd."
580
581 #: flask_security/templates/security/email/change_notice.html:3
582 msgid "If you did not change your password,"
583 msgstr "Als u uw wachtwoord niet hebt aangepast,"
584
585 #: flask_security/templates/security/email/change_notice.html:3
586 msgid "click here to reset it"
587 msgstr "Klik hier om het te resetten"
588
589 #: flask_security/templates/security/email/confirmation_instructions.html:1
590 msgid "Please confirm your email through the link below:"
591 msgstr "Gelieve uw e-mailadres te bevestigen via onderstaande link:"
592
593 #: flask_security/templates/security/email/confirmation_instructions.html:3
594 #: flask_security/templates/security/email/welcome.html:6
595 msgid "Confirm my account"
596 msgstr "Bevestig mijn account"
597
598 #: flask_security/templates/security/email/login_instructions.html:1
599 #: flask_security/templates/security/email/welcome.html:1
600 #, python-format
601 msgid "Welcome %(email)s!"
602 msgstr "Welkom, %(email)s!"
603
604 #: flask_security/templates/security/email/login_instructions.html:3
605 msgid "You can log into your account through the link below:"
606 msgstr "U kunt inloggen door onderstaande link te gebruiken:"
607
608 #: flask_security/templates/security/email/login_instructions.html:5
609 msgid "Login now"
610 msgstr "Nu inloggen"
611
612 #: flask_security/templates/security/email/reset_instructions.html:1
613 msgid "Click here to reset your password"
614 msgstr "Klik hier om uw wachtwoord te resetten"
615
616 #: flask_security/templates/security/email/two_factor_instructions.html:3
617 msgid "You can log into your account using the following code:"
618 msgstr "U kunt inloggen door de volgende code te gebruiken:"
619
620 #: flask_security/templates/security/email/two_factor_rescue.html:1
621 msgid "can not access mail account"
622 msgstr "kan niet in het e-mail account"
623
624 #: flask_security/templates/security/email/us_instructions.html:3
625 #, fuzzy
626 msgid "You can sign into your account using the following code:"
627 msgstr "U kunt inloggen door de volgende code te gebruiken:"
628
629 #: flask_security/templates/security/email/us_instructions.html:6
630 #, fuzzy
631 msgid "Or use the the link below:"
632 msgstr "U kan uw e-mailadres bevestigen via de onderstaande link:"
633
634 #: flask_security/templates/security/email/welcome.html:4
635 msgid "You can confirm your email through the link below:"
636 msgstr "U kan uw e-mailadres bevestigen via de onderstaande link:"
637
0 # Portuguese (Brazil) translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # José Neto <[email protected]>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-09-27 23:39-0300\n"
12 "Last-Translator: José Neto <[email protected]>\n"
13 "Language: pt_BR\n"
14 "Language-Team: \n"
15 "Plural-Forms: nplurals=2; plural=(n > 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Login obrigatório"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Bem-vindo"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Por favor, confirme seu email"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Instruções de login"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "Sua senha foi redefinida"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "Sua senha foi alterada"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instruções para redfinir a senha"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "Você não tem permissão para ver este recurso"
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Por favor, reautentique-se para acessar esta página."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "Obrigado. As instruções para a confirmação foram enviadas para %(email)s."
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "Obrigado. Seu email foi confirmado."
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "Seu email já foi confirmado."
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "Token de confirmação inválido."
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "%(email)s já está associado a uma conta."
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "Senha não confere"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "Senhas não conferem"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "Redirecionamentos para fora do domínio são proibidos"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr "As instruções para redefinir sua senha foram enviadas para %(email)s."
119
120 #: flask_security/core.py:311
121 #, python-format
122 msgid ""
123 "You did not reset your password within %(within)s. New instructions have "
124 "been sent to %(email)s."
125 msgstr ""
126 "Você não redefiniu sua senha dentro de %(within)s. Novas instruções foram"
127 " enviadas para %(email)s."
128
129 #: flask_security/core.py:317
130 msgid "Invalid reset password token."
131 msgstr "Token de redefinição de senha inválido."
132
133 #: flask_security/core.py:318
134 msgid "Email requires confirmation."
135 msgstr "O email requer confirmação."
136
137 #: flask_security/core.py:320
138 #, python-format
139 msgid "Confirmation instructions have been sent to %(email)s."
140 msgstr "As instruções de confirmaç foram enviadas para %(email)s."
141
142 #: flask_security/core.py:324
143 #, python-format
144 msgid ""
145 "You did not confirm your email within %(within)s. New instructions to "
146 "confirm your email have been sent to %(email)s."
147 msgstr ""
148 "Você não confirmou seu email dentro de %(within)s. Novas instruções foram"
149 " enviadas para %(email)s."
150
151 #: flask_security/core.py:332
152 #, python-format
153 msgid ""
154 "You did not login within %(within)s. New instructions to login have been "
155 "sent to %(email)s."
156 msgstr ""
157 "Você não logou dentro de %(within)s. Novas instruções para logar foram "
158 "enviadas para %(email)s."
159
160 #: flask_security/core.py:339
161 #, python-format
162 msgid "Instructions to login have been sent to %(email)s."
163 msgstr "Instruções para logar foram enviadas para %(email)s."
164
165 #: flask_security/core.py:342
166 msgid "Invalid login token."
167 msgstr "Token de login inválido."
168
169 #: flask_security/core.py:343
170 msgid "Account is disabled."
171 msgstr "Conta desabilitada."
172
173 #: flask_security/core.py:344
174 msgid "Email not provided"
175 msgstr "Email não informado"
176
177 #: flask_security/core.py:345
178 msgid "Invalid email address"
179 msgstr "Endereço de email inválido"
180
181 #: flask_security/core.py:346
182 #, fuzzy
183 msgid "Invalid code"
184 msgstr "Senha inválida"
185
186 #: flask_security/core.py:347
187 msgid "Password not provided"
188 msgstr "Senha não informada"
189
190 #: flask_security/core.py:348
191 msgid "No password is set for this user"
192 msgstr "Nenhuma senha definida para este usuário"
193
194 #: flask_security/core.py:350
195 #, fuzzy, python-format
196 msgid "Password must be at least %(length)s characters"
197 msgstr "A senha deve ter pelo menos 6 caracteres"
198
199 #: flask_security/core.py:353
200 msgid "Password not complex enough"
201 msgstr ""
202
203 #: flask_security/core.py:354
204 msgid "Password on breached list"
205 msgstr ""
206
207 #: flask_security/core.py:356
208 msgid "Failed to contact breached passwords site"
209 msgstr ""
210
211 #: flask_security/core.py:359
212 msgid "Phone number not valid e.g. missing country code"
213 msgstr ""
214
215 #: flask_security/core.py:360
216 msgid "Specified user does not exist"
217 msgstr "Usuário não existe"
218
219 #: flask_security/core.py:361
220 msgid "Invalid password"
221 msgstr "Senha inválida"
222
223 #: flask_security/core.py:362
224 msgid "Password or code submitted is not valid"
225 msgstr ""
226
227 #: flask_security/core.py:363
228 msgid "You have successfully logged in."
229 msgstr "Você logou com sucesso."
230
231 #: flask_security/core.py:364
232 msgid "Forgot password?"
233 msgstr "Esqueceu a senha?"
234
235 #: flask_security/core.py:366
236 msgid ""
237 "You successfully reset your password and you have been logged in "
238 "automatically."
239 msgstr "Você redefiniu sua senha com sucesso e foi logado automaticamente."
240
241 #: flask_security/core.py:373
242 msgid "Your new password must be different than your previous password."
243 msgstr "Sua nova senha deve ser diferente da sua senha anterior."
244
245 #: flask_security/core.py:376
246 msgid "You successfully changed your password."
247 msgstr "Você alterou sua senha com sucesso."
248
249 #: flask_security/core.py:377
250 msgid "Please log in to access this page."
251 msgstr "Por favor, logue para acessar esta página."
252
253 #: flask_security/core.py:378
254 msgid "Please reauthenticate to access this page."
255 msgstr "Por favor, reautentique-se para acessar esta página."
256
257 #: flask_security/core.py:379
258 msgid "Reauthentication successful"
259 msgstr ""
260
261 #: flask_security/core.py:381
262 msgid "You can only access this endpoint when not logged in."
263 msgstr ""
264
265 #: flask_security/core.py:384
266 msgid "Failed to send code. Please try again later"
267 msgstr ""
268
269 #: flask_security/core.py:385
270 msgid "Invalid Token"
271 msgstr ""
272
273 #: flask_security/core.py:386
274 msgid "Your token has been confirmed"
275 msgstr ""
276
277 #: flask_security/core.py:388
278 msgid "You successfully changed your two-factor method."
279 msgstr ""
280
281 #: flask_security/core.py:392
282 msgid "You successfully confirmed password"
283 msgstr ""
284
285 #: flask_security/core.py:396
286 msgid "Password confirmation is needed in order to access page"
287 msgstr ""
288
289 #: flask_security/core.py:400
290 msgid "You currently do not have permissions to access this page"
291 msgstr ""
292
293 #: flask_security/core.py:403
294 msgid "Marked method is not valid"
295 msgstr ""
296
297 #: flask_security/core.py:405
298 msgid "You successfully disabled two factor authorization."
299 msgstr ""
300
301 #: flask_security/core.py:408
302 msgid "Requested method is not valid"
303 msgstr ""
304
305 #: flask_security/core.py:410
306 #, python-format
307 msgid "Setup must be completed within %(within)s. Please start over."
308 msgstr ""
309
310 #: flask_security/core.py:413
311 msgid "Unified sign in setup successful"
312 msgstr ""
313
314 #: flask_security/core.py:414
315 msgid "You must specify a valid identity to sign in"
316 msgstr ""
317
318 #: flask_security/core.py:415
319 #, python-format
320 msgid "Use this code to sign in: %(code)s."
321 msgstr ""
322
323 #: flask_security/forms.py:50
324 msgid "Email Address"
325 msgstr "Endereço de email"
326
327 #: flask_security/forms.py:51
328 msgid "Password"
329 msgstr "Senha"
330
331 #: flask_security/forms.py:52
332 msgid "Remember Me"
333 msgstr "Lembre de mim"
334
335 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
336 #: flask_security/templates/security/login_user.html:6
337 #: flask_security/templates/security/send_login.html:6
338 msgid "Login"
339 msgstr "Login"
340
341 #: flask_security/forms.py:54
342 #: flask_security/templates/security/email/us_instructions.html:8
343 #: flask_security/templates/security/us_signin.html:6
344 msgid "Sign In"
345 msgstr ""
346
347 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
348 #: flask_security/templates/security/register_user.html:6
349 msgid "Register"
350 msgstr "Registro"
351
352 #: flask_security/forms.py:56
353 msgid "Resend Confirmation Instructions"
354 msgstr "Reenviar instruções de confirmação"
355
356 #: flask_security/forms.py:57
357 msgid "Recover Password"
358 msgstr "Recuperar senha"
359
360 #: flask_security/forms.py:58
361 msgid "Reset Password"
362 msgstr "Redefinir senha"
363
364 #: flask_security/forms.py:59
365 msgid "Retype Password"
366 msgstr "Reescreva a senha"
367
368 #: flask_security/forms.py:60
369 msgid "New Password"
370 msgstr "Nova senha"
371
372 #: flask_security/forms.py:61
373 msgid "Change Password"
374 msgstr "Alterar senha"
375
376 #: flask_security/forms.py:62
377 msgid "Send Login Link"
378 msgstr "Enviar link de login"
379
380 #: flask_security/forms.py:63
381 msgid "Verify Password"
382 msgstr ""
383
384 #: flask_security/forms.py:64
385 msgid "Change Method"
386 msgstr ""
387
388 #: flask_security/forms.py:65
389 msgid "Phone Number"
390 msgstr ""
391
392 #: flask_security/forms.py:66
393 msgid "Authentication Code"
394 msgstr ""
395
396 #: flask_security/forms.py:67
397 msgid "Submit"
398 msgstr ""
399
400 #: flask_security/forms.py:68
401 msgid "Submit Code"
402 msgstr ""
403
404 #: flask_security/forms.py:69
405 msgid "Error(s)"
406 msgstr ""
407
408 #: flask_security/forms.py:70
409 msgid "Identity"
410 msgstr ""
411
412 #: flask_security/forms.py:71
413 msgid "Send Code"
414 msgstr ""
415
416 #: flask_security/forms.py:72
417 #, fuzzy
418 msgid "Passcode"
419 msgstr "Senha"
420
421 #: flask_security/unified_signin.py:145
422 #, fuzzy
423 msgid "Code or Password"
424 msgstr "Recuperar senha"
425
426 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
427 msgid "Available Methods"
428 msgstr ""
429
430 #: flask_security/unified_signin.py:151
431 msgid "Via email"
432 msgstr ""
433
434 #: flask_security/unified_signin.py:151
435 msgid "Via SMS"
436 msgstr ""
437
438 #: flask_security/unified_signin.py:272
439 msgid "Set up using email"
440 msgstr ""
441
442 #: flask_security/unified_signin.py:275
443 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
444 msgstr ""
445
446 #: flask_security/unified_signin.py:277
447 msgid "Set up using SMS"
448 msgstr ""
449
450 #: flask_security/templates/security/_menu.html:2
451 msgid "Menu"
452 msgstr "Menu"
453
454 #: flask_security/templates/security/_menu.html:8
455 msgid "Unified Sign In"
456 msgstr ""
457
458 #: flask_security/templates/security/_menu.html:14
459 msgid "Forgot password"
460 msgstr "Esqueceu a senha"
461
462 #: flask_security/templates/security/_menu.html:17
463 msgid "Confirm account"
464 msgstr "Confirmar conta"
465
466 #: flask_security/templates/security/change_password.html:6
467 msgid "Change password"
468 msgstr "Alterar senha"
469
470 #: flask_security/templates/security/forgot_password.html:6
471 msgid "Send password reset instructions"
472 msgstr "Enviar instruções para redefinir a senha"
473
474 #: flask_security/templates/security/reset_password.html:6
475 msgid "Reset password"
476 msgstr "Redefinir senha"
477
478 #: flask_security/templates/security/send_confirmation.html:6
479 msgid "Resend confirmation instructions"
480 msgstr "Reenviar instruções de confirmação"
481
482 #: flask_security/templates/security/two_factor_setup.html:6
483 msgid "Two-factor authentication adds an extra layer of security to your account"
484 msgstr ""
485
486 #: flask_security/templates/security/two_factor_setup.html:7
487 msgid ""
488 "In addition to your username and password, you'll need to use a code that"
489 " we will send you"
490 msgstr ""
491
492 #: flask_security/templates/security/two_factor_setup.html:18
493 msgid "To complete logging in, please enter the code sent to your mail"
494 msgstr ""
495
496 #: flask_security/templates/security/two_factor_setup.html:21
497 #: flask_security/templates/security/us_setup.html:21
498 msgid ""
499 "Open your authenticator app on your device and scan the following qrcode "
500 "to start receiving codes:"
501 msgstr ""
502
503 #: flask_security/templates/security/two_factor_setup.html:22
504 msgid "Two factor authentication code"
505 msgstr ""
506
507 #: flask_security/templates/security/two_factor_setup.html:25
508 msgid "To Which Phone Number Should We Send Code To?"
509 msgstr ""
510
511 #: flask_security/templates/security/two_factor_verify_code.html:6
512 msgid "Two-factor Authentication"
513 msgstr ""
514
515 #: flask_security/templates/security/two_factor_verify_code.html:7
516 msgid "Please enter your authentication code"
517 msgstr ""
518
519 #: flask_security/templates/security/two_factor_verify_code.html:18
520 msgid "The code for authentication was sent to your email address"
521 msgstr ""
522
523 #: flask_security/templates/security/two_factor_verify_code.html:21
524 msgid "A mail was sent to us in order to reset your application account"
525 msgstr ""
526
527 #: flask_security/templates/security/two_factor_verify_password.html:6
528 #: flask_security/templates/security/verify.html:6
529 msgid "Please Enter Your Password"
530 msgstr ""
531
532 #: flask_security/templates/security/us_setup.html:6
533 msgid "Setup Unified Sign In options"
534 msgstr ""
535
536 #: flask_security/templates/security/us_setup.html:22
537 msgid "Passwordless QRCode"
538 msgstr ""
539
540 #: flask_security/templates/security/us_setup.html:25
541 #: flask_security/templates/security/us_signin.html:23
542 #: flask_security/templates/security/us_verify.html:21
543 #, fuzzy
544 msgid "Code has been sent"
545 msgstr "Sua senha foi redefinida"
546
547 #: flask_security/templates/security/us_setup.html:29
548 msgid "No methods have been enabled - nothing to setup"
549 msgstr ""
550
551 #: flask_security/templates/security/us_signin.html:15
552 #: flask_security/templates/security/us_verify.html:13
553 msgid "Request one-time code be sent"
554 msgstr ""
555
556 #: flask_security/templates/security/us_verify.html:6
557 #, fuzzy
558 msgid "Please re-authenticate"
559 msgstr "Por favor, reautentique-se para acessar esta página."
560
561 #: flask_security/templates/security/email/change_notice.html:1
562 msgid "Your password has been changed."
563 msgstr "Sua senha foi alterada."
564
565 #: flask_security/templates/security/email/change_notice.html:3
566 msgid "If you did not change your password,"
567 msgstr "Se você não alterou sua senha,"
568
569 #: flask_security/templates/security/email/change_notice.html:3
570 msgid "click here to reset it"
571 msgstr "clique aqui para resetar"
572
573 #: flask_security/templates/security/email/confirmation_instructions.html:1
574 msgid "Please confirm your email through the link below:"
575 msgstr "Por favor, confirme seu email através do link abaixo:"
576
577 #: flask_security/templates/security/email/confirmation_instructions.html:3
578 #: flask_security/templates/security/email/welcome.html:6
579 msgid "Confirm my account"
580 msgstr "Confirmar minha conta"
581
582 #: flask_security/templates/security/email/login_instructions.html:1
583 #: flask_security/templates/security/email/welcome.html:1
584 #, python-format
585 msgid "Welcome %(email)s!"
586 msgstr "Bem-vindo %(email)s!"
587
588 #: flask_security/templates/security/email/login_instructions.html:3
589 msgid "You can log into your account through the link below:"
590 msgstr "Você pode logar na sua conta através do link abaixo:"
591
592 #: flask_security/templates/security/email/login_instructions.html:5
593 msgid "Login now"
594 msgstr "Logar agora"
595
596 #: flask_security/templates/security/email/reset_instructions.html:1
597 msgid "Click here to reset your password"
598 msgstr "Clique aqui para redefinir sua senha"
599
600 #: flask_security/templates/security/email/two_factor_instructions.html:3
601 msgid "You can log into your account using the following code:"
602 msgstr ""
603
604 #: flask_security/templates/security/email/two_factor_rescue.html:1
605 msgid "can not access mail account"
606 msgstr ""
607
608 #: flask_security/templates/security/email/us_instructions.html:3
609 #, fuzzy
610 msgid "You can sign into your account using the following code:"
611 msgstr "Você pode logar na sua conta através do link abaixo:"
612
613 #: flask_security/templates/security/email/us_instructions.html:6
614 #, fuzzy
615 msgid "Or use the the link below:"
616 msgstr "Você pode confirmar seu email através do link abaixo:"
617
618 #: flask_security/templates/security/email/welcome.html:4
619 msgid "You can confirm your email through the link below:"
620 msgstr "Você pode confirmar seu email através do link abaixo:"
621
0 # Portuguese (Portugal) translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # Micael Grilo <[email protected]>, 2018.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2018-04-27 14:00+0100\n"
12 "Last-Translator: Micael Grilo <[email protected]>\n"
13 "Language: pt_PT\n"
14 "Language-Team: \n"
15 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "Login obrigatório"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "Bem-vindo"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "Por favor, confirme o seu email"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "Instruções de login"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "A sua palavra-passe foi redefinida"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "A sua palavra-passe foi alterada"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "Instruções para redefinir a palavra-passe"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "Não tem permissões para ver este recurso"
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "Por favor, reautentique-se para aceder esta página."
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "Obrigado. As instruções para a confirmação foram enviadas para %(email)s."
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "Obrigado. O seu email foi confirmado."
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "O seu email já foi confirmado."
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "Token de confirmação inválido."
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "%(email)s já está associado a uma conta."
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "Palavra-passe não coincide"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "Palavras-passe não coincidem"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "Redirecionamentos para fora do domínio são proibidos"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr ""
119 "As instruções para redefinir a sua palavra-passe foram enviadas para "
120 "%(email)s."
121
122 #: flask_security/core.py:311
123 #, python-format
124 msgid ""
125 "You did not reset your password within %(within)s. New instructions have "
126 "been sent to %(email)s."
127 msgstr ""
128 "Não redefiniu a sua palavra-passe dentro de %(within)s. Novas instruções "
129 "foram enviadas para %(email)s."
130
131 #: flask_security/core.py:317
132 msgid "Invalid reset password token."
133 msgstr "Token de redefinição de senha inválido."
134
135 #: flask_security/core.py:318
136 msgid "Email requires confirmation."
137 msgstr "O email requer confirmação."
138
139 #: flask_security/core.py:320
140 #, python-format
141 msgid "Confirmation instructions have been sent to %(email)s."
142 msgstr "As instruções de confirmação foram enviadas para %(email)s."
143
144 #: flask_security/core.py:324
145 #, python-format
146 msgid ""
147 "You did not confirm your email within %(within)s. New instructions to "
148 "confirm your email have been sent to %(email)s."
149 msgstr ""
150 "Não confirmou o seu email dentro de %(within)s. Novas instruções foram "
151 "enviadas para %(email)s."
152
153 #: flask_security/core.py:332
154 #, python-format
155 msgid ""
156 "You did not login within %(within)s. New instructions to login have been "
157 "sent to %(email)s."
158 msgstr ""
159 "Não iniciou sessão dentro de %(within)s. Novas instruções de inicio de "
160 "sessão foram enviadas para %(email)s."
161
162 #: flask_security/core.py:339
163 #, python-format
164 msgid "Instructions to login have been sent to %(email)s."
165 msgstr "Instruções para o inicio de sessão foram enviadas para %(email)s."
166
167 #: flask_security/core.py:342
168 msgid "Invalid login token."
169 msgstr "Token de login inválido."
170
171 #: flask_security/core.py:343
172 msgid "Account is disabled."
173 msgstr "Conta desactivada."
174
175 #: flask_security/core.py:344
176 msgid "Email not provided"
177 msgstr "Email em falta"
178
179 #: flask_security/core.py:345
180 msgid "Invalid email address"
181 msgstr "Endereço de email inválido"
182
183 #: flask_security/core.py:346
184 #, fuzzy
185 msgid "Invalid code"
186 msgstr "Palavra-passe inválida"
187
188 #: flask_security/core.py:347
189 msgid "Password not provided"
190 msgstr "Palavra-passe em falta"
191
192 #: flask_security/core.py:348
193 msgid "No password is set for this user"
194 msgstr "Nenhuma palavra-passe foi definida para este utilizador"
195
196 #: flask_security/core.py:350
197 #, fuzzy, python-format
198 msgid "Password must be at least %(length)s characters"
199 msgstr "A palavra-passe deve ter pelo menos 6 caracteres"
200
201 #: flask_security/core.py:353
202 msgid "Password not complex enough"
203 msgstr ""
204
205 #: flask_security/core.py:354
206 msgid "Password on breached list"
207 msgstr ""
208
209 #: flask_security/core.py:356
210 msgid "Failed to contact breached passwords site"
211 msgstr ""
212
213 #: flask_security/core.py:359
214 msgid "Phone number not valid e.g. missing country code"
215 msgstr ""
216
217 #: flask_security/core.py:360
218 msgid "Specified user does not exist"
219 msgstr "Utilizador não existe"
220
221 #: flask_security/core.py:361
222 msgid "Invalid password"
223 msgstr "Palavra-passe inválida"
224
225 #: flask_security/core.py:362
226 msgid "Password or code submitted is not valid"
227 msgstr ""
228
229 #: flask_security/core.py:363
230 msgid "You have successfully logged in."
231 msgstr "Sessão iniciada com sucesso."
232
233 #: flask_security/core.py:364
234 msgid "Forgot password?"
235 msgstr "Esqueceu a palavra-passe?"
236
237 #: flask_security/core.py:366
238 msgid ""
239 "You successfully reset your password and you have been logged in "
240 "automatically."
241 msgstr ""
242 "Redefiniu a sua palavra-passe com sucesso e iniciou sessão "
243 "automaticamente."
244
245 #: flask_security/core.py:373
246 msgid "Your new password must be different than your previous password."
247 msgstr "A sua nova palavra-passe deve ser diferente da anterior."
248
249 #: flask_security/core.py:376
250 msgid "You successfully changed your password."
251 msgstr "Alterou a sua palavra-passe com sucesso."
252
253 #: flask_security/core.py:377
254 msgid "Please log in to access this page."
255 msgstr "Por favor, inicie sessão para aceder a esta página."
256
257 #: flask_security/core.py:378
258 msgid "Please reauthenticate to access this page."
259 msgstr "Por favor, reautentique-se para aceder esta página."
260
261 #: flask_security/core.py:379
262 msgid "Reauthentication successful"
263 msgstr ""
264
265 #: flask_security/core.py:381
266 msgid "You can only access this endpoint when not logged in."
267 msgstr ""
268
269 #: flask_security/core.py:384
270 msgid "Failed to send code. Please try again later"
271 msgstr ""
272
273 #: flask_security/core.py:385
274 msgid "Invalid Token"
275 msgstr ""
276
277 #: flask_security/core.py:386
278 msgid "Your token has been confirmed"
279 msgstr ""
280
281 #: flask_security/core.py:388
282 msgid "You successfully changed your two-factor method."
283 msgstr ""
284
285 #: flask_security/core.py:392
286 msgid "You successfully confirmed password"
287 msgstr ""
288
289 #: flask_security/core.py:396
290 msgid "Password confirmation is needed in order to access page"
291 msgstr ""
292
293 #: flask_security/core.py:400
294 msgid "You currently do not have permissions to access this page"
295 msgstr ""
296
297 #: flask_security/core.py:403
298 msgid "Marked method is not valid"
299 msgstr ""
300
301 #: flask_security/core.py:405
302 msgid "You successfully disabled two factor authorization."
303 msgstr ""
304
305 #: flask_security/core.py:408
306 msgid "Requested method is not valid"
307 msgstr ""
308
309 #: flask_security/core.py:410
310 #, python-format
311 msgid "Setup must be completed within %(within)s. Please start over."
312 msgstr ""
313
314 #: flask_security/core.py:413
315 msgid "Unified sign in setup successful"
316 msgstr ""
317
318 #: flask_security/core.py:414
319 msgid "You must specify a valid identity to sign in"
320 msgstr ""
321
322 #: flask_security/core.py:415
323 #, python-format
324 msgid "Use this code to sign in: %(code)s."
325 msgstr ""
326
327 #: flask_security/forms.py:50
328 msgid "Email Address"
329 msgstr "Endereço de email"
330
331 #: flask_security/forms.py:51
332 msgid "Password"
333 msgstr "Palavra-passe"
334
335 #: flask_security/forms.py:52
336 msgid "Remember Me"
337 msgstr "Lembrar-me"
338
339 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
340 #: flask_security/templates/security/login_user.html:6
341 #: flask_security/templates/security/send_login.html:6
342 msgid "Login"
343 msgstr "Login"
344
345 #: flask_security/forms.py:54
346 #: flask_security/templates/security/email/us_instructions.html:8
347 #: flask_security/templates/security/us_signin.html:6
348 msgid "Sign In"
349 msgstr ""
350
351 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
352 #: flask_security/templates/security/register_user.html:6
353 msgid "Register"
354 msgstr "Registo"
355
356 #: flask_security/forms.py:56
357 msgid "Resend Confirmation Instructions"
358 msgstr "Reenviar instruções de confirmação"
359
360 #: flask_security/forms.py:57
361 msgid "Recover Password"
362 msgstr "Recuperar palavra-passe"
363
364 #: flask_security/forms.py:58
365 msgid "Reset Password"
366 msgstr "Redefinir palavra-passe"
367
368 #: flask_security/forms.py:59
369 msgid "Retype Password"
370 msgstr "Reescreva a palavra-passe"
371
372 #: flask_security/forms.py:60
373 msgid "New Password"
374 msgstr "Nova palavra-passe"
375
376 #: flask_security/forms.py:61
377 msgid "Change Password"
378 msgstr "Alterar palavra-passe"
379
380 #: flask_security/forms.py:62
381 msgid "Send Login Link"
382 msgstr "Enviar endereço de login"
383
384 #: flask_security/forms.py:63
385 msgid "Verify Password"
386 msgstr ""
387
388 #: flask_security/forms.py:64
389 msgid "Change Method"
390 msgstr ""
391
392 #: flask_security/forms.py:65
393 msgid "Phone Number"
394 msgstr ""
395
396 #: flask_security/forms.py:66
397 msgid "Authentication Code"
398 msgstr ""
399
400 #: flask_security/forms.py:67
401 msgid "Submit"
402 msgstr ""
403
404 #: flask_security/forms.py:68
405 msgid "Submit Code"
406 msgstr ""
407
408 #: flask_security/forms.py:69
409 msgid "Error(s)"
410 msgstr ""
411
412 #: flask_security/forms.py:70
413 msgid "Identity"
414 msgstr ""
415
416 #: flask_security/forms.py:71
417 msgid "Send Code"
418 msgstr ""
419
420 #: flask_security/forms.py:72
421 #, fuzzy
422 msgid "Passcode"
423 msgstr "Palavra-passe"
424
425 #: flask_security/unified_signin.py:145
426 #, fuzzy
427 msgid "Code or Password"
428 msgstr "Recuperar palavra-passe"
429
430 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
431 msgid "Available Methods"
432 msgstr ""
433
434 #: flask_security/unified_signin.py:151
435 msgid "Via email"
436 msgstr ""
437
438 #: flask_security/unified_signin.py:151
439 msgid "Via SMS"
440 msgstr ""
441
442 #: flask_security/unified_signin.py:272
443 msgid "Set up using email"
444 msgstr ""
445
446 #: flask_security/unified_signin.py:275
447 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
448 msgstr ""
449
450 #: flask_security/unified_signin.py:277
451 msgid "Set up using SMS"
452 msgstr ""
453
454 #: flask_security/templates/security/_menu.html:2
455 msgid "Menu"
456 msgstr "Menu"
457
458 #: flask_security/templates/security/_menu.html:8
459 msgid "Unified Sign In"
460 msgstr ""
461
462 #: flask_security/templates/security/_menu.html:14
463 msgid "Forgot password"
464 msgstr "Esqueceu a palavra-passe"
465
466 #: flask_security/templates/security/_menu.html:17
467 msgid "Confirm account"
468 msgstr "Confirmar conta"
469
470 #: flask_security/templates/security/change_password.html:6
471 msgid "Change password"
472 msgstr "Alterar palavra-passe"
473
474 #: flask_security/templates/security/forgot_password.html:6
475 msgid "Send password reset instructions"
476 msgstr "Enviar instruções para redefinir a palavra-passe"
477
478 #: flask_security/templates/security/reset_password.html:6
479 msgid "Reset password"
480 msgstr "Redefinir palavra-passe"
481
482 #: flask_security/templates/security/send_confirmation.html:6
483 msgid "Resend confirmation instructions"
484 msgstr "Reenviar instruções de confirmação"
485
486 #: flask_security/templates/security/two_factor_setup.html:6
487 msgid "Two-factor authentication adds an extra layer of security to your account"
488 msgstr ""
489
490 #: flask_security/templates/security/two_factor_setup.html:7
491 msgid ""
492 "In addition to your username and password, you'll need to use a code that"
493 " we will send you"
494 msgstr ""
495
496 #: flask_security/templates/security/two_factor_setup.html:18
497 msgid "To complete logging in, please enter the code sent to your mail"
498 msgstr ""
499
500 #: flask_security/templates/security/two_factor_setup.html:21
501 #: flask_security/templates/security/us_setup.html:21
502 msgid ""
503 "Open your authenticator app on your device and scan the following qrcode "
504 "to start receiving codes:"
505 msgstr ""
506
507 #: flask_security/templates/security/two_factor_setup.html:22
508 msgid "Two factor authentication code"
509 msgstr ""
510
511 #: flask_security/templates/security/two_factor_setup.html:25
512 msgid "To Which Phone Number Should We Send Code To?"
513 msgstr ""
514
515 #: flask_security/templates/security/two_factor_verify_code.html:6
516 msgid "Two-factor Authentication"
517 msgstr ""
518
519 #: flask_security/templates/security/two_factor_verify_code.html:7
520 msgid "Please enter your authentication code"
521 msgstr ""
522
523 #: flask_security/templates/security/two_factor_verify_code.html:18
524 msgid "The code for authentication was sent to your email address"
525 msgstr ""
526
527 #: flask_security/templates/security/two_factor_verify_code.html:21
528 msgid "A mail was sent to us in order to reset your application account"
529 msgstr ""
530
531 #: flask_security/templates/security/two_factor_verify_password.html:6
532 #: flask_security/templates/security/verify.html:6
533 msgid "Please Enter Your Password"
534 msgstr ""
535
536 #: flask_security/templates/security/us_setup.html:6
537 msgid "Setup Unified Sign In options"
538 msgstr ""
539
540 #: flask_security/templates/security/us_setup.html:22
541 msgid "Passwordless QRCode"
542 msgstr ""
543
544 #: flask_security/templates/security/us_setup.html:25
545 #: flask_security/templates/security/us_signin.html:23
546 #: flask_security/templates/security/us_verify.html:21
547 #, fuzzy
548 msgid "Code has been sent"
549 msgstr "A sua palavra-passe foi redefinida"
550
551 #: flask_security/templates/security/us_setup.html:29
552 msgid "No methods have been enabled - nothing to setup"
553 msgstr ""
554
555 #: flask_security/templates/security/us_signin.html:15
556 #: flask_security/templates/security/us_verify.html:13
557 msgid "Request one-time code be sent"
558 msgstr ""
559
560 #: flask_security/templates/security/us_verify.html:6
561 #, fuzzy
562 msgid "Please re-authenticate"
563 msgstr "Por favor, reautentique-se para aceder esta página."
564
565 #: flask_security/templates/security/email/change_notice.html:1
566 msgid "Your password has been changed."
567 msgstr "A sua palavra-passe foi alterada."
568
569 #: flask_security/templates/security/email/change_notice.html:3
570 msgid "If you did not change your password,"
571 msgstr "Não alterou a sua palavra-passe,"
572
573 #: flask_security/templates/security/email/change_notice.html:3
574 msgid "click here to reset it"
575 msgstr "clique aqui para redefinir"
576
577 #: flask_security/templates/security/email/confirmation_instructions.html:1
578 msgid "Please confirm your email through the link below:"
579 msgstr "Por favor, confirme o seu email através do endereço abaixo:"
580
581 #: flask_security/templates/security/email/confirmation_instructions.html:3
582 #: flask_security/templates/security/email/welcome.html:6
583 msgid "Confirm my account"
584 msgstr "Confirmar minha conta"
585
586 #: flask_security/templates/security/email/login_instructions.html:1
587 #: flask_security/templates/security/email/welcome.html:1
588 #, python-format
589 msgid "Welcome %(email)s!"
590 msgstr "Bem-vindo %(email)s!"
591
592 #: flask_security/templates/security/email/login_instructions.html:3
593 msgid "You can log into your account through the link below:"
594 msgstr "Você pode iniciar sessão na sua conta através do endereço abaixo:"
595
596 #: flask_security/templates/security/email/login_instructions.html:5
597 msgid "Login now"
598 msgstr "Iniciar sessão"
599
600 #: flask_security/templates/security/email/reset_instructions.html:1
601 msgid "Click here to reset your password"
602 msgstr "Clique aqui para redefinir a sua palavra-passe"
603
604 #: flask_security/templates/security/email/two_factor_instructions.html:3
605 msgid "You can log into your account using the following code:"
606 msgstr ""
607
608 #: flask_security/templates/security/email/two_factor_rescue.html:1
609 msgid "can not access mail account"
610 msgstr ""
611
612 #: flask_security/templates/security/email/us_instructions.html:3
613 #, fuzzy
614 msgid "You can sign into your account using the following code:"
615 msgstr "Você pode iniciar sessão na sua conta através do endereço abaixo:"
616
617 #: flask_security/templates/security/email/us_instructions.html:6
618 #, fuzzy
619 msgid "Or use the the link below:"
620 msgstr "Você pode confirmar o seu email através do endereço abaixo:"
621
622 #: flask_security/templates/security/email/welcome.html:4
623 msgid "You can confirm your email through the link below:"
624 msgstr "Você pode confirmar o seu email através do endereço abaixo:"
625
0 # Russian Translations for Flask-Security.
1 # Copyright (C) 2017 CERN, leovp
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2017-04-15 15:15+0300\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language: ru_RU\n"
14 "Language-Team: Leonid R. <[email protected]>\n"
15 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
16 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
17 "MIME-Version: 1.0\n"
18 "Content-Type: text/plain; charset=utf-8\n"
19 "Content-Transfer-Encoding: 8bit\n"
20 "Generated-By: Babel 2.8.0\n"
21
22 #: flask_security/core.py:207
23 msgid "Login Required"
24 msgstr "Требуется авторизация"
25
26 #: flask_security/core.py:208
27 #: flask_security/templates/security/email/two_factor_instructions.html:1
28 #: flask_security/templates/security/email/us_instructions.html:1
29 msgid "Welcome"
30 msgstr "Добро пожаловать"
31
32 #: flask_security/core.py:209
33 msgid "Please confirm your email"
34 msgstr "Пожалуйста, подтвердите свой почтовый адрес"
35
36 #: flask_security/core.py:210
37 msgid "Login instructions"
38 msgstr "Инструкция по входу"
39
40 #: flask_security/core.py:211
41 #: flask_security/templates/security/email/reset_notice.html:1
42 msgid "Your password has been reset"
43 msgstr "Ваш пароль был сброшен"
44
45 #: flask_security/core.py:212
46 msgid "Your password has been changed"
47 msgstr "Ваш пароль был изменён"
48
49 #: flask_security/core.py:213
50 msgid "Password reset instructions"
51 msgstr "Инструкция по восстановлению пароля"
52
53 #: flask_security/core.py:216
54 msgid "Two-factor Login"
55 msgstr ""
56
57 #: flask_security/core.py:217
58 msgid "Two-factor Rescue"
59 msgstr ""
60
61 #: flask_security/core.py:266
62 msgid "Verification Code"
63 msgstr ""
64
65 #: flask_security/core.py:282
66 msgid "Input not appropriate for requested API"
67 msgstr ""
68
69 #: flask_security/core.py:283
70 msgid "You do not have permission to view this resource."
71 msgstr "У вас нет прав доступа к этому ресурсу."
72
73 #: flask_security/core.py:285
74 msgid "You are not authenticated. Please supply the correct credentials."
75 msgstr ""
76
77 #: flask_security/core.py:289
78 #, fuzzy
79 msgid "You must re-authenticate to access this endpoint"
80 msgstr "Пожалуйста, войдите заново чтобы получить доступ к этой странице."
81
82 #: flask_security/core.py:293
83 #, python-format
84 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
85 msgstr "Спасибо. Инструкция по подтверждению аккаунта отправлена на %(email)s."
86
87 #: flask_security/core.py:296
88 msgid "Thank you. Your email has been confirmed."
89 msgstr "Спасибо. Ваш почтовый адрес был подтверждён."
90
91 #: flask_security/core.py:297
92 msgid "Your email has already been confirmed."
93 msgstr "Ваш почтовый адрес уже подтверждён."
94
95 #: flask_security/core.py:298
96 msgid "Invalid confirmation token."
97 msgstr "Неверный токен для подтверждения аккаунта."
98
99 #: flask_security/core.py:300
100 #, python-format
101 msgid "%(email)s is already associated with an account."
102 msgstr "%(email)s уже привязан к другому аккаунту."
103
104 #: flask_security/core.py:303
105 msgid "Password does not match"
106 msgstr "Пароль не подходит"
107
108 #: flask_security/core.py:304
109 msgid "Passwords do not match"
110 msgstr "Пароли не совпадают"
111
112 #: flask_security/core.py:305
113 msgid "Redirections outside the domain are forbidden"
114 msgstr "Перенаправления вне текущего домена запрещены"
115
116 #: flask_security/core.py:307
117 #, python-format
118 msgid "Instructions to reset your password have been sent to %(email)s."
119 msgstr "Инструкция по восстановлению пароля отправлена на %(email)s."
120
121 #: flask_security/core.py:311
122 #, python-format
123 msgid ""
124 "You did not reset your password within %(within)s. New instructions have "
125 "been sent to %(email)s."
126 msgstr ""
127 "Вы не восстановили пароль в течение %(within)s. Новая инструкция "
128 "отправлена на %(email)s."
129
130 #: flask_security/core.py:317
131 msgid "Invalid reset password token."
132 msgstr "Неверный токен для восстановления пароля."
133
134 #: flask_security/core.py:318
135 msgid "Email requires confirmation."
136 msgstr "Почтовый адрес требует подтверждения."
137
138 #: flask_security/core.py:320
139 #, python-format
140 msgid "Confirmation instructions have been sent to %(email)s."
141 msgstr "Инструкция по подтверждению аккаунта отправлена на %(email)s."
142
143 #: flask_security/core.py:324
144 #, python-format
145 msgid ""
146 "You did not confirm your email within %(within)s. New instructions to "
147 "confirm your email have been sent to %(email)s."
148 msgstr ""
149 "Вы не подтвердили свой почтовый адрес в течение %(within)s. Новая "
150 "инструкция по подтверждению отправлена на %(email)s."
151
152 #: flask_security/core.py:332
153 #, python-format
154 msgid ""
155 "You did not login within %(within)s. New instructions to login have been "
156 "sent to %(email)s."
157 msgstr ""
158 "Вы не вошли в течение %(within)s. Новая инструкция по входу отправлена на"
159 " %(email)s."
160
161 #: flask_security/core.py:339
162 #, python-format
163 msgid "Instructions to login have been sent to %(email)s."
164 msgstr "Инструкция по входу отправлена на %(email)s."
165
166 #: flask_security/core.py:342
167 msgid "Invalid login token."
168 msgstr "Неверный токен для входа."
169
170 #: flask_security/core.py:343
171 msgid "Account is disabled."
172 msgstr "Аккаунт отключён."
173
174 #: flask_security/core.py:344
175 msgid "Email not provided"
176 msgstr "Почтовый адрес не введён"
177
178 #: flask_security/core.py:345
179 msgid "Invalid email address"
180 msgstr "Неверный почтовый адрес"
181
182 #: flask_security/core.py:346
183 #, fuzzy
184 msgid "Invalid code"
185 msgstr "Неверный пароль"
186
187 #: flask_security/core.py:347
188 msgid "Password not provided"
189 msgstr "Пароль не введён"
190
191 #: flask_security/core.py:348
192 msgid "No password is set for this user"
193 msgstr "У данного пользователя не установлен пароль"
194
195 #: flask_security/core.py:350
196 #, fuzzy, python-format
197 msgid "Password must be at least %(length)s characters"
198 msgstr "Пароль должен содержать как минимум 6 символов"
199
200 #: flask_security/core.py:353
201 msgid "Password not complex enough"
202 msgstr ""
203
204 #: flask_security/core.py:354
205 msgid "Password on breached list"
206 msgstr ""
207
208 #: flask_security/core.py:356
209 msgid "Failed to contact breached passwords site"
210 msgstr ""
211
212 #: flask_security/core.py:359
213 msgid "Phone number not valid e.g. missing country code"
214 msgstr ""
215
216 #: flask_security/core.py:360
217 msgid "Specified user does not exist"
218 msgstr "Данный пользователь не найден"
219
220 #: flask_security/core.py:361
221 msgid "Invalid password"
222 msgstr "Неверный пароль"
223
224 #: flask_security/core.py:362
225 msgid "Password or code submitted is not valid"
226 msgstr ""
227
228 #: flask_security/core.py:363
229 msgid "You have successfully logged in."
230 msgstr "Вы вошли."
231
232 #: flask_security/core.py:364
233 msgid "Forgot password?"
234 msgstr "Забыли пароль?"
235
236 #: flask_security/core.py:366
237 msgid ""
238 "You successfully reset your password and you have been logged in "
239 "automatically."
240 msgstr "Ваш пароль был восстановлен и вы автоматически вошли."
241
242 #: flask_security/core.py:373
243 msgid "Your new password must be different than your previous password."
244 msgstr "Ваш новый пароль должен отличаться от предыдущего."
245
246 #: flask_security/core.py:376
247 msgid "You successfully changed your password."
248 msgstr "Вы удачно сменили пароль."
249
250 #: flask_security/core.py:377
251 msgid "Please log in to access this page."
252 msgstr "Пожалуйста, войдите чтобы получить доступ к этой странице."
253
254 #: flask_security/core.py:378
255 msgid "Please reauthenticate to access this page."
256 msgstr "Пожалуйста, войдите заново чтобы получить доступ к этой странице."
257
258 #: flask_security/core.py:379
259 msgid "Reauthentication successful"
260 msgstr ""
261
262 #: flask_security/core.py:381
263 msgid "You can only access this endpoint when not logged in."
264 msgstr ""
265
266 #: flask_security/core.py:384
267 msgid "Failed to send code. Please try again later"
268 msgstr ""
269
270 #: flask_security/core.py:385
271 msgid "Invalid Token"
272 msgstr ""
273
274 #: flask_security/core.py:386
275 msgid "Your token has been confirmed"
276 msgstr ""
277
278 #: flask_security/core.py:388
279 msgid "You successfully changed your two-factor method."
280 msgstr ""
281
282 #: flask_security/core.py:392
283 msgid "You successfully confirmed password"
284 msgstr ""
285
286 #: flask_security/core.py:396
287 msgid "Password confirmation is needed in order to access page"
288 msgstr ""
289
290 #: flask_security/core.py:400
291 msgid "You currently do not have permissions to access this page"
292 msgstr ""
293
294 #: flask_security/core.py:403
295 msgid "Marked method is not valid"
296 msgstr ""
297
298 #: flask_security/core.py:405
299 msgid "You successfully disabled two factor authorization."
300 msgstr ""
301
302 #: flask_security/core.py:408
303 msgid "Requested method is not valid"
304 msgstr ""
305
306 #: flask_security/core.py:410
307 #, python-format
308 msgid "Setup must be completed within %(within)s. Please start over."
309 msgstr ""
310
311 #: flask_security/core.py:413
312 msgid "Unified sign in setup successful"
313 msgstr ""
314
315 #: flask_security/core.py:414
316 msgid "You must specify a valid identity to sign in"
317 msgstr ""
318
319 #: flask_security/core.py:415
320 #, python-format
321 msgid "Use this code to sign in: %(code)s."
322 msgstr ""
323
324 #: flask_security/forms.py:50
325 msgid "Email Address"
326 msgstr "Почтовый адрес"
327
328 #: flask_security/forms.py:51
329 msgid "Password"
330 msgstr "Пароль"
331
332 #: flask_security/forms.py:52
333 msgid "Remember Me"
334 msgstr "Запомнить меня"
335
336 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
337 #: flask_security/templates/security/login_user.html:6
338 #: flask_security/templates/security/send_login.html:6
339 msgid "Login"
340 msgstr "Войти"
341
342 #: flask_security/forms.py:54
343 #: flask_security/templates/security/email/us_instructions.html:8
344 #: flask_security/templates/security/us_signin.html:6
345 msgid "Sign In"
346 msgstr ""
347
348 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
349 #: flask_security/templates/security/register_user.html:6
350 msgid "Register"
351 msgstr "Зарегистрироваться"
352
353 #: flask_security/forms.py:56
354 msgid "Resend Confirmation Instructions"
355 msgstr "Заново отправить инструкцию по подтверждению аккаунта"
356
357 #: flask_security/forms.py:57
358 msgid "Recover Password"
359 msgstr "Восстановить пароль"
360
361 #: flask_security/forms.py:58
362 msgid "Reset Password"
363 msgstr "Сбросить пароль"
364
365 #: flask_security/forms.py:59
366 msgid "Retype Password"
367 msgstr "Подтверждение пароля"
368
369 #: flask_security/forms.py:60
370 msgid "New Password"
371 msgstr "Новый пароль"
372
373 #: flask_security/forms.py:61
374 msgid "Change Password"
375 msgstr "Сменить пароль"
376
377 #: flask_security/forms.py:62
378 msgid "Send Login Link"
379 msgstr "Отправить ссылку для входа"
380
381 #: flask_security/forms.py:63
382 msgid "Verify Password"
383 msgstr ""
384
385 #: flask_security/forms.py:64
386 msgid "Change Method"
387 msgstr ""
388
389 #: flask_security/forms.py:65
390 msgid "Phone Number"
391 msgstr ""
392
393 #: flask_security/forms.py:66
394 msgid "Authentication Code"
395 msgstr ""
396
397 #: flask_security/forms.py:67
398 msgid "Submit"
399 msgstr ""
400
401 #: flask_security/forms.py:68
402 msgid "Submit Code"
403 msgstr ""
404
405 #: flask_security/forms.py:69
406 msgid "Error(s)"
407 msgstr ""
408
409 #: flask_security/forms.py:70
410 msgid "Identity"
411 msgstr ""
412
413 #: flask_security/forms.py:71
414 msgid "Send Code"
415 msgstr ""
416
417 #: flask_security/forms.py:72
418 #, fuzzy
419 msgid "Passcode"
420 msgstr "Пароль"
421
422 #: flask_security/unified_signin.py:145
423 #, fuzzy
424 msgid "Code or Password"
425 msgstr "Восстановить пароль"
426
427 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
428 msgid "Available Methods"
429 msgstr ""
430
431 #: flask_security/unified_signin.py:151
432 msgid "Via email"
433 msgstr ""
434
435 #: flask_security/unified_signin.py:151
436 msgid "Via SMS"
437 msgstr ""
438
439 #: flask_security/unified_signin.py:272
440 msgid "Set up using email"
441 msgstr ""
442
443 #: flask_security/unified_signin.py:275
444 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
445 msgstr ""
446
447 #: flask_security/unified_signin.py:277
448 msgid "Set up using SMS"
449 msgstr ""
450
451 #: flask_security/templates/security/_menu.html:2
452 msgid "Menu"
453 msgstr "Меню"
454
455 #: flask_security/templates/security/_menu.html:8
456 msgid "Unified Sign In"
457 msgstr ""
458
459 #: flask_security/templates/security/_menu.html:14
460 msgid "Forgot password"
461 msgstr "Забыли пароль"
462
463 #: flask_security/templates/security/_menu.html:17
464 msgid "Confirm account"
465 msgstr "Подтвердить аккаунт"
466
467 #: flask_security/templates/security/change_password.html:6
468 msgid "Change password"
469 msgstr "Сменить пароль"
470
471 #: flask_security/templates/security/forgot_password.html:6
472 msgid "Send password reset instructions"
473 msgstr "Отправить инструкцию по сбросу пароля"
474
475 #: flask_security/templates/security/reset_password.html:6
476 msgid "Reset password"
477 msgstr "Сбросить пароль"
478
479 #: flask_security/templates/security/send_confirmation.html:6
480 msgid "Resend confirmation instructions"
481 msgstr "Заново отправить инструкцию по подтверждению аккаунта"
482
483 #: flask_security/templates/security/two_factor_setup.html:6
484 msgid "Two-factor authentication adds an extra layer of security to your account"
485 msgstr ""
486
487 #: flask_security/templates/security/two_factor_setup.html:7
488 msgid ""
489 "In addition to your username and password, you'll need to use a code that"
490 " we will send you"
491 msgstr ""
492
493 #: flask_security/templates/security/two_factor_setup.html:18
494 msgid "To complete logging in, please enter the code sent to your mail"
495 msgstr ""
496
497 #: flask_security/templates/security/two_factor_setup.html:21
498 #: flask_security/templates/security/us_setup.html:21
499 msgid ""
500 "Open your authenticator app on your device and scan the following qrcode "
501 "to start receiving codes:"
502 msgstr ""
503
504 #: flask_security/templates/security/two_factor_setup.html:22
505 msgid "Two factor authentication code"
506 msgstr ""
507
508 #: flask_security/templates/security/two_factor_setup.html:25
509 msgid "To Which Phone Number Should We Send Code To?"
510 msgstr ""
511
512 #: flask_security/templates/security/two_factor_verify_code.html:6
513 msgid "Two-factor Authentication"
514 msgstr ""
515
516 #: flask_security/templates/security/two_factor_verify_code.html:7
517 msgid "Please enter your authentication code"
518 msgstr ""
519
520 #: flask_security/templates/security/two_factor_verify_code.html:18
521 msgid "The code for authentication was sent to your email address"
522 msgstr ""
523
524 #: flask_security/templates/security/two_factor_verify_code.html:21
525 msgid "A mail was sent to us in order to reset your application account"
526 msgstr ""
527
528 #: flask_security/templates/security/two_factor_verify_password.html:6
529 #: flask_security/templates/security/verify.html:6
530 msgid "Please Enter Your Password"
531 msgstr ""
532
533 #: flask_security/templates/security/us_setup.html:6
534 msgid "Setup Unified Sign In options"
535 msgstr ""
536
537 #: flask_security/templates/security/us_setup.html:22
538 msgid "Passwordless QRCode"
539 msgstr ""
540
541 #: flask_security/templates/security/us_setup.html:25
542 #: flask_security/templates/security/us_signin.html:23
543 #: flask_security/templates/security/us_verify.html:21
544 #, fuzzy
545 msgid "Code has been sent"
546 msgstr "Ваш пароль был сброшен"
547
548 #: flask_security/templates/security/us_setup.html:29
549 msgid "No methods have been enabled - nothing to setup"
550 msgstr ""
551
552 #: flask_security/templates/security/us_signin.html:15
553 #: flask_security/templates/security/us_verify.html:13
554 msgid "Request one-time code be sent"
555 msgstr ""
556
557 #: flask_security/templates/security/us_verify.html:6
558 #, fuzzy
559 msgid "Please re-authenticate"
560 msgstr "Пожалуйста, войдите заново чтобы получить доступ к этой странице."
561
562 #: flask_security/templates/security/email/change_notice.html:1
563 msgid "Your password has been changed."
564 msgstr "Ваш пароль был изменён."
565
566 #: flask_security/templates/security/email/change_notice.html:3
567 msgid "If you did not change your password,"
568 msgstr "Если вы не меняли свой пароль,"
569
570 #: flask_security/templates/security/email/change_notice.html:3
571 msgid "click here to reset it"
572 msgstr "нажмите сюда чтобы сбросить его"
573
574 #: flask_security/templates/security/email/confirmation_instructions.html:1
575 msgid "Please confirm your email through the link below:"
576 msgstr "Пожалуйста, подтвердите свой почтовый адрес перейдя по ссылке:"
577
578 #: flask_security/templates/security/email/confirmation_instructions.html:3
579 #: flask_security/templates/security/email/welcome.html:6
580 msgid "Confirm my account"
581 msgstr "Подтвердить аккаунт"
582
583 #: flask_security/templates/security/email/login_instructions.html:1
584 #: flask_security/templates/security/email/welcome.html:1
585 #, python-format
586 msgid "Welcome %(email)s!"
587 msgstr "Добро пожаловать, %(email)s!"
588
589 #: flask_security/templates/security/email/login_instructions.html:3
590 msgid "You can log into your account through the link below:"
591 msgstr "Вы можете войти по ссылке ниже:"
592
593 #: flask_security/templates/security/email/login_instructions.html:5
594 msgid "Login now"
595 msgstr "Войти"
596
597 #: flask_security/templates/security/email/reset_instructions.html:1
598 msgid "Click here to reset your password"
599 msgstr "Нажмите, чтобы сбросить свой пароль"
600
601 #: flask_security/templates/security/email/two_factor_instructions.html:3
602 msgid "You can log into your account using the following code:"
603 msgstr ""
604
605 #: flask_security/templates/security/email/two_factor_rescue.html:1
606 msgid "can not access mail account"
607 msgstr ""
608
609 #: flask_security/templates/security/email/us_instructions.html:3
610 #, fuzzy
611 msgid "You can sign into your account using the following code:"
612 msgstr "Вы можете войти по ссылке ниже:"
613
614 #: flask_security/templates/security/email/us_instructions.html:6
615 #, fuzzy
616 msgid "Or use the the link below:"
617 msgstr "Вы можете подтвердить свой почтовый адрес перейдя по ссылке:"
618
619 #: flask_security/templates/security/email/welcome.html:4
620 msgid "You can confirm your email through the link below:"
621 msgstr "Вы можете подтвердить свой почтовый адрес перейдя по ссылке:"
622
0 # Turkish translation for Flask-Security.
1 # Copyright (C) 2019 Ecmel B. Canlıer <[email protected]>
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 #
5 msgid ""
6 msgstr ""
7 "Project-Id-Version: Flask-Security 2.0.1\n"
8 "Report-Msgid-Bugs-To: [email protected]\n"
9 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
10 "PO-Revision-Date: 2018-12-20 18:48+0300\n"
11 "Last-Translator: Ecmel B. Canlıer <[email protected]>\n"
12 "Language: tr_TR\n"
13 "Language-Team: \n"
14 "Plural-Forms: nplurals=2; plural=(n != 1)\n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=utf-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Generated-By: Babel 2.8.0\n"
19
20 #: flask_security/core.py:207
21 msgid "Login Required"
22 msgstr "Giriş yapmanız gerekmektedir"
23
24 #: flask_security/core.py:208
25 #: flask_security/templates/security/email/two_factor_instructions.html:1
26 #: flask_security/templates/security/email/us_instructions.html:1
27 msgid "Welcome"
28 msgstr "Hoş Geldiniz"
29
30 #: flask_security/core.py:209
31 msgid "Please confirm your email"
32 msgstr "Lütfen e-posta adresinizi onaylayın"
33
34 #: flask_security/core.py:210
35 msgid "Login instructions"
36 msgstr "Giriş talimatları"
37
38 #: flask_security/core.py:211
39 #: flask_security/templates/security/email/reset_notice.html:1
40 msgid "Your password has been reset"
41 msgstr "Şifreniz yenilenmiştir"
42
43 #: flask_security/core.py:212
44 msgid "Your password has been changed"
45 msgstr "Şifreniz değiştirilmiştir"
46
47 #: flask_security/core.py:213
48 msgid "Password reset instructions"
49 msgstr "Şifre yenileme talimatları"
50
51 #: flask_security/core.py:216
52 msgid "Two-factor Login"
53 msgstr ""
54
55 #: flask_security/core.py:217
56 msgid "Two-factor Rescue"
57 msgstr ""
58
59 #: flask_security/core.py:266
60 msgid "Verification Code"
61 msgstr ""
62
63 #: flask_security/core.py:282
64 msgid "Input not appropriate for requested API"
65 msgstr ""
66
67 #: flask_security/core.py:283
68 msgid "You do not have permission to view this resource."
69 msgstr "Bu maddeyi görmeye yetkiniz yoktur."
70
71 #: flask_security/core.py:285
72 msgid "You are not authenticated. Please supply the correct credentials."
73 msgstr ""
74
75 #: flask_security/core.py:289
76 #, fuzzy
77 msgid "You must re-authenticate to access this endpoint"
78 msgstr "Bu sayfaya erişebilmek için lütfen tekrardan giriş yapın."
79
80 #: flask_security/core.py:293
81 #, python-format
82 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
83 msgstr "Teşekkür ederiz. Onaylama talimatları %(email)s adresine gönderilmiştir."
84
85 #: flask_security/core.py:296
86 msgid "Thank you. Your email has been confirmed."
87 msgstr "Teşekkür ederiz. E-posta adresiniz onaylanmıştır"
88
89 #: flask_security/core.py:297
90 msgid "Your email has already been confirmed."
91 msgstr "E-posta adresiniz zaten onaylanmış."
92
93 #: flask_security/core.py:298
94 msgid "Invalid confirmation token."
95 msgstr "Yanlış onaylama kodu."
96
97 #: flask_security/core.py:300
98 #, python-format
99 msgid "%(email)s is already associated with an account."
100 msgstr "%(email)s başka bir hesaba bağlı."
101
102 #: flask_security/core.py:303
103 msgid "Password does not match"
104 msgstr "Şifre yanlış"
105
106 #: flask_security/core.py:304
107 msgid "Passwords do not match"
108 msgstr "Şifreler uymuyor"
109
110 #: flask_security/core.py:305
111 msgid "Redirections outside the domain are forbidden"
112 msgstr "Adres dışına yönlendirmeler yasaktır"
113
114 #: flask_security/core.py:307
115 #, python-format
116 msgid "Instructions to reset your password have been sent to %(email)s."
117 msgstr "Şifrenizi yenileme talimatları %(email)s adresine gönderilmiştir."
118
119 #: flask_security/core.py:311
120 #, python-format
121 msgid ""
122 "You did not reset your password within %(within)s. New instructions have "
123 "been sent to %(email)s."
124 msgstr ""
125 "Şifrenizi %(within)s içinde yenilemediniz. Yeni talimatlar %(email)s "
126 "adresine gönderilmiştir."
127
128 #: flask_security/core.py:317
129 msgid "Invalid reset password token."
130 msgstr "Yanlış şifre yenileme kodu."
131
132 #: flask_security/core.py:318
133 msgid "Email requires confirmation."
134 msgstr "E-posta onayı gerekmektedir."
135
136 #: flask_security/core.py:320
137 #, python-format
138 msgid "Confirmation instructions have been sent to %(email)s."
139 msgstr "Onaylama talimatları %(email)s adresine gönderilmiştir."
140
141 #: flask_security/core.py:324
142 #, python-format
143 msgid ""
144 "You did not confirm your email within %(within)s. New instructions to "
145 "confirm your email have been sent to %(email)s."
146 msgstr ""
147 "E-posta adresinizi %(within)s içinde onaylamadınız. Yeni onaylama "
148 "talimatları %(email)s adresine gönderilmiştir."
149
150 #: flask_security/core.py:332
151 #, python-format
152 msgid ""
153 "You did not login within %(within)s. New instructions to login have been "
154 "sent to %(email)s."
155 msgstr ""
156 "%(within)s içinde giriş yapmadınız. Yeni giriş yapma talimatları "
157 "%(email)s adresine gönderilmiştir."
158
159 #: flask_security/core.py:339
160 #, python-format
161 msgid "Instructions to login have been sent to %(email)s."
162 msgstr "Giriş yapma talimatları %(email)s adresine gönderilmiştir."
163
164 #: flask_security/core.py:342
165 msgid "Invalid login token."
166 msgstr "Yanlış giriş kodu."
167
168 #: flask_security/core.py:343
169 msgid "Account is disabled."
170 msgstr "Hesap kapalıdır."
171
172 #: flask_security/core.py:344
173 msgid "Email not provided"
174 msgstr "E-posta verilmemiş"
175
176 #: flask_security/core.py:345
177 msgid "Invalid email address"
178 msgstr "Yanlış e-posta adresi"
179
180 #: flask_security/core.py:346
181 #, fuzzy
182 msgid "Invalid code"
183 msgstr "Şifre yanlış"
184
185 #: flask_security/core.py:347
186 msgid "Password not provided"
187 msgstr "Şifre verilmemiş"
188
189 #: flask_security/core.py:348
190 msgid "No password is set for this user"
191 msgstr "Bu kullanıcı için bir şifre yok"
192
193 #: flask_security/core.py:350
194 #, fuzzy, python-format
195 msgid "Password must be at least %(length)s characters"
196 msgstr "Şifreniz en az 6 karakter olmalıdır"
197
198 #: flask_security/core.py:353
199 msgid "Password not complex enough"
200 msgstr ""
201
202 #: flask_security/core.py:354
203 msgid "Password on breached list"
204 msgstr ""
205
206 #: flask_security/core.py:356
207 msgid "Failed to contact breached passwords site"
208 msgstr ""
209
210 #: flask_security/core.py:359
211 msgid "Phone number not valid e.g. missing country code"
212 msgstr ""
213
214 #: flask_security/core.py:360
215 msgid "Specified user does not exist"
216 msgstr "Böyle bir kullanıcı yok"
217
218 #: flask_security/core.py:361
219 msgid "Invalid password"
220 msgstr "Şifre yanlış"
221
222 #: flask_security/core.py:362
223 msgid "Password or code submitted is not valid"
224 msgstr ""
225
226 #: flask_security/core.py:363
227 msgid "You have successfully logged in."
228 msgstr "Başarıyla giriş yaptınız."
229
230 #: flask_security/core.py:364
231 msgid "Forgot password?"
232 msgstr "Şifrenizi mi unuttunuz?"
233
234 #: flask_security/core.py:366
235 msgid ""
236 "You successfully reset your password and you have been logged in "
237 "automatically."
238 msgstr "Şifreniz yenilenmiştir ve otomatik olarak giriş yapmış bulunmaktasınız."
239
240 #: flask_security/core.py:373
241 msgid "Your new password must be different than your previous password."
242 msgstr "Yeni şifreniz eski şifrenizden farklı olmalıdır."
243
244 #: flask_security/core.py:376
245 msgid "You successfully changed your password."
246 msgstr "Şifrenizi başarıyla değiştirdiniz."
247
248 #: flask_security/core.py:377
249 msgid "Please log in to access this page."
250 msgstr "Bu sayfaya erişebilmek için lütfen giriş yapın."
251
252 #: flask_security/core.py:378
253 msgid "Please reauthenticate to access this page."
254 msgstr "Bu sayfaya erişebilmek için lütfen tekrardan giriş yapın."
255
256 #: flask_security/core.py:379
257 msgid "Reauthentication successful"
258 msgstr ""
259
260 #: flask_security/core.py:381
261 msgid "You can only access this endpoint when not logged in."
262 msgstr ""
263
264 #: flask_security/core.py:384
265 msgid "Failed to send code. Please try again later"
266 msgstr ""
267
268 #: flask_security/core.py:385
269 msgid "Invalid Token"
270 msgstr ""
271
272 #: flask_security/core.py:386
273 msgid "Your token has been confirmed"
274 msgstr ""
275
276 #: flask_security/core.py:388
277 msgid "You successfully changed your two-factor method."
278 msgstr ""
279
280 #: flask_security/core.py:392
281 msgid "You successfully confirmed password"
282 msgstr ""
283
284 #: flask_security/core.py:396
285 msgid "Password confirmation is needed in order to access page"
286 msgstr ""
287
288 #: flask_security/core.py:400
289 msgid "You currently do not have permissions to access this page"
290 msgstr ""
291
292 #: flask_security/core.py:403
293 msgid "Marked method is not valid"
294 msgstr ""
295
296 #: flask_security/core.py:405
297 msgid "You successfully disabled two factor authorization."
298 msgstr ""
299
300 #: flask_security/core.py:408
301 msgid "Requested method is not valid"
302 msgstr ""
303
304 #: flask_security/core.py:410
305 #, python-format
306 msgid "Setup must be completed within %(within)s. Please start over."
307 msgstr ""
308
309 #: flask_security/core.py:413
310 msgid "Unified sign in setup successful"
311 msgstr ""
312
313 #: flask_security/core.py:414
314 msgid "You must specify a valid identity to sign in"
315 msgstr ""
316
317 #: flask_security/core.py:415
318 #, python-format
319 msgid "Use this code to sign in: %(code)s."
320 msgstr ""
321
322 #: flask_security/forms.py:50
323 msgid "Email Address"
324 msgstr "E-posta Adresi"
325
326 #: flask_security/forms.py:51
327 msgid "Password"
328 msgstr "Şifre"
329
330 #: flask_security/forms.py:52
331 msgid "Remember Me"
332 msgstr "Beni Hatırla"
333
334 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
335 #: flask_security/templates/security/login_user.html:6
336 #: flask_security/templates/security/send_login.html:6
337 msgid "Login"
338 msgstr "Giriş Yap"
339
340 #: flask_security/forms.py:54
341 #: flask_security/templates/security/email/us_instructions.html:8
342 #: flask_security/templates/security/us_signin.html:6
343 msgid "Sign In"
344 msgstr ""
345
346 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
347 #: flask_security/templates/security/register_user.html:6
348 msgid "Register"
349 msgstr "Kayıt Ol"
350
351 #: flask_security/forms.py:56
352 msgid "Resend Confirmation Instructions"
353 msgstr "Onaylama Talimatlarını Tekrar Gönder"
354
355 #: flask_security/forms.py:57
356 msgid "Recover Password"
357 msgstr "Şifre Kurtar"
358
359 #: flask_security/forms.py:58
360 msgid "Reset Password"
361 msgstr "Şifre Yenile"
362
363 #: flask_security/forms.py:59
364 msgid "Retype Password"
365 msgstr "Şifre Tekrarı"
366
367 #: flask_security/forms.py:60
368 msgid "New Password"
369 msgstr "Yeni Şifre"
370
371 #: flask_security/forms.py:61
372 msgid "Change Password"
373 msgstr "Şifre Değiştir"
374
375 #: flask_security/forms.py:62
376 msgid "Send Login Link"
377 msgstr "Giriş Linki Gönder"
378
379 #: flask_security/forms.py:63
380 msgid "Verify Password"
381 msgstr ""
382
383 #: flask_security/forms.py:64
384 msgid "Change Method"
385 msgstr ""
386
387 #: flask_security/forms.py:65
388 msgid "Phone Number"
389 msgstr ""
390
391 #: flask_security/forms.py:66
392 msgid "Authentication Code"
393 msgstr ""
394
395 #: flask_security/forms.py:67
396 msgid "Submit"
397 msgstr ""
398
399 #: flask_security/forms.py:68
400 msgid "Submit Code"
401 msgstr ""
402
403 #: flask_security/forms.py:69
404 msgid "Error(s)"
405 msgstr ""
406
407 #: flask_security/forms.py:70
408 msgid "Identity"
409 msgstr ""
410
411 #: flask_security/forms.py:71
412 msgid "Send Code"
413 msgstr ""
414
415 #: flask_security/forms.py:72
416 #, fuzzy
417 msgid "Passcode"
418 msgstr "Şifre"
419
420 #: flask_security/unified_signin.py:145
421 #, fuzzy
422 msgid "Code or Password"
423 msgstr "Şifre Kurtar"
424
425 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
426 msgid "Available Methods"
427 msgstr ""
428
429 #: flask_security/unified_signin.py:151
430 msgid "Via email"
431 msgstr ""
432
433 #: flask_security/unified_signin.py:151
434 msgid "Via SMS"
435 msgstr ""
436
437 #: flask_security/unified_signin.py:272
438 msgid "Set up using email"
439 msgstr ""
440
441 #: flask_security/unified_signin.py:275
442 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
443 msgstr ""
444
445 #: flask_security/unified_signin.py:277
446 msgid "Set up using SMS"
447 msgstr ""
448
449 #: flask_security/templates/security/_menu.html:2
450 msgid "Menu"
451 msgstr "Menü"
452
453 #: flask_security/templates/security/_menu.html:8
454 msgid "Unified Sign In"
455 msgstr ""
456
457 #: flask_security/templates/security/_menu.html:14
458 msgid "Forgot password"
459 msgstr "Şifremi unuttum"
460
461 #: flask_security/templates/security/_menu.html:17
462 msgid "Confirm account"
463 msgstr "Hesabı onayla"
464
465 #: flask_security/templates/security/change_password.html:6
466 msgid "Change password"
467 msgstr "Şifre değiştir"
468
469 #: flask_security/templates/security/forgot_password.html:6
470 msgid "Send password reset instructions"
471 msgstr "Şifre değiştirme talimatlarını gönder"
472
473 #: flask_security/templates/security/reset_password.html:6
474 msgid "Reset password"
475 msgstr "Şifre yenile"
476
477 #: flask_security/templates/security/send_confirmation.html:6
478 msgid "Resend confirmation instructions"
479 msgstr "Onaylama talimatlarını tekrar gönder"
480
481 #: flask_security/templates/security/two_factor_setup.html:6
482 msgid "Two-factor authentication adds an extra layer of security to your account"
483 msgstr ""
484
485 #: flask_security/templates/security/two_factor_setup.html:7
486 msgid ""
487 "In addition to your username and password, you'll need to use a code that"
488 " we will send you"
489 msgstr ""
490
491 #: flask_security/templates/security/two_factor_setup.html:18
492 msgid "To complete logging in, please enter the code sent to your mail"
493 msgstr ""
494
495 #: flask_security/templates/security/two_factor_setup.html:21
496 #: flask_security/templates/security/us_setup.html:21
497 msgid ""
498 "Open your authenticator app on your device and scan the following qrcode "
499 "to start receiving codes:"
500 msgstr ""
501
502 #: flask_security/templates/security/two_factor_setup.html:22
503 msgid "Two factor authentication code"
504 msgstr ""
505
506 #: flask_security/templates/security/two_factor_setup.html:25
507 msgid "To Which Phone Number Should We Send Code To?"
508 msgstr ""
509
510 #: flask_security/templates/security/two_factor_verify_code.html:6
511 msgid "Two-factor Authentication"
512 msgstr ""
513
514 #: flask_security/templates/security/two_factor_verify_code.html:7
515 msgid "Please enter your authentication code"
516 msgstr ""
517
518 #: flask_security/templates/security/two_factor_verify_code.html:18
519 msgid "The code for authentication was sent to your email address"
520 msgstr ""
521
522 #: flask_security/templates/security/two_factor_verify_code.html:21
523 msgid "A mail was sent to us in order to reset your application account"
524 msgstr ""
525
526 #: flask_security/templates/security/two_factor_verify_password.html:6
527 #: flask_security/templates/security/verify.html:6
528 msgid "Please Enter Your Password"
529 msgstr ""
530
531 #: flask_security/templates/security/us_setup.html:6
532 msgid "Setup Unified Sign In options"
533 msgstr ""
534
535 #: flask_security/templates/security/us_setup.html:22
536 msgid "Passwordless QRCode"
537 msgstr ""
538
539 #: flask_security/templates/security/us_setup.html:25
540 #: flask_security/templates/security/us_signin.html:23
541 #: flask_security/templates/security/us_verify.html:21
542 #, fuzzy
543 msgid "Code has been sent"
544 msgstr "Şifreniz yenilenmiştir"
545
546 #: flask_security/templates/security/us_setup.html:29
547 msgid "No methods have been enabled - nothing to setup"
548 msgstr ""
549
550 #: flask_security/templates/security/us_signin.html:15
551 #: flask_security/templates/security/us_verify.html:13
552 msgid "Request one-time code be sent"
553 msgstr ""
554
555 #: flask_security/templates/security/us_verify.html:6
556 #, fuzzy
557 msgid "Please re-authenticate"
558 msgstr "Bu sayfaya erişebilmek için lütfen tekrardan giriş yapın."
559
560 #: flask_security/templates/security/email/change_notice.html:1
561 msgid "Your password has been changed."
562 msgstr "Şifreniz değiştirilmiştir."
563
564 #: flask_security/templates/security/email/change_notice.html:3
565 msgid "If you did not change your password,"
566 msgstr "Eğer siz değiştirmediyseniz,"
567
568 #: flask_security/templates/security/email/change_notice.html:3
569 msgid "click here to reset it"
570 msgstr "buraya tıklayarak yenileyiniz"
571
572 #: flask_security/templates/security/email/confirmation_instructions.html:1
573 msgid "Please confirm your email through the link below:"
574 msgstr "Lütfen e-posta adresinizi aşağıdaki linkten onaylayınız:"
575
576 #: flask_security/templates/security/email/confirmation_instructions.html:3
577 #: flask_security/templates/security/email/welcome.html:6
578 msgid "Confirm my account"
579 msgstr "Hesabımı onayla"
580
581 #: flask_security/templates/security/email/login_instructions.html:1
582 #: flask_security/templates/security/email/welcome.html:1
583 #, python-format
584 msgid "Welcome %(email)s!"
585 msgstr "Hoş Geldin %(email)s"
586
587 #: flask_security/templates/security/email/login_instructions.html:3
588 msgid "You can log into your account through the link below:"
589 msgstr "Hesabına aşağıdaki linkten giriş yapabilirsin:"
590
591 #: flask_security/templates/security/email/login_instructions.html:5
592 msgid "Login now"
593 msgstr "Şimdi giriş yap"
594
595 #: flask_security/templates/security/email/reset_instructions.html:1
596 msgid "Click here to reset your password"
597 msgstr "Şifreni yenilemek için buraya tıkla"
598
599 #: flask_security/templates/security/email/two_factor_instructions.html:3
600 msgid "You can log into your account using the following code:"
601 msgstr ""
602
603 #: flask_security/templates/security/email/two_factor_rescue.html:1
604 msgid "can not access mail account"
605 msgstr ""
606
607 #: flask_security/templates/security/email/us_instructions.html:3
608 #, fuzzy
609 msgid "You can sign into your account using the following code:"
610 msgstr "Hesabına aşağıdaki linkten giriş yapabilirsin:"
611
612 #: flask_security/templates/security/email/us_instructions.html:6
613 #, fuzzy
614 msgid "Or use the the link below:"
615 msgstr "E-posta adresinizi aşağıdaki linkten onaylayabilirsiniz:"
616
617 #: flask_security/templates/security/email/welcome.html:4
618 msgid "You can confirm your email through the link below:"
619 msgstr "E-posta adresinizi aşağıdaki linkten onaylayabilirsiniz:"
620
0 # Chinese (Simplified, China) translations for Flask-Security.
1 # Copyright (C) 2017 CERN
2 # This file is distributed under the same license as the Flask-Security
3 # project.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
5 #
6 msgid ""
7 msgstr ""
8 "Project-Id-Version: Flask-Security 2.0.1\n"
9 "Report-Msgid-Bugs-To: [email protected]\n"
10 "POT-Creation-Date: 2020-04-19 13:18-0700\n"
11 "PO-Revision-Date: 2018-08-02 19:55+0800\n"
12 "Last-Translator: SteinKuo <[email protected]>\n"
13 "Language: zh_CN\n"
14 "Language-Team: Chinese Simplified <[email protected]>\n"
15 "Plural-Forms: nplurals=1; plural=0\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=utf-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Generated-By: Babel 2.8.0\n"
20
21 #: flask_security/core.py:207
22 msgid "Login Required"
23 msgstr "需要登录"
24
25 #: flask_security/core.py:208
26 #: flask_security/templates/security/email/two_factor_instructions.html:1
27 #: flask_security/templates/security/email/us_instructions.html:1
28 msgid "Welcome"
29 msgstr "欢迎"
30
31 #: flask_security/core.py:209
32 msgid "Please confirm your email"
33 msgstr "请激活你的电子邮箱"
34
35 #: flask_security/core.py:210
36 msgid "Login instructions"
37 msgstr "登录邮件"
38
39 #: flask_security/core.py:211
40 #: flask_security/templates/security/email/reset_notice.html:1
41 msgid "Your password has been reset"
42 msgstr "你的密码已重置"
43
44 #: flask_security/core.py:212
45 msgid "Your password has been changed"
46 msgstr "你的密码已更改"
47
48 #: flask_security/core.py:213
49 msgid "Password reset instructions"
50 msgstr "密码重置"
51
52 #: flask_security/core.py:216
53 msgid "Two-factor Login"
54 msgstr ""
55
56 #: flask_security/core.py:217
57 msgid "Two-factor Rescue"
58 msgstr ""
59
60 #: flask_security/core.py:266
61 msgid "Verification Code"
62 msgstr ""
63
64 #: flask_security/core.py:282
65 msgid "Input not appropriate for requested API"
66 msgstr ""
67
68 #: flask_security/core.py:283
69 msgid "You do not have permission to view this resource."
70 msgstr "你无权查看此资源!"
71
72 #: flask_security/core.py:285
73 msgid "You are not authenticated. Please supply the correct credentials."
74 msgstr ""
75
76 #: flask_security/core.py:289
77 #, fuzzy
78 msgid "You must re-authenticate to access this endpoint"
79 msgstr "请重新进行身份验证,以访问此页面。"
80
81 #: flask_security/core.py:293
82 #, python-format
83 msgid "Thank you. Confirmation instructions have been sent to %(email)s."
84 msgstr "谢谢你。已发送激活邮件到 %(email)s。"
85
86 #: flask_security/core.py:296
87 msgid "Thank you. Your email has been confirmed."
88 msgstr "谢谢。你的邮箱已激活!"
89
90 #: flask_security/core.py:297
91 msgid "Your email has already been confirmed."
92 msgstr "你的邮箱已激活!"
93
94 #: flask_security/core.py:298
95 msgid "Invalid confirmation token."
96 msgstr "无效验证码!"
97
98 #: flask_security/core.py:300
99 #, python-format
100 msgid "%(email)s is already associated with an account."
101 msgstr "%(email)s 已关联账户。"
102
103 #: flask_security/core.py:303
104 msgid "Password does not match"
105 msgstr "密码不匹配"
106
107 #: flask_security/core.py:304
108 msgid "Passwords do not match"
109 msgstr "密码不匹配"
110
111 #: flask_security/core.py:305
112 msgid "Redirections outside the domain are forbidden"
113 msgstr "禁止域名外重定向"
114
115 #: flask_security/core.py:307
116 #, python-format
117 msgid "Instructions to reset your password have been sent to %(email)s."
118 msgstr "重置密码邮件已发送到 %(email)s。"
119
120 #: flask_security/core.py:311
121 #, python-format
122 msgid ""
123 "You did not reset your password within %(within)s. New instructions have "
124 "been sent to %(email)s."
125 msgstr "你未在 %(within)s 重置密码。新重置密码邮件已发送到 %(email)s。"
126
127 #: flask_security/core.py:317
128 msgid "Invalid reset password token."
129 msgstr "密码重置验证码无效!"
130
131 #: flask_security/core.py:318
132 msgid "Email requires confirmation."
133 msgstr "请先激活邮箱。"
134
135 #: flask_security/core.py:320
136 #, python-format
137 msgid "Confirmation instructions have been sent to %(email)s."
138 msgstr "激活邮件已发送到 %(email)s。"
139
140 #: flask_security/core.py:324
141 #, python-format
142 msgid ""
143 "You did not confirm your email within %(within)s. New instructions to "
144 "confirm your email have been sent to %(email)s."
145 msgstr "你未在 %(within)s 激活邮箱。新激活邮件已发送到 %(email)s。"
146
147 #: flask_security/core.py:332
148 #, python-format
149 msgid ""
150 "You did not login within %(within)s. New instructions to login have been "
151 "sent to %(email)s."
152 msgstr "你未在 %(within)s 登录账户。新登录邮件已发送到 %(email)s。"
153
154 #: flask_security/core.py:339
155 #, python-format
156 msgid "Instructions to login have been sent to %(email)s."
157 msgstr "登录邮件已发送到 %(email)s。"
158
159 #: flask_security/core.py:342
160 msgid "Invalid login token."
161 msgstr "无效登录验证码!"
162
163 #: flask_security/core.py:343
164 msgid "Account is disabled."
165 msgstr "账户已被禁用!"
166
167 #: flask_security/core.py:344
168 msgid "Email not provided"
169 msgstr "未填写电子邮箱"
170
171 #: flask_security/core.py:345
172 msgid "Invalid email address"
173 msgstr "无效邮箱地址"
174
175 #: flask_security/core.py:346
176 #, fuzzy
177 msgid "Invalid code"
178 msgstr "密码不正确"
179
180 #: flask_security/core.py:347
181 msgid "Password not provided"
182 msgstr "未填写密码"
183
184 #: flask_security/core.py:348
185 msgid "No password is set for this user"
186 msgstr "此账户未设置密码"
187
188 #: flask_security/core.py:350
189 #, fuzzy, python-format
190 msgid "Password must be at least %(length)s characters"
191 msgstr "密码至少6个字符"
192
193 #: flask_security/core.py:353
194 msgid "Password not complex enough"
195 msgstr ""
196
197 #: flask_security/core.py:354
198 msgid "Password on breached list"
199 msgstr ""
200
201 #: flask_security/core.py:356
202 msgid "Failed to contact breached passwords site"
203 msgstr ""
204
205 #: flask_security/core.py:359
206 msgid "Phone number not valid e.g. missing country code"
207 msgstr ""
208
209 #: flask_security/core.py:360
210 msgid "Specified user does not exist"
211 msgstr "此用户不存在"
212
213 #: flask_security/core.py:361
214 msgid "Invalid password"
215 msgstr "密码不正确"
216
217 #: flask_security/core.py:362
218 msgid "Password or code submitted is not valid"
219 msgstr ""
220
221 #: flask_security/core.py:363
222 msgid "You have successfully logged in."
223 msgstr "你已成功登录!"
224
225 #: flask_security/core.py:364
226 msgid "Forgot password?"
227 msgstr "忘记密码?"
228
229 #: flask_security/core.py:366
230 msgid ""
231 "You successfully reset your password and you have been logged in "
232 "automatically."
233 msgstr "你的密码已成功重置,并已自动登录。"
234
235 #: flask_security/core.py:373
236 msgid "Your new password must be different than your previous password."
237 msgstr "你的新密码不能与当前密码相同。"
238
239 #: flask_security/core.py:376
240 msgid "You successfully changed your password."
241 msgstr "你已成功更改密码!"
242
243 #: flask_security/core.py:377
244 msgid "Please log in to access this page."
245 msgstr "请登录访问此页面。"
246
247 #: flask_security/core.py:378
248 msgid "Please reauthenticate to access this page."
249 msgstr "请重新进行身份验证,以访问此页面。"
250
251 #: flask_security/core.py:379
252 msgid "Reauthentication successful"
253 msgstr ""
254
255 #: flask_security/core.py:381
256 msgid "You can only access this endpoint when not logged in."
257 msgstr ""
258
259 #: flask_security/core.py:384
260 msgid "Failed to send code. Please try again later"
261 msgstr ""
262
263 #: flask_security/core.py:385
264 msgid "Invalid Token"
265 msgstr ""
266
267 #: flask_security/core.py:386
268 msgid "Your token has been confirmed"
269 msgstr ""
270
271 #: flask_security/core.py:388
272 msgid "You successfully changed your two-factor method."
273 msgstr ""
274
275 #: flask_security/core.py:392
276 msgid "You successfully confirmed password"
277 msgstr ""
278
279 #: flask_security/core.py:396
280 msgid "Password confirmation is needed in order to access page"
281 msgstr ""
282
283 #: flask_security/core.py:400
284 msgid "You currently do not have permissions to access this page"
285 msgstr ""
286
287 #: flask_security/core.py:403
288 msgid "Marked method is not valid"
289 msgstr ""
290
291 #: flask_security/core.py:405
292 msgid "You successfully disabled two factor authorization."
293 msgstr ""
294
295 #: flask_security/core.py:408
296 msgid "Requested method is not valid"
297 msgstr ""
298
299 #: flask_security/core.py:410
300 #, python-format
301 msgid "Setup must be completed within %(within)s. Please start over."
302 msgstr ""
303
304 #: flask_security/core.py:413
305 msgid "Unified sign in setup successful"
306 msgstr ""
307
308 #: flask_security/core.py:414
309 msgid "You must specify a valid identity to sign in"
310 msgstr ""
311
312 #: flask_security/core.py:415
313 #, python-format
314 msgid "Use this code to sign in: %(code)s."
315 msgstr ""
316
317 #: flask_security/forms.py:50
318 msgid "Email Address"
319 msgstr "邮箱地址"
320
321 #: flask_security/forms.py:51
322 msgid "Password"
323 msgstr "密码"
324
325 #: flask_security/forms.py:52
326 msgid "Remember Me"
327 msgstr "记住我"
328
329 #: flask_security/forms.py:53 flask_security/templates/security/_menu.html:5
330 #: flask_security/templates/security/login_user.html:6
331 #: flask_security/templates/security/send_login.html:6
332 msgid "Login"
333 msgstr "登录"
334
335 #: flask_security/forms.py:54
336 #: flask_security/templates/security/email/us_instructions.html:8
337 #: flask_security/templates/security/us_signin.html:6
338 msgid "Sign In"
339 msgstr ""
340
341 #: flask_security/forms.py:55 flask_security/templates/security/_menu.html:11
342 #: flask_security/templates/security/register_user.html:6
343 msgid "Register"
344 msgstr "注册"
345
346 #: flask_security/forms.py:56
347 msgid "Resend Confirmation Instructions"
348 msgstr "重新发送邮件验证"
349
350 #: flask_security/forms.py:57
351 msgid "Recover Password"
352 msgstr "恢复密码"
353
354 #: flask_security/forms.py:58
355 msgid "Reset Password"
356 msgstr "重置密码"
357
358 #: flask_security/forms.py:59
359 msgid "Retype Password"
360 msgstr "再次确认密码"
361
362 #: flask_security/forms.py:60
363 msgid "New Password"
364 msgstr "新密码"
365
366 #: flask_security/forms.py:61
367 msgid "Change Password"
368 msgstr "更改密码"
369
370 #: flask_security/forms.py:62
371 msgid "Send Login Link"
372 msgstr "发送登录链接"
373
374 #: flask_security/forms.py:63
375 msgid "Verify Password"
376 msgstr ""
377
378 #: flask_security/forms.py:64
379 msgid "Change Method"
380 msgstr ""
381
382 #: flask_security/forms.py:65
383 msgid "Phone Number"
384 msgstr ""
385
386 #: flask_security/forms.py:66
387 msgid "Authentication Code"
388 msgstr ""
389
390 #: flask_security/forms.py:67
391 msgid "Submit"
392 msgstr ""
393
394 #: flask_security/forms.py:68
395 msgid "Submit Code"
396 msgstr ""
397
398 #: flask_security/forms.py:69
399 msgid "Error(s)"
400 msgstr ""
401
402 #: flask_security/forms.py:70
403 msgid "Identity"
404 msgstr ""
405
406 #: flask_security/forms.py:71
407 msgid "Send Code"
408 msgstr ""
409
410 #: flask_security/forms.py:72
411 #, fuzzy
412 msgid "Passcode"
413 msgstr "密码"
414
415 #: flask_security/unified_signin.py:145
416 #, fuzzy
417 msgid "Code or Password"
418 msgstr "恢复密码"
419
420 #: flask_security/unified_signin.py:150 flask_security/unified_signin.py:270
421 msgid "Available Methods"
422 msgstr ""
423
424 #: flask_security/unified_signin.py:151
425 msgid "Via email"
426 msgstr ""
427
428 #: flask_security/unified_signin.py:151
429 msgid "Via SMS"
430 msgstr ""
431
432 #: flask_security/unified_signin.py:272
433 msgid "Set up using email"
434 msgstr ""
435
436 #: flask_security/unified_signin.py:275
437 msgid "Set up using an authenticator app (e.g. google, lastpass, authy)"
438 msgstr ""
439
440 #: flask_security/unified_signin.py:277
441 msgid "Set up using SMS"
442 msgstr ""
443
444 #: flask_security/templates/security/_menu.html:2
445 msgid "Menu"
446 msgstr "菜单"
447
448 #: flask_security/templates/security/_menu.html:8
449 msgid "Unified Sign In"
450 msgstr ""
451
452 #: flask_security/templates/security/_menu.html:14
453 msgid "Forgot password"
454 msgstr "忘记密码"
455
456 #: flask_security/templates/security/_menu.html:17
457 msgid "Confirm account"
458 msgstr "激活账户"
459
460 #: flask_security/templates/security/change_password.html:6
461 msgid "Change password"
462 msgstr "更改密码"
463
464 #: flask_security/templates/security/forgot_password.html:6
465 msgid "Send password reset instructions"
466 msgstr "发送密码重置邮件"
467
468 #: flask_security/templates/security/reset_password.html:6
469 msgid "Reset password"
470 msgstr "重置密码"
471
472 #: flask_security/templates/security/send_confirmation.html:6
473 msgid "Resend confirmation instructions"
474 msgstr "重新发送激活邮件"
475
476 #: flask_security/templates/security/two_factor_setup.html:6
477 msgid "Two-factor authentication adds an extra layer of security to your account"
478 msgstr ""
479
480 #: flask_security/templates/security/two_factor_setup.html:7
481 msgid ""
482 "In addition to your username and password, you'll need to use a code that"
483 " we will send you"
484 msgstr ""
485
486 #: flask_security/templates/security/two_factor_setup.html:18
487 msgid "To complete logging in, please enter the code sent to your mail"
488 msgstr ""
489
490 #: flask_security/templates/security/two_factor_setup.html:21
491 #: flask_security/templates/security/us_setup.html:21
492 msgid ""
493 "Open your authenticator app on your device and scan the following qrcode "
494 "to start receiving codes:"
495 msgstr ""
496
497 #: flask_security/templates/security/two_factor_setup.html:22
498 msgid "Two factor authentication code"
499 msgstr ""
500
501 #: flask_security/templates/security/two_factor_setup.html:25
502 msgid "To Which Phone Number Should We Send Code To?"
503 msgstr ""
504
505 #: flask_security/templates/security/two_factor_verify_code.html:6
506 msgid "Two-factor Authentication"
507 msgstr ""
508
509 #: flask_security/templates/security/two_factor_verify_code.html:7
510 msgid "Please enter your authentication code"
511 msgstr ""
512
513 #: flask_security/templates/security/two_factor_verify_code.html:18
514 msgid "The code for authentication was sent to your email address"
515 msgstr ""
516
517 #: flask_security/templates/security/two_factor_verify_code.html:21
518 msgid "A mail was sent to us in order to reset your application account"
519 msgstr ""
520
521 #: flask_security/templates/security/two_factor_verify_password.html:6
522 #: flask_security/templates/security/verify.html:6
523 msgid "Please Enter Your Password"
524 msgstr ""
525
526 #: flask_security/templates/security/us_setup.html:6
527 msgid "Setup Unified Sign In options"
528 msgstr ""
529
530 #: flask_security/templates/security/us_setup.html:22
531 msgid "Passwordless QRCode"
532 msgstr ""
533
534 #: flask_security/templates/security/us_setup.html:25
535 #: flask_security/templates/security/us_signin.html:23
536 #: flask_security/templates/security/us_verify.html:21
537 #, fuzzy
538 msgid "Code has been sent"
539 msgstr "你的密码已重置"
540
541 #: flask_security/templates/security/us_setup.html:29
542 msgid "No methods have been enabled - nothing to setup"
543 msgstr ""
544
545 #: flask_security/templates/security/us_signin.html:15
546 #: flask_security/templates/security/us_verify.html:13
547 msgid "Request one-time code be sent"
548 msgstr ""
549
550 #: flask_security/templates/security/us_verify.html:6
551 #, fuzzy
552 msgid "Please re-authenticate"
553 msgstr "请重新进行身份验证,以访问此页面。"
554
555 #: flask_security/templates/security/email/change_notice.html:1
556 msgid "Your password has been changed."
557 msgstr "你的密码已更改。"
558
559 #: flask_security/templates/security/email/change_notice.html:3
560 msgid "If you did not change your password,"
561 msgstr "如果你没更改你的密码,"
562
563 #: flask_security/templates/security/email/change_notice.html:3
564 msgid "click here to reset it"
565 msgstr "点击这里重置密码"
566
567 #: flask_security/templates/security/email/confirmation_instructions.html:1
568 msgid "Please confirm your email through the link below:"
569 msgstr "请通过下面链接激活的你的邮箱:"
570
571 #: flask_security/templates/security/email/confirmation_instructions.html:3
572 #: flask_security/templates/security/email/welcome.html:6
573 msgid "Confirm my account"
574 msgstr "激活账户"
575
576 #: flask_security/templates/security/email/login_instructions.html:1
577 #: flask_security/templates/security/email/welcome.html:1
578 #, python-format
579 msgid "Welcome %(email)s!"
580 msgstr "欢迎你,%(email)s!"
581
582 #: flask_security/templates/security/email/login_instructions.html:3
583 msgid "You can log into your account through the link below:"
584 msgstr "你可以通过下面链接登录的你的账户:"
585
586 #: flask_security/templates/security/email/login_instructions.html:5
587 msgid "Login now"
588 msgstr "立刻登录"
589
590 #: flask_security/templates/security/email/reset_instructions.html:1
591 msgid "Click here to reset your password"
592 msgstr "点击这里重置密码"
593
594 #: flask_security/templates/security/email/two_factor_instructions.html:3
595 msgid "You can log into your account using the following code:"
596 msgstr ""
597
598 #: flask_security/templates/security/email/two_factor_rescue.html:1
599 msgid "can not access mail account"
600 msgstr ""
601
602 #: flask_security/templates/security/email/us_instructions.html:3
603 #, fuzzy
604 msgid "You can sign into your account using the following code:"
605 msgstr "你可以通过下面链接登录的你的账户:"
606
607 #: flask_security/templates/security/email/us_instructions.html:6
608 #, fuzzy
609 msgid "Or use the the link below:"
610 msgstr "你可以通过下面链接激活你的邮箱:"
611
612 #: flask_security/templates/security/email/welcome.html:4
613 msgid "You can confirm your email through the link below:"
614 msgstr "你可以通过下面链接激活你的邮箱:"
615
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.two_factor
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security two_factor module
6
7 :copyright: (c) 2016 by Gal Stainfeld, at Emedgene
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 """
10
11 from flask import current_app as app, redirect, request, session
12 from werkzeug.datastructures import MultiDict
13 from werkzeug.local import LocalProxy
14
15 from .utils import (
16 SmsSenderFactory,
17 base_render_json,
18 config_value,
19 do_flash,
20 login_user,
21 json_error_response,
22 url_for_security,
23 )
24 from .signals import (
25 tf_code_confirmed,
26 tf_disabled,
27 tf_security_token_sent,
28 tf_profile_changed,
29 )
30
31 # Convenient references
32 _security = LocalProxy(lambda: app.extensions["security"])
33 _datastore = LocalProxy(lambda: _security.datastore)
34
35
36 def tf_clean_session():
37 """
38 Clean out ALL stuff stored in session (e.g. on logout)
39 """
40 if config_value("TWO_FACTOR"):
41 for k in [
42 "tf_state",
43 "tf_user_id",
44 "tf_primary_method",
45 "tf_confirmed",
46 "tf_remember_login",
47 "tf_totp_secret",
48 ]:
49 session.pop(k, None)
50
51
52 def tf_send_security_token(user, method, totp_secret, phone_number):
53 """Sends the security token via email/sms for the specified user.
54
55 :param user: The user to send the code to
56 :param method: The method in which the code will be sent
57 ('email' or 'sms', or 'authenticator') at the moment
58 :param totp_secret: a unique shared secret of the user
59 :param phone_number: If 'sms' phone number to send to
60
61 There is no return value - it is assumed that exceptions are thrown by underlying
62 methods that callers can catch.
63
64 Flask-Security code should NOT call this directly -
65 call :meth:`.UserMixin.tf_send_security_token`
66 """
67 token_to_be_sent = _security._totp_factory.generate_totp_password(totp_secret)
68 if method == "email" or method == "mail":
69 _security._send_mail(
70 config_value("EMAIL_SUBJECT_TWO_FACTOR"),
71 user.email,
72 "two_factor_instructions",
73 user=user,
74 token=token_to_be_sent,
75 username=user.calc_username(),
76 )
77 elif method == "sms":
78 msg = "Use this code to log in: %s" % token_to_be_sent
79 from_number = config_value("SMS_SERVICE_CONFIG")["PHONE_NUMBER"]
80 to_number = phone_number
81 sms_sender = SmsSenderFactory.createSender(config_value("SMS_SERVICE"))
82 sms_sender.send_sms(from_number=from_number, to_number=to_number, msg=msg)
83
84 elif method == "google_authenticator" or method == "authenticator":
85 # password are generated automatically in the authenticator apps
86 pass
87 tf_security_token_sent.send(
88 app._get_current_object(),
89 user=user,
90 method=method,
91 token=token_to_be_sent,
92 phone_number=phone_number,
93 )
94
95
96 def complete_two_factor_process(
97 user, primary_method, totp_secret, is_changing, remember_login=None
98 ):
99 """clean session according to process (login or changing two-factor method)
100 and perform action accordingly
101 """
102
103 _datastore.tf_set(user, primary_method, totp_secret=totp_secret)
104
105 # if we are changing two-factor method
106 if is_changing:
107 completion_message = "TWO_FACTOR_CHANGE_METHOD_SUCCESSFUL"
108 tf_profile_changed.send(
109 app._get_current_object(), user=user, method=primary_method
110 )
111 # if we are logging in for the first time
112 else:
113 completion_message = "TWO_FACTOR_LOGIN_SUCCESSFUL"
114 tf_code_confirmed.send(
115 app._get_current_object(), user=user, method=primary_method
116 )
117 login_user(user, remember=remember_login)
118 tf_clean_session()
119 return completion_message
120
121
122 def tf_disable(user):
123 """ Disable two factor for user """
124 tf_clean_session()
125 _datastore.tf_reset(user)
126 tf_disabled.send(app._get_current_object(), user=user)
127
128
129 def is_tf_setup(user):
130 """ Return True is user account is setup for 2FA. """
131 return user.tf_totp_secret and user.tf_primary_method
132
133
134 def tf_login(user, remember=None, primary_authn_via=None):
135 """ Helper for two-factor authentication login
136
137 This is called only when login/password have already been validated.
138 This can be from login, register, confirm, unified sign in, unified magic link.
139
140 The result of this is either sending a 2FA token OR starting setup for new user.
141 In either case we do NOT log in user, so we must store some info in session to
142 track our state (including what user).
143 """
144
145 # on initial login clear any possible state out - this can happen if on same
146 # machine log in more than once since for 2FA you are not authenticated
147 # until complete 2FA.
148 tf_clean_session()
149
150 session["tf_user_id"] = user.id
151 if "remember":
152 session["tf_remember_login"] = remember
153
154 # Set info into form for JSON response
155 json_response = {"tf_required": True}
156 # if user's two-factor properties are not configured
157 if user.tf_primary_method is None or user.tf_totp_secret is None:
158 session["tf_state"] = "setup_from_login"
159 json_response["tf_state"] = "setup_from_login"
160 if not _security._want_json(request):
161 return redirect(url_for_security("two_factor_setup"))
162
163 # if user's two-factor properties are configured
164 else:
165 session["tf_state"] = "ready"
166 json_response["tf_state"] = "ready"
167 json_response["tf_primary_method"] = user.tf_primary_method
168
169 msg = user.tf_send_security_token(
170 method=user.tf_primary_method,
171 totp_secret=user.tf_totp_secret,
172 phone_number=user.tf_phone_number,
173 )
174 if msg:
175 # send code didn't work
176 if not _security._want_json(request):
177 # This is a mess - we are deep down in the login/unified sign in flow.
178 do_flash(msg, "error")
179 return redirect(url_for_security("login"))
180 else:
181 payload = json_error_response(errors=msg)
182 return _security._render_json(payload, 500, None, None)
183
184 if not _security._want_json(request):
185 return redirect(url_for_security("two_factor_token_validation"))
186
187 # JSON response - Fake up a form - doesn't really matter which.
188 form = _security.login_form(MultiDict([]))
189 form.user = user
190
191 return base_render_json(form, include_user=False, additional=json_response)
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.unified_signin
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security Unified Signin module
6
7 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
8 :license: MIT, see LICENSE for more details.
9
10 This implements a unified sign in endpoint - allowing
11 authentication via identity and passcode - where identity is configured
12 via SECURITY_USER_IDENTITY_ATTRIBUTES, and allowable passcodes are either a
13 password or one of US_ENABLED_METHODS.
14
15 Finish up:
16 - we should be able to add a phone number as part of setup even w/o any METHODS -
17 i.e. to allow login with any identity (phone) and a password.
18 - add username as last IDENTITY_MAPPING and allow anything...?? or just in example?
19
20 Consider/Questions:
21 - Allow registering/confirming with just a phone number - this likely would require
22 a new register/confirm endpoint in order to implement verification.
23 - Right now ChangePassword won't work - it requires an existing password - so
24 if the user doesn't have one - can't change it. However ForgotPassword will in
25 fact allow the user to add a password. Is that sufficient?
26 - Any reason to support 'next' in form? xx?next=yyy works fine.
27 - separate code validation times for SMS, email, authenticator?
28 - token versus code versus passcode? Confusing terminology.
29
30 """
31
32 import sys
33 import time
34
35 from flask import current_app as app
36 from flask import abort, after_this_request, redirect, request, session
37 from flask_login import current_user
38 from werkzeug.datastructures import MultiDict
39 from werkzeug.local import LocalProxy
40 from wtforms import BooleanField, RadioField, StringField, SubmitField, validators
41
42 from .confirmable import requires_confirmation
43 from .decorators import anonymous_user_required, auth_required, unauth_csrf
44 from .forms import Form, Required, get_form_field_label
45 from .quart_compat import get_quart_status
46 from .signals import us_profile_changed, us_security_token_sent
47 from .twofactor import is_tf_setup, tf_login
48 from .utils import (
49 _,
50 SmsSenderFactory,
51 base_render_json,
52 check_and_get_token_status,
53 config_value,
54 do_flash,
55 get_post_login_redirect,
56 get_post_verify_redirect,
57 get_message,
58 get_url,
59 get_within_delta,
60 json_error_response,
61 login_user,
62 propagate_next,
63 suppress_form_csrf,
64 url_for_security,
65 )
66
67 # Convenient references
68 _security = LocalProxy(lambda: app.extensions["security"])
69 _datastore = LocalProxy(lambda: _security.datastore)
70
71
72 PY3 = sys.version_info[0] == 3
73 if PY3 and get_quart_status(): # pragma: no cover
74 from .async_compat import _commit # noqa: F401
75 else:
76
77 def _commit(response=None):
78 _datastore.commit()
79 return response
80
81
82 def _compute_code_methods():
83 # Return list of methods that actually send codes
84 return list(set(config_value("US_ENABLED_METHODS")) - {"password", "authenticator"})
85
86
87 def _compute_setup_methods():
88 # Return list of methods that require setup
89 return list(set(config_value("US_ENABLED_METHODS")) - {"password"})
90
91
92 def _compute_active_methods(user):
93 # Compute methods already setup. The only oddity is that 'email'
94 # can be 'auto-setup' - so include that.
95 active_methods = set(config_value("US_ENABLED_METHODS")) & set(
96 _datastore.us_get_totp_secrets(user).keys()
97 )
98 if "email" in config_value("US_ENABLED_METHODS"):
99 active_methods |= {"email"}
100 return list(active_methods)
101
102
103 def _us_common_validate(form):
104 # Be aware - this has side effect on the form - it will fill in
105 # the form.user
106
107 # Validate identity - we go in order to figure out which user attribute the
108 # request gave us. Note that we give up on the first 'match' even if that
109 # doesn't yield a user. Why?
110 for mapping in config_value("USER_IDENTITY_MAPPINGS"):
111 # What we want is an ordered dict - but those don't exist for py27 -
112 # so there is really just one element here.
113 for ua, mapper in mapping.items():
114 # Make sure we don't validate on a column that application
115 # hasn't specifically configured as a unique/identity column
116 # In other words - might have a phone number for 2FA or unified
117 # but don't want the user to be able to use that as primary identity
118 if ua in config_value("USER_IDENTITY_ATTRIBUTES"):
119 # Allow mapper to alter (coerce) to type DB requires
120 idata = mapper(form.identity.data)
121 if idata is not None:
122 form.user = _datastore.find_user(**{ua: idata})
123 if not form.user:
124 form.identity.errors.append(
125 get_message("US_SPECIFY_IDENTITY")[0]
126 )
127 return False
128 if not form.user.is_active:
129 form.identity.errors.append(get_message("DISABLED_ACCOUNT")[0])
130 return False
131 return True
132 return False
133
134
135 class _UnifiedPassCodeForm(Form):
136 """ Common form for signin and verify/reauthenticate.
137 """
138
139 user = None
140 authn_via = None
141
142 passcode = StringField(
143 get_form_field_label("passcode"),
144 render_kw={"placeholder": _("Code or Password")},
145 )
146 submit = SubmitField(get_form_field_label("submit"))
147
148 chosen_method = RadioField(
149 _("Available Methods"),
150 choices=[("email", _("Via email")), ("sms", _("Via SMS"))],
151 validators=[validators.Optional()],
152 )
153 submit_send_code = SubmitField(get_form_field_label("sendcode"))
154
155 def __init__(self, *args, **kwargs):
156 super(_UnifiedPassCodeForm, self).__init__(*args, **kwargs)
157
158 def validate(self):
159 if not super(_UnifiedPassCodeForm, self).validate():
160 return False
161 if not self.user:
162 # This is sign-in case.
163 if not _us_common_validate(self):
164 return False
165
166 totp_secrets = _datastore.us_get_totp_secrets(self.user)
167 if self.submit.data:
168 # This is authn - verify passcode/password
169 # Since we have a unique totp_secret for each method - we
170 # can figure out which mechanism was used.
171 # Note that password check requires a string (not int or None)
172 passcode = self.passcode.data
173 if not passcode:
174 self.passcode.errors.append(get_message("INVALID_PASSWORD_CODE")[0])
175 return False
176 passcode = str(passcode)
177
178 ok = False
179 for method in config_value("US_ENABLED_METHODS"):
180 if method == "password":
181 if self.user.verify_and_update_password(passcode):
182 ok = True
183 break
184 else:
185 if method in totp_secrets and _security._totp_factory.verify_totp(
186 token=passcode,
187 totp_secret=totp_secrets[method],
188 user=self.user,
189 window=config_value("US_TOKEN_VALIDITY"),
190 ):
191 ok = True
192 break
193 if not ok:
194 self.passcode.errors.append(get_message("INVALID_PASSWORD_CODE")[0])
195 return False
196
197 self.authn_via = method
198 return True
199 elif self.submit_send_code.data:
200 # Send a code - chosen_method must be valid
201 cm = self.chosen_method.data
202 if cm not in config_value("US_ENABLED_METHODS"):
203 self.chosen_method.errors.append(
204 get_message("US_METHOD_NOT_AVAILABLE")[0]
205 )
206 return False
207 # Don't require 'email' to be setup since in the case of no password
208 # we have to rely on the 'confirmation' email as verification.
209 # In send_code_helper, we will setup the totp_secret for email on the fly.
210 if cm != "email" and cm not in totp_secrets:
211 self.chosen_method.errors.append(
212 get_message("US_METHOD_NOT_AVAILABLE")[0]
213 )
214 return False
215 if cm == "sms" and not self.user.us_phone_number:
216 # They need to us-setup!
217 self.chosen_method.errors.append(get_message("PHONE_INVALID")[0])
218 return False
219 return True
220 return False # pragma: no cover
221
222
223 class UnifiedSigninForm(_UnifiedPassCodeForm):
224 """ A unified login form
225 For either identity/password or request and enter code.
226 """
227
228 user = None
229
230 identity = StringField(get_form_field_label("identity"), validators=[Required()],)
231 remember = BooleanField(get_form_field_label("remember_me"))
232
233 def __init__(self, *args, **kwargs):
234 super(UnifiedSigninForm, self).__init__(*args, **kwargs)
235 self.remember.default = config_value("DEFAULT_REMEMBER_ME")
236
237 def validate(self):
238 self.user = None
239 if not super(UnifiedSigninForm, self).validate():
240 return False
241
242 if self.submit.data:
243 # This is login
244 # Only check this once authenticated to not give away info
245 if requires_confirmation(self.user):
246 self.identity.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
247 return False
248 return True
249
250
251 class UnifiedVerifyForm(_UnifiedPassCodeForm):
252 """ Verify authentication.
253 This is for freshness 'reauthentication' required.
254 """
255
256 user = None
257
258 def validate(self):
259 self.user = current_user
260 if not super(UnifiedVerifyForm, self).validate():
261 return False
262 return True
263
264
265 class UnifiedSigninSetupForm(Form):
266 """ Setup form """
267
268 chosen_method = RadioField(
269 _("Available Methods"),
270 choices=[
271 ("email", _("Set up using email")),
272 (
273 "authenticator",
274 _("Set up using an authenticator app (e.g. google, lastpass, authy)"),
275 ),
276 ("sms", _("Set up using SMS")),
277 ],
278 )
279 phone = StringField(get_form_field_label("phone"))
280 submit = SubmitField(get_form_field_label("submit"))
281
282 def __init__(self, *args, **kwargs):
283 super(UnifiedSigninSetupForm, self).__init__(*args, **kwargs)
284
285 def validate(self):
286 if not super(UnifiedSigninSetupForm, self).validate():
287 return False
288 if self.chosen_method.data not in config_value("US_ENABLED_METHODS"):
289 self.chosen_method.errors.append(get_message("US_METHOD_NOT_AVAILABLE")[0])
290 return False
291
292 if self.chosen_method.data == "sms":
293 msg = _security._phone_util.validate_phone_number(self.phone.data)
294 if msg:
295 self.phone.errors.append(msg)
296 return False
297
298 return True
299
300
301 class UnifiedSigninSetupValidateForm(Form):
302 """The unified sign in setup validation form """
303
304 # These 2 filled in by view
305 user = None
306 totp_secret = None
307
308 passcode = StringField(get_form_field_label("passcode"), validators=[Required()])
309 submit = SubmitField(get_form_field_label("submitcode"))
310
311 def __init__(self, *args, **kwargs):
312 super(UnifiedSigninSetupValidateForm, self).__init__(*args, **kwargs)
313
314 def validate(self):
315 if not super(UnifiedSigninSetupValidateForm, self).validate():
316 return False
317
318 if not _security._totp_factory.verify_totp(
319 token=self.passcode.data,
320 totp_secret=self.totp_secret,
321 user=self.user,
322 window=config_value("US_TOKEN_VALIDITY"),
323 ):
324 self.passcode.errors.append(get_message("INVALID_PASSWORD_CODE")[0])
325 return False
326
327 return True
328
329
330 def _send_code_helper(form):
331 # send code
332 user = form.user
333 method = form.chosen_method.data
334 totp_secrets = _datastore.us_get_totp_secrets(user)
335 # We 'auto-setup' email since in the case of no password the normal us-setup
336 # mechanisms of course don't work. We rely on the fact that the user went
337 # through the 'confirmation' process to validate the email.
338 if method == "email" and method not in totp_secrets:
339 after_this_request(_commit)
340 totp_secrets[method] = _security._totp_factory.generate_totp_secret()
341 _datastore.us_put_totp_secrets(user, totp_secrets)
342
343 msg = user.us_send_security_token(
344 method,
345 totp_secret=totp_secrets[method],
346 phone_number=user.us_phone_number,
347 send_magic_link=True,
348 )
349 code_sent = True
350 if msg:
351 # send code didn't work
352 code_sent = False
353 form.chosen_method.errors.append(msg)
354 return code_sent, msg
355
356
357 @anonymous_user_required
358 @unauth_csrf(fall_through=True)
359 def us_signin_send_code():
360 """
361 Send code view.
362 This takes an identity (as configured in USER_IDENTITY_ATTRIBUTES)
363 and a method request to send a code.
364 """
365 form_class = _security.us_signin_form
366
367 if request.is_json:
368 if request.content_length:
369 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
370 else:
371 form = form_class(formdata=None, meta=suppress_form_csrf())
372 else:
373 form = form_class(meta=suppress_form_csrf())
374 form.submit_send_code.data = True
375
376 code_methods = _compute_code_methods()
377
378 if form.validate_on_submit():
379 code_sent, msg = _send_code_helper(form)
380 if _security._want_json(request):
381 # Not authenticated yet - so don't send any user info.
382 return base_render_json(
383 form, include_user=False, error_status_code=500 if msg else 400
384 )
385
386 return _security.render_template(
387 config_value("US_SIGNIN_TEMPLATE"),
388 us_signin_form=form,
389 available_methods=config_value("US_ENABLED_METHODS"),
390 code_methods=code_methods,
391 chosen_method=form.chosen_method.data,
392 code_sent=code_sent,
393 skip_loginmenu=True,
394 **_security._run_ctx_processor("us_signin")
395 )
396
397 # Here on GET or failed validation
398 if _security._want_json(request):
399 payload = {
400 "available_methods": config_value("US_ENABLED_METHODS"),
401 "code_methods": code_methods,
402 "identity_attributes": config_value("USER_IDENTITY_ATTRIBUTES"),
403 }
404 return base_render_json(form, include_user=False, additional=payload)
405
406 return _security.render_template(
407 config_value("US_SIGNIN_TEMPLATE"),
408 us_signin_form=form,
409 available_methods=config_value("US_ENABLED_METHODS"),
410 code_methods=code_methods,
411 skip_loginmenu=True,
412 **_security._run_ctx_processor("us_signin")
413 )
414
415
416 @auth_required()
417 def us_verify_send_code():
418 """
419 Send code during verify.
420 """
421 form_class = _security.us_verify_form
422
423 if request.is_json:
424 if request.content_length:
425 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
426 else:
427 form = form_class(formdata=None, meta=suppress_form_csrf())
428 else:
429 form = form_class(meta=suppress_form_csrf())
430 form.submit_send_code.data = True
431
432 code_methods = _compute_code_methods()
433
434 if form.validate_on_submit():
435 code_sent, msg = _send_code_helper(form)
436 if _security._want_json(request):
437 # Not authenticated yet - so don't send any user info.
438 return base_render_json(
439 form, include_user=False, error_status_code=500 if msg else 400
440 )
441
442 return _security.render_template(
443 config_value("US_VERIFY_TEMPLATE"),
444 us_verify_form=form,
445 available_methods=config_value("US_ENABLED_METHODS"),
446 code_methods=code_methods,
447 chosen_method=form.chosen_method.data,
448 code_sent=code_sent,
449 skip_login_menu=True,
450 send_code_to=get_url(
451 _security.us_verify_send_code_url,
452 qparams={"next": propagate_next(request.url)},
453 ),
454 **_security._run_ctx_processor("us_verify")
455 )
456
457 # Here on GET or failed validation
458 if _security._want_json(request):
459 payload = {
460 "available_methods": config_value("US_ENABLED_METHODS"),
461 "code_methods": code_methods,
462 }
463 return base_render_json(form, additional=payload)
464
465 return _security.render_template(
466 config_value("US_VERIFY_TEMPLATE"),
467 us_verify_form=form,
468 available_methods=config_value("US_ENABLED_METHODS"),
469 code_methods=code_methods,
470 skip_login_menu=True,
471 send_code_to=get_url(
472 _security.us_verify_send_code_url,
473 qparams={"next": propagate_next(request.url)},
474 ),
475 **_security._run_ctx_processor("us_verify")
476 )
477
478
479 @unauth_csrf(fall_through=True)
480 def us_signin():
481 """
482 Unified sign in view.
483 This takes an identity (as configured in USER_IDENTITY_ATTRIBUTES)
484 and a passcode (password or OTP).
485
486 Allow already authenticated users. For GET this is useful for
487 single-page-applications on refresh - session still active but need to
488 access user info and csrf-token.
489 For POST - redirects to POST_LOGIN_VIEW (forms) or returns 400 (json).
490 """
491
492 if current_user.is_authenticated and request.method == "POST":
493 # Just redirect current_user to POST_LOGIN_VIEW (or next).
494 # While its tempting to try to logout the current user and login the
495 # new requested user - that simply doesn't work with CSRF.
496
497 # While this is close to anonymous_user_required - it differs in that
498 # it uses get_post_login_redirect which correctly handles 'next'.
499 # TODO: consider changing anonymous_user_required to also call
500 # get_post_login_redirect - not sure why it never has?
501 if _security._want_json(request):
502 payload = json_error_response(
503 errors=get_message("ANONYMOUS_USER_REQUIRED")[0]
504 )
505 return _security._render_json(payload, 400, None, None)
506 else:
507 return redirect(get_post_login_redirect())
508
509 form_class = _security.us_signin_form
510
511 if request.is_json:
512 if request.content_length:
513 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
514 else:
515 form = form_class(formdata=None, meta=suppress_form_csrf())
516 else:
517 form = form_class(meta=suppress_form_csrf())
518 form.submit.data = True
519
520 if form.validate_on_submit():
521 # Require multi-factor is it is enabled, and the method
522 # we authenticated with requires it and either user has requested MFA or it is
523 # required.
524 remember_me = form.remember.data if "remember" in form else None
525 if (
526 config_value("TWO_FACTOR")
527 and form.authn_via in config_value("US_MFA_REQUIRED")
528 and (config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(form.user))
529 ):
530 return tf_login(
531 form.user, remember=remember_me, primary_authn_via=form.authn_via
532 )
533
534 after_this_request(_commit)
535 login_user(form.user, remember=remember_me, authn_via=[form.authn_via])
536
537 if _security._want_json(request):
538 return base_render_json(form, include_auth_token=True)
539
540 return redirect(get_post_login_redirect())
541
542 # Here on GET or failed POST validate
543 code_methods = _compute_code_methods()
544 if _security._want_json(request):
545 payload = {
546 "available_methods": config_value("US_ENABLED_METHODS"),
547 "code_methods": code_methods,
548 "identity_attributes": config_value("USER_IDENTITY_ATTRIBUTES"),
549 }
550 return base_render_json(form, include_user=False, additional=payload)
551
552 if current_user.is_authenticated:
553 # Basically a no-op if authenticated - just perform the same
554 # post-login redirect as if user just logged in.
555 return redirect(get_post_login_redirect())
556
557 # On error - wipe code
558 form.passcode.data = None
559 return _security.render_template(
560 config_value("US_SIGNIN_TEMPLATE"),
561 us_signin_form=form,
562 available_methods=config_value("US_ENABLED_METHODS"),
563 code_methods=code_methods,
564 skip_login_menu=True,
565 **_security._run_ctx_processor("us_signin")
566 )
567
568
569 @auth_required()
570 def us_verify():
571 """
572 Re-authenticate to reset freshness time.
573 This is likely the result of a reauthn_handler redirect, which
574 will have filled in ?next=xxx - which we want to carefully not lose as we
575 go through these steps.
576 """
577 form_class = _security.us_verify_form
578
579 if request.is_json:
580 if request.content_length:
581 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
582 else:
583 form = form_class(formdata=None, meta=suppress_form_csrf())
584 else:
585 form = form_class(meta=suppress_form_csrf())
586 form.submit.data = True
587
588 code_methods = _compute_code_methods()
589
590 if form.validate_on_submit():
591 # verified - so set freshness time.
592 session["fs_paa"] = time.time()
593
594 if _security._want_json(request):
595 return base_render_json(form, include_auth_token=True)
596
597 do_flash(*get_message("REAUTHENTICATION_SUCCESSFUL"))
598 return redirect(get_post_verify_redirect())
599
600 # Here on GET or failed POST validate
601 if _security._want_json(request):
602 payload = {
603 "available_methods": config_value("US_ENABLED_METHODS"),
604 "code_methods": code_methods,
605 }
606 return base_render_json(form, additional=payload)
607
608 # On error - wipe code
609 form.passcode.data = None
610 return _security.render_template(
611 config_value("US_VERIFY_TEMPLATE"),
612 us_verify_form=form,
613 code_methods=code_methods,
614 skip_login_menu=True,
615 send_code_to=get_url(
616 _security.us_verify_send_code_url,
617 qparams={"next": propagate_next(request.url)},
618 ),
619 **_security._run_ctx_processor("us_verify")
620 )
621
622
623 @anonymous_user_required
624 def us_verify_link():
625 """
626 Used to verify a magic email link. GET only
627 """
628 if not all(v in request.args for v in ["email", "code"]):
629 m, c = get_message("API_ERROR")
630 if _security.redirect_behavior == "spa":
631 return redirect(get_url(_security.login_error_view, qparams={c: m}))
632 do_flash(m, c)
633 return redirect(url_for_security("us_signin"))
634
635 user = _datastore.find_user(email=request.args.get("email"))
636 if not user or not user.active:
637 if not user:
638 m, c = get_message("USER_DOES_NOT_EXIST")
639 else:
640 m, c = get_message("DISABLED_ACCOUNT")
641 if _security.redirect_behavior == "spa":
642 return redirect(get_url(_security.login_error_view, qparams={c: m}))
643 do_flash(m, c)
644 return redirect(url_for_security("us_signin"))
645
646 totp_secrets = _datastore.us_get_totp_secrets(user)
647 if "email" not in totp_secrets or not _security._totp_factory.verify_totp(
648 token=request.args.get("code"),
649 totp_secret=totp_secrets["email"],
650 user=user,
651 window=config_value("US_TOKEN_VALIDITY"),
652 ):
653 m, c = get_message("INVALID_CODE")
654 if _security.redirect_behavior == "spa":
655 return redirect(
656 get_url(
657 _security.login_error_view,
658 qparams=user.get_redirect_qparams({c: m}),
659 )
660 )
661 do_flash(m, c)
662 return redirect(url_for_security("us_signin"))
663
664 if (
665 config_value("TWO_FACTOR")
666 and "email" in config_value("US_MFA_REQUIRED")
667 and (config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(user))
668 ):
669 # tf_login doesn't know anything about "spa" etc. In general two-factor
670 # isn't quite ready for SPA. So we return an error via a redirect rather
671 # than mess up SPA applications. To be clear - this simply doesn't
672 # work - using a magic link w/ 2FA - need to use code.
673 if _security.redirect_behavior == "spa":
674 return redirect(
675 get_url(
676 _security.login_error_view,
677 qparams=user.get_redirect_qparams({"tf_required": 1}),
678 )
679 )
680 return tf_login(user, primary_authn_via="email")
681
682 login_user(user, authn_via=["email"])
683 after_this_request(_commit)
684 if _security.redirect_behavior == "spa":
685 # We do NOT send the authentication token here since the only way to
686 # send it would be via a query param and that isn't secure. (logging and
687 # possibly HTTP Referer header).
688 # This means that this can only work if sessions are active which sort of
689 # makes sense - otherwise you need to use /us-signin with a code.
690 return redirect(
691 get_url(_security.post_login_view, qparams=user.get_redirect_qparams())
692 )
693
694 do_flash(*get_message("PASSWORDLESS_LOGIN_SUCCESSFUL"))
695 return redirect(get_post_login_redirect())
696
697
698 @auth_required(
699 within=lambda: config_value("FRESHNESS"),
700 grace=lambda: config_value("FRESHNESS_GRACE_PERIOD"),
701 )
702 def us_setup():
703 """
704 Change unified sign in methods.
705 We want to verify the new method - so don't store anything yet in DB
706 use a timed signed token to pass along state.
707 GET - retrieve current info (json) or form.
708 """
709 form_class = _security.us_setup_form
710
711 if request.is_json:
712 if request.content_length:
713 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
714 else:
715 form = form_class(formdata=None, meta=suppress_form_csrf())
716 else:
717 form = form_class(meta=suppress_form_csrf())
718
719 setup_methods = _compute_setup_methods()
720 active_methods = _compute_active_methods(current_user)
721
722 if form.validate_on_submit():
723 method = form.chosen_method.data
724 # Always generate a totp_secret. We don't set it in the DB until
725 # user has successfully validated.
726 totp = _security._totp_factory.generate_totp_secret()
727
728 # N.B. totp (totp_secret) is actually encrypted - so it seems safe enough
729 # to send it to the user.
730 # Only check phone number if SMS (see form validate)
731 state = {
732 "totp_secret": totp,
733 "chosen_method": method,
734 "phone_number": _security._phone_util.get_canonical_form(form.phone.data)
735 if method == "sms"
736 else None,
737 }
738 msg = current_user.us_send_security_token(
739 method=method,
740 totp_secret=state["totp_secret"],
741 phone_number=state["phone_number"],
742 )
743 if msg:
744 # sending didn't work.
745 form.chosen_method.errors.append(msg)
746 if _security._want_json(request):
747 # Not authenticated yet - so don't send any user info.
748 return base_render_json(
749 form, include_user=False, error_status_code=500 if msg else 400
750 )
751 return _security.render_template(
752 config_value("US_SETUP_TEMPLATE"),
753 available_methods=config_value("US_ENABLED_METHODS"),
754 active_methods=active_methods,
755 setup_methods=setup_methods,
756 us_setup_form=form,
757 **_security._run_ctx_processor("us_setup")
758 )
759
760 state_token = _security.us_setup_serializer.dumps(state)
761
762 if _security._want_json(request):
763 payload = {"state": state_token, "chosen_method": form.chosen_method.data}
764 return base_render_json(form, include_user=False, additional=payload)
765 return _security.render_template(
766 config_value("US_SETUP_TEMPLATE"),
767 available_methods=config_value("US_ENABLED_METHODS"),
768 active_methods=active_methods,
769 setup_methods=setup_methods,
770 code_sent=form.chosen_method.data in _compute_code_methods(),
771 chosen_method=form.chosen_method.data,
772 us_setup_form=form,
773 us_setup_validate_form=_security.us_setup_validate_form(),
774 state=state_token,
775 **_security._run_ctx_processor("us_setup")
776 )
777
778 # Get here on initial new setup (GET)
779 # Or failure of POST
780 if _security._want_json(request):
781 payload = {
782 "identity_attributes": config_value("USER_IDENTITY_ATTRIBUTES"),
783 "available_methods": config_value("US_ENABLED_METHODS"),
784 "active_methods": active_methods,
785 "setup_methods": setup_methods,
786 "phone": current_user.us_phone_number,
787 }
788 return base_render_json(form, include_user=False, additional=payload)
789
790 # Show user existing phone number
791 form.phone.data = current_user.us_phone_number
792 return _security.render_template(
793 config_value("US_SETUP_TEMPLATE"),
794 available_methods=config_value("US_ENABLED_METHODS"),
795 active_methods=active_methods,
796 setup_methods=setup_methods,
797 us_setup_form=form,
798 **_security._run_ctx_processor("us_setup")
799 )
800
801
802 @auth_required()
803 def us_setup_validate(token):
804 """
805 Validate new setup.
806 The token is the state variable which is signed and timed
807 and contains all the state that once confirmed will be stored in the user record.
808 """
809
810 form_class = _security.us_setup_validate_form
811
812 if request.is_json:
813 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
814 else:
815 form = form_class(meta=suppress_form_csrf())
816
817 expired, invalid, state = check_and_get_token_status(
818 token, "us_setup", get_within_delta("US_SETUP_WITHIN")
819 )
820 if invalid:
821 m, c = get_message("API_ERROR")
822 if expired:
823 m, c = get_message("US_SETUP_EXPIRED", within=config_value("US_SETUP_WITHIN"))
824 if invalid or expired:
825 if _security._want_json(request):
826 payload = json_error_response(errors=m)
827 return _security._render_json(payload, 400, None, None)
828 do_flash(m, c)
829 return redirect(url_for_security("us_setup"))
830
831 form.totp_secret = state["totp_secret"]
832 form.user = current_user
833
834 if form.validate_on_submit():
835 after_this_request(_commit)
836 method = state["chosen_method"]
837 phone = state["phone_number"] if method == "sms" else None
838 _datastore.us_set(current_user, method, state["totp_secret"], phone)
839
840 us_profile_changed.send(
841 app._get_current_object(), user=current_user, method=method
842 )
843 if _security._want_json(request):
844 return base_render_json(
845 form,
846 include_user=False,
847 additional=dict(
848 chosen_method=method, phone=current_user.us_phone_number
849 ),
850 )
851 else:
852 do_flash(*get_message("US_SETUP_SUCCESSFUL"))
853 return redirect(
854 get_url(_security.us_post_setup_view)
855 or get_url(_security.post_login_view)
856 )
857
858 # Code not correct/outdated.
859 if _security._want_json(request):
860 return base_render_json(form, include_user=False)
861 m, c = get_message("INVALID_PASSWORD_CODE")
862 do_flash(m, c)
863 return redirect(url_for_security("us_setup"))
864
865
866 @auth_required()
867 def us_qrcode(token):
868
869 if "authenticator" not in config_value("US_ENABLED_METHODS"):
870 return abort(404)
871 expired, invalid, state = check_and_get_token_status(
872 token, "us_setup", get_within_delta("US_SETUP_WITHIN")
873 )
874 if expired or invalid:
875 return abort(400)
876
877 try:
878 import pyqrcode
879
880 # By convention, the URI should have the username that the user
881 # logs in with.
882 username = current_user.calc_username()
883 url = pyqrcode.create(
884 _security._totp_factory.get_totp_uri(
885 username if username else "Unknown", state["totp_secret"]
886 )
887 )
888 except ImportError: # pragma: no cover
889 raise
890 from io import BytesIO
891
892 stream = BytesIO()
893 url.svg(stream, scale=3)
894 return (
895 stream.getvalue(),
896 200,
897 {
898 "Content-Type": "image/svg+xml",
899 "Cache-Control": "no-cache, no-store, must-revalidate",
900 "Pragma": "no-cache",
901 "Expires": "0",
902 },
903 )
904
905
906 def us_send_security_token(
907 user, method, totp_secret, phone_number, send_magic_link=False
908 ):
909 """ Generate and send the security code.
910
911 :param user: The user to send the code to
912 :param method: The method in which the code will be sent
913 :param totp_secret: the unique shared secret of the user
914 :param phone_number: If 'sms' phone number to send to
915 :param send_magic_link: If true a magic link that can be clicked on will be sent.
916 This shouldn't be sent during a setup.
917
918 There is no return value - it is assumed that exceptions are thrown by underlying
919 methods that callers can catch.
920
921 Flask-Security code should NOT call this directly -
922 call :meth:`.UserMixin.us_send_security_token`
923
924 .. versionadded:: 3.4.0
925 """
926 token = _security._totp_factory.generate_totp_password(totp_secret)
927
928 if method == "email":
929 login_link = None
930 if send_magic_link:
931 login_link = url_for_security(
932 "us_verify_link", email=user.email, code=token, _external=True
933 )
934 _security._send_mail(
935 config_value("US_EMAIL_SUBJECT"),
936 user.email,
937 "us_instructions",
938 user=user,
939 username=user.calc_username(),
940 token=token,
941 login_link=login_link,
942 )
943 elif method == "sms":
944 m, c = get_message("USE_CODE", code=token)
945 from_number = config_value("SMS_SERVICE_CONFIG")["PHONE_NUMBER"]
946 to_number = phone_number
947 sms_sender = SmsSenderFactory.createSender(config_value("SMS_SERVICE"))
948 sms_sender.send_sms(from_number=from_number, to_number=to_number, msg=m)
949
950 elif method == "authenticator" or method == "password":
951 # tokens are generated automatically with authenticator apps
952 # and passwords are well passwords
953 # Still go ahead and notify signal receivers that they requested it.
954 token = None
955 us_security_token_sent.send(
956 app._get_current_object(),
957 user=user,
958 method=method,
959 token=token,
960 phone_number=phone_number,
961 send_magic_link=send_magic_link,
962 )
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.utils
3 ~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security utils module
6
7 :copyright: (c) 2012-2019 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10 """
11 import abc
12 import base64
13 import datetime
14 from functools import partial
15 import hashlib
16 import hmac
17 import sys
18 import time
19 import warnings
20 from contextlib import contextmanager
21 from datetime import timedelta
22
23 from flask import _request_ctx_stack, current_app, flash, g, request, session, url_for
24 from flask.json import JSONEncoder
25 from flask.signals import message_flashed
26 from flask_login import login_user as _login_user
27 from flask_login import logout_user as _logout_user
28 from flask_login import current_user
29 from flask_login import COOKIE_NAME as REMEMBER_COOKIE_NAME
30 from flask_mail import Message
31 from flask_principal import AnonymousIdentity, Identity, identity_changed, Need
32 from flask_wtf import csrf
33 from wtforms import validators, ValidationError
34 from itsdangerous import BadSignature, SignatureExpired
35 from speaklater import is_lazy_string
36 from werkzeug.local import LocalProxy
37 from werkzeug.datastructures import MultiDict
38 from .quart_compat import best
39 from .signals import (
40 login_instructions_sent,
41 reset_password_instructions_sent,
42 user_authenticated,
43 user_registered,
44 )
45
46 try: # pragma: no cover
47 from urlparse import parse_qsl, parse_qs, urlsplit, urlunsplit
48 from urllib import urlencode
49 except ImportError: # pragma: no cover
50 from urllib.parse import parse_qsl, parse_qs, urlsplit, urlunsplit, urlencode
51
52 # Convenient references
53 _security = LocalProxy(lambda: current_app.extensions["security"])
54
55 _datastore = LocalProxy(lambda: _security.datastore)
56
57 _pwd_context = LocalProxy(lambda: _security.pwd_context)
58
59 _hashing_context = LocalProxy(lambda: _security.hashing_context)
60
61 localize_callback = LocalProxy(lambda: _security.i18n_domain.gettext)
62
63 PY3 = sys.version_info[0] == 3
64
65 if PY3: # pragma: no cover
66 string_types = (str,) # noqa
67 text_type = str # noqa
68 else: # pragma: no cover
69 string_types = (basestring,) # noqa
70 text_type = unicode # noqa
71
72 FsPermNeed = partial(Need, "fsperm")
73 FsPermNeed.__doc__ = """A need with the method preset to `"fsperm"`."""
74
75
76 def _(translate):
77 """Identity function to mark strings for translation."""
78 return translate
79
80
81 def find_csrf_field_name():
82 """
83 We need to clear it on logout (since that isn't being done by Flask-WTF).
84 The field name is configurable withing Flask-WTF as well as being
85 overridable.
86 We take the field name from the login_form as set by the configuration.
87 """
88 form = _security.login_form(MultiDict([]))
89 if hasattr(form.meta, "csrf_field_name"):
90 return form.meta.csrf_field_name
91 return None
92
93
94 def login_user(user, remember=None, authn_via=None):
95 """Perform the login routine.
96
97 If *SECURITY_TRACKABLE* is used, make sure you commit changes after this
98 request (i.e. ``app.security.datastore.commit()``).
99
100 :param user: The user to login
101 :param remember: Flag specifying if the remember cookie should be set.
102 Defaults to ``False``
103 :param authn_via: A list of strings denoting which mechanism(s) the user
104 authenticated with.
105 These should be one or more of ["password", "sms", "authenticator", "email"] or
106 other 'auto-login' mechanisms.
107 """
108
109 if remember is None:
110 remember = config_value("DEFAULT_REMEMBER_ME")
111
112 if not _login_user(user, remember): # pragma: no cover
113 return False
114
115 if _security.trackable:
116 remote_addr = request.remote_addr or None # make sure it is None
117
118 old_current_login, new_current_login = (
119 user.current_login_at,
120 _security.datetime_factory(),
121 )
122 old_current_ip, new_current_ip = user.current_login_ip, remote_addr
123
124 user.last_login_at = old_current_login or new_current_login
125 user.current_login_at = new_current_login
126 user.last_login_ip = old_current_ip
127 user.current_login_ip = new_current_ip
128 user.login_count = user.login_count + 1 if user.login_count else 1
129
130 _datastore.put(user)
131
132 session["fs_cc"] = "set" # CSRF cookie
133 session["fs_paa"] = time.time() # Primary authentication at - timestamp
134
135 identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
136
137 user_authenticated.send(
138 current_app._get_current_object(), user=user, authn_via=authn_via
139 )
140 return True
141
142
143 def logout_user():
144 """Logs out the current user.
145
146 This will also clean up the remember me cookie if it exists.
147
148 This sends an ``identity_changed`` signal to note that the current
149 identity is now the `AnonymousIdentity`
150 """
151
152 for key in ("identity.name", "identity.auth_type", "fs_paa", "fs_gexp"):
153 session.pop(key, None)
154
155 # Clear csrf token between sessions.
156 # Ideally this would be handled by Flask-WTF but...
157 # We don't clear entire session since Flask-Login seems to like having it.
158 csrf_field_name = find_csrf_field_name()
159 if csrf_field_name:
160 session.pop(csrf_field_name, None)
161 # Flask-WTF 'caches' csrf_token - and only set the session if not already
162 # in 'g'. Be sure to clear both. This affects at least /confirm
163 g.pop(csrf_field_name, None)
164 session["fs_cc"] = "clear"
165 identity_changed.send(
166 current_app._get_current_object(), identity=AnonymousIdentity()
167 )
168 _logout_user()
169
170
171 def _py2timestamp(dt):
172 return time.mktime(dt.timetuple()) + dt.microsecond / 1e6
173
174
175 def check_and_update_authn_fresh(within, grace):
176 """ Check if user authenticated within specified time and update grace period.
177
178 :param within: A timedelta specifying the maximum time in the past that the caller
179 authenticated that is still considered 'fresh'.
180 :param grace: A timedelta that, if the current session is considered 'fresh'
181 will set a grace period for which freshness won't be checked.
182 The intent here is that the caller shouldn't get part-way though
183 a set of operations and suddenly be required to authenticate again.
184
185 If within.total_seconds() is negative, will always return True (always 'fresh').
186 This effectively just disables this entire mechanism.
187
188 If "fs_gexp" is in the session and the current timestamp is less than that,
189 return True and extend grace time (i.e. set fs_gexp to current time + grace).
190
191 If not within the grace period, and within.total_seconds() is 0,
192 return False (not fresh).
193
194 Be aware that for this to work, sessions and therefore session cookies
195 must be functioning and being sent as part of the request.
196
197 .. warning::
198 Be sure the caller is already authenticated PRIOR to calling this method.
199
200 .. versionadded:: 3.4.0
201 """
202
203 if within.total_seconds() < 0:
204 # this means 'always fresh'
205 return True
206
207 if "fs_paa" not in session:
208 # No session, you can't play.
209 return False
210
211 now = datetime.datetime.utcnow()
212 new_exp = now + grace
213 # grace_ts = int(new_exp.timestamp())
214 grace_ts = int(_py2timestamp(new_exp))
215
216 fs_gexp = session.get("fs_gexp", None)
217 if fs_gexp:
218 # if now.timestamp() < fs_gexp:
219 if _py2timestamp(now) < fs_gexp:
220 # Within grace period - extend it and we're good.
221 session["fs_gexp"] = grace_ts
222 return True
223
224 # Special case 0 - return False always, but set grace period.
225 if within.total_seconds() == 0:
226 session["fs_gexp"] = grace_ts
227 return False
228
229 authn_time = datetime.datetime.utcfromtimestamp(session["fs_paa"])
230 # allow for some time drift where it's possible authn_time is in the future
231 # but lets be cautious and not allow arbitrary future times
232 delta = now - authn_time
233 if within > delta > -within:
234 session["fs_gexp"] = grace_ts
235 return True
236 return False
237
238
239 def get_hmac(password):
240 """Returns a Base64 encoded HMAC+SHA512 of the password signed with
241 the salt specified by *SECURITY_PASSWORD_SALT*.
242
243 :param password: The password to sign
244 """
245 salt = _security.password_salt
246
247 if salt is None:
248 raise RuntimeError(
249 "The configuration value `SECURITY_PASSWORD_SALT` must "
250 "not be None when the value of `SECURITY_PASSWORD_HASH` is "
251 'set to "%s"' % _security.password_hash
252 )
253
254 h = hmac.new(encode_string(salt), encode_string(password), hashlib.sha512)
255 return base64.b64encode(h.digest())
256
257
258 def verify_password(password, password_hash):
259 """Returns ``True`` if the password matches the supplied hash.
260
261 :param password: A plaintext password to verify
262 :param password_hash: The expected hash value of the password
263 (usually from your database)
264 """
265 if use_double_hash(password_hash):
266 password = get_hmac(password)
267
268 return _pwd_context.verify(password, password_hash)
269
270
271 def verify_and_update_password(password, user):
272 """Returns ``True`` if the password is valid for the specified user.
273
274 Additionally, the hashed password in the database is updated if the
275 hashing algorithm happens to have changed.
276
277 N.B. you MUST call DB commit if you are using a session-based datastore
278 (such as SqlAlchemy) since the user instance might have been altered
279 (i.e. ``app.security.datastore.commit()``).
280 This is usually handled in the view.
281
282 :param password: A plaintext password to verify
283 :param user: The user to verify against
284
285 .. tip::
286 This should not be called directly - rather use
287 :meth:`.UserMixin.verify_and_update_password`
288
289 """
290 if use_double_hash(user.password):
291 verified = _pwd_context.verify(get_hmac(password), user.password)
292 else:
293 # Try with original password.
294 verified = _pwd_context.verify(password, user.password)
295
296 if verified and _pwd_context.needs_update(user.password):
297 user.password = hash_password(password)
298 _datastore.put(user)
299 return verified
300
301
302 def encrypt_password(password): # pragma: no cover
303 """Encrypt the specified plaintext password.
304
305 It uses the configured encryption options.
306
307 .. deprecated:: 2.0.2
308 Use :func:`hash_password` instead.
309
310 :param password: The plaintext password to encrypt
311 """
312 warnings.warn(
313 "Please use hash_password instead of encrypt_password.", DeprecationWarning
314 )
315 return hash_password(password)
316
317
318 def hash_password(password):
319 """Hash the specified plaintext password.
320
321 Unless the hash algorithm (as specified by `SECURITY_PASSWORD_HASH`) is listed in
322 the configuration variable `SECURITY_PASSWORD_SINGLE_HASH`,
323 perform a double hash - first create an HMAC from the plaintext password
324 and the value of `SECURITY_PASSWORD_SALT`,
325 then use the configured hashing algorithm.
326 This satisfies OWASP/ASVS section 2.4.5: 'provide additional
327 iteration of a key derivation'.
328
329 .. versionadded:: 2.0.2
330
331 :param password: The plaintext password to hash
332 """
333 if use_double_hash():
334 password = get_hmac(password).decode("ascii")
335
336 # Passing in options as part of hash is deprecated in passlib 1.7
337 # and new algorithms like argon2 don't even support it.
338 return _pwd_context.hash(
339 password,
340 **config_value("PASSWORD_HASH_OPTIONS", default={}).get(
341 _security.password_hash, {}
342 )
343 )
344
345
346 def encode_string(string):
347 """Encodes a string to bytes, if it isn't already.
348
349 :param string: The string to encode"""
350
351 if isinstance(string, text_type):
352 string = string.encode("utf-8")
353 return string
354
355
356 def hash_data(data):
357 return _hashing_context.hash(encode_string(data))
358
359
360 def verify_hash(hashed_data, compare_data):
361 return _hashing_context.verify(encode_string(compare_data), hashed_data)
362
363
364 def suppress_form_csrf():
365 """
366 Return meta contents if we should suppress form from attempting to validate CSRF.
367
368 If app doesn't want CSRF for unauth endpoints then check if caller is authenticated
369 or not (many endpoints can be called either way).
370 """
371 ctx = _request_ctx_stack.top
372 if hasattr(ctx, "fs_ignore_csrf") and ctx.fs_ignore_csrf:
373 # This is the case where CsrfProtect was already called (e.g. @auth_required)
374 return {"csrf": False}
375 if (
376 config_value("CSRF_IGNORE_UNAUTH_ENDPOINTS")
377 and not current_user.is_authenticated
378 ):
379 return {"csrf": False}
380 return {}
381
382
383 def do_flash(message, category=None):
384 """Flash a message depending on if the `FLASH_MESSAGES` configuration
385 value is set.
386
387 :param message: The flash message
388 :param category: The flash message category
389 """
390 if config_value("FLASH_MESSAGES"):
391 flash(message, category)
392
393
394 def get_url(endpoint_or_url, qparams=None):
395 """Returns a URL if a valid endpoint is found. Otherwise, returns the
396 provided value.
397
398 :param endpoint_or_url: The endpoint name or URL to default to
399 :param qparams: additional query params to add to end of url
400 :return: URL
401 """
402 try:
403 return transform_url(url_for(endpoint_or_url), qparams)
404 except Exception:
405 # This is an external URL (no endpoint defined in app)
406 # For (mostly) testing - allow changing/adding the url - for example
407 # add a different host:port for cases where the UI is running
408 # separately.
409 if _security.redirect_host:
410 url = transform_url(
411 endpoint_or_url, qparams, netloc=_security.redirect_host
412 )
413 else:
414 url = transform_url(endpoint_or_url, qparams)
415
416 return url
417
418
419 def slash_url_suffix(url, suffix):
420 """Adds a slash either to the beginning or the end of a suffix
421 (which is to be appended to a URL), depending on whether or not
422 the URL ends with a slash."""
423
424 return url.endswith("/") and ("%s/" % suffix) or ("/%s" % suffix)
425
426
427 def transform_url(url, qparams=None, **kwargs):
428 """ Modify url
429
430 :param url: url to transform (can be relative)
431 :param qparams: additional query params to add to end of url
432 :param kwargs: pieces of URL to modify - e.g. netloc=localhost:8000
433 :return: Modified URL
434
435 .. versionadded:: 3.2.0
436 """
437 if not url:
438 return url
439 link_parse = urlsplit(url)
440 if qparams:
441 current_query = dict(parse_qsl(link_parse.query))
442 current_query.update(qparams)
443 link_parse = link_parse._replace(query=urlencode(current_query))
444 return urlunsplit(link_parse._replace(**kwargs))
445
446
447 def get_security_endpoint_name(endpoint):
448 return "%s.%s" % (_security.blueprint_name, endpoint)
449
450
451 def url_for_security(endpoint, **values):
452 """Return a URL for the security blueprint
453
454 :param endpoint: the endpoint of the URL (name of the function)
455 :param values: the variable arguments of the URL rule
456 :param _external: if set to `True`, an absolute URL is generated. Server
457 address can be changed via `SERVER_NAME` configuration variable which
458 defaults to `localhost`.
459 :param _anchor: if provided this is added as anchor to the URL.
460 :param _method: if provided this explicitly specifies an HTTP method.
461 """
462 endpoint = get_security_endpoint_name(endpoint)
463 return url_for(endpoint, **values)
464
465
466 def validate_redirect_url(url):
467 if url is None or url.strip() == "":
468 return False
469 url_next = urlsplit(url)
470 url_base = urlsplit(request.host_url)
471 if (url_next.netloc or url_next.scheme) and url_next.netloc != url_base.netloc:
472 return False
473 return True
474
475
476 def get_post_action_redirect(config_key, declared=None):
477 urls = [
478 get_url(request.args.get("next", None)),
479 get_url(request.form.get("next", None)),
480 find_redirect(config_key),
481 ]
482 if declared:
483 urls.insert(0, declared)
484 for url in urls:
485 if validate_redirect_url(url):
486 return url
487
488
489 def get_post_login_redirect(declared=None):
490 return get_post_action_redirect("SECURITY_POST_LOGIN_VIEW", declared)
491
492
493 def get_post_register_redirect(declared=None):
494 return get_post_action_redirect("SECURITY_POST_REGISTER_VIEW", declared)
495
496
497 def get_post_logout_redirect(declared=None):
498 return get_post_action_redirect("SECURITY_POST_LOGOUT_VIEW", declared)
499
500
501 def get_post_verify_redirect(declared=None):
502 return get_post_action_redirect("SECURITY_POST_VERIFY_VIEW", declared)
503
504
505 def find_redirect(key):
506 """Returns the URL to redirect to after a user logs in successfully.
507
508 :param key: The session or application configuration key to search for
509 """
510 rv = (
511 get_url(session.pop(key.lower(), None))
512 or get_url(current_app.config[key.upper()] or None)
513 or "/"
514 )
515 return rv
516
517
518 def propagate_next(url):
519 # return either URL or, if URL already has a ?next=xx, return that.
520 url_next = urlsplit(url)
521 qparams = parse_qs(url_next.query)
522 if "next" in qparams:
523 return qparams["next"][0]
524 return url
525
526
527 def get_config(app):
528 """Conveniently get the security configuration for the specified
529 application without the annoying 'SECURITY_' prefix.
530
531 :param app: The application to inspect
532 """
533 items = app.config.items()
534 prefix = "SECURITY_"
535
536 def strip_prefix(tup):
537 return (tup[0].replace("SECURITY_", ""), tup[1])
538
539 return dict([strip_prefix(i) for i in items if i[0].startswith(prefix)])
540
541
542 def get_message(key, **kwargs):
543 rv = config_value("MSG_" + key)
544 return localize_callback(rv[0], **kwargs), rv[1]
545
546
547 def config_value(key, app=None, default=None):
548 """Get a Flask-Security configuration value.
549
550 :param key: The configuration key without the prefix `SECURITY_`
551 :param app: An optional specific application to inspect. Defaults to
552 Flask's `current_app`
553 :param default: An optional default value if the value is not set
554 """
555 app = app or current_app
556 return get_config(app).get(key.upper(), default)
557
558
559 def get_max_age(key, app=None):
560 td = get_within_delta(key + "_WITHIN", app)
561 return td.seconds + td.days * 24 * 3600
562
563
564 def get_within_delta(key, app=None):
565 """Get a timedelta object from the application configuration following
566 the internal convention of::
567
568 <Amount of Units> <Type of Units>
569
570 Examples of valid config values::
571
572 5 days
573 10 minutes
574
575 :param key: The config value key without the `SECURITY_` prefix
576 :param app: Optional application to inspect. Defaults to Flask's
577 `current_app`
578 """
579 txt = config_value(key, app=app)
580 values = txt.split()
581 return timedelta(**{values[1]: int(values[0])})
582
583
584 def send_mail(subject, recipient, template, **context):
585 """Send an email via the Flask-Mail extension.
586
587 :param subject: Email subject
588 :param recipient: Email recipient
589 :param template: The name of the email template
590 :param context: The context to render the template with
591 """
592
593 context.setdefault("security", _security)
594 context.update(_security._run_ctx_processor("mail"))
595
596 sender = _security.email_sender
597 if isinstance(sender, LocalProxy):
598 sender = sender._get_current_object()
599
600 msg = Message(subject, sender=sender, recipients=[recipient])
601
602 ctx = ("security/email", template)
603 if config_value("EMAIL_PLAINTEXT"):
604 msg.body = _security.render_template("%s/%s.txt" % ctx, **context)
605 if config_value("EMAIL_HTML"):
606 msg.html = _security.render_template("%s/%s.html" % ctx, **context)
607
608 if _security._send_mail_task:
609 _security._send_mail_task(msg)
610 return
611
612 mail = current_app.extensions.get("mail")
613 mail.send(msg)
614
615
616 def get_token_status(token, serializer, max_age=None, return_data=False):
617 """Get the status of a token.
618
619 :param token: The token to check
620 :param serializer: The name of the seriailzer. Can be one of the
621 following: ``confirm``, ``login``, ``reset``
622 :param max_age: The name of the max age config option. Can be on of
623 the following: ``CONFIRM_EMAIL``, ``LOGIN``,
624 ``RESET_PASSWORD``
625 """
626 serializer = getattr(_security, serializer + "_serializer")
627 max_age = get_max_age(max_age)
628 user, data = None, None
629 expired, invalid = False, False
630
631 try:
632 data = serializer.loads(token, max_age=max_age)
633 except SignatureExpired:
634 d, data = serializer.loads_unsafe(token)
635 expired = True
636 except (BadSignature, TypeError, ValueError):
637 invalid = True
638
639 if data:
640 user = _datastore.find_user(id=data[0])
641
642 expired = expired and (user is not None)
643
644 if return_data:
645 return expired, invalid, user, data
646 else:
647 return expired, invalid, user
648
649
650 def check_and_get_token_status(token, serializer, within=None):
651 """Get the status of a token and return data.
652
653 :param token: The token to check
654 :param serializer: The name of the serializer. Can be one of the
655 following: ``confirm``, ``login``, ``reset``, ``us_setup``
656 :param within: max age - passed as a timedelta
657
658 :return: a tuple of (expired, invalid, data)
659
660 .. versionadded:: 3.4.0
661 """
662 serializer = getattr(_security, serializer + "_serializer")
663 max_age = within.total_seconds()
664 data = None
665 expired, invalid = False, False
666
667 try:
668 data = serializer.loads(token, max_age=max_age)
669 except SignatureExpired:
670 d, data = serializer.loads_unsafe(token)
671 expired = True
672 except (BadSignature, TypeError, ValueError):
673 invalid = True
674
675 return expired, invalid, data
676
677
678 def get_identity_attributes(app=None):
679 app = app or current_app
680 attrs = app.config["SECURITY_USER_IDENTITY_ATTRIBUTES"]
681 try:
682 attrs = [f.strip() for f in attrs.split(",")]
683 except AttributeError:
684 pass
685 return attrs
686
687
688 def uia_phone_mapper(identity):
689 """ Used to match identity as a phone number. This is a simple proxy
690 to :py:class:`PhoneUtil`
691
692 See :py:data:`SECURITY_USER_IDENTITY_MAPPINGS`.
693
694 .. versionadded:: 3.4.0
695 """
696 ph = _security._phone_util.get_canonical_form(identity)
697 return ph
698
699
700 def uia_email_mapper(identity):
701 """ Used to match identity as an email.
702
703 See :py:data:`SECURITY_USER_IDENTITY_MAPPINGS`.
704
705 .. versionadded:: 3.4.0
706 """
707
708 # Fake up enough to invoke the WTforms email validator.
709 class FakeField(object):
710 pass
711
712 email_validator = validators.Email(message="nothing")
713 field = FakeField()
714 setattr(field, "data", identity)
715 try:
716 email_validator(None, field)
717 except ValidationError:
718 return None
719 return identity
720
721
722 def use_double_hash(password_hash=None):
723 """Return a bool indicating whether a password should be hashed twice."""
724 # Default to plaintext for backward compatibility with
725 # SECURITY_PASSWORD_SINGLE_HASH = False
726 single_hash = config_value("PASSWORD_SINGLE_HASH") or {"plaintext"}
727
728 if password_hash is None:
729 scheme = _security.password_hash
730 else:
731 scheme = _pwd_context.identify(password_hash)
732
733 return not (single_hash is True or scheme in single_hash)
734
735
736 def csrf_cookie_handler(response):
737 """ Called at end of every request.
738 Uses session to track state (set/clear)
739
740 Ideally we just need to set this once - however by default
741 Flask-WTF has a time-out on these tokens governed by *WTF_CSRF_TIME_LIMIT*.
742 While we could set that to None - and OWASP implies this is fine - that might
743 not be agreeable to everyone.
744 So as a basic usability hack - we check if it is expired and re-generate so at least
745 the user doesn't have to log out and back in (just refresh).
746 We also support a *CSRF_COOKIE_REFRESH_EACH_REQUEST* analogous to Flask's
747 *SESSION_REFRESH_EACH_REQUEST*
748
749 It is of course removed on logout/session end.
750 Other info on web suggests replacing on every POST and accepting up to 'age' ago.
751 """
752 csrf_cookie = config_value("CSRF_COOKIE")
753 if not csrf_cookie or not csrf_cookie["key"]:
754 return response
755
756 op = session.get("fs_cc", None)
757 if not op:
758 remember_cookie_name = current_app.config.get(
759 "REMEMBER_COOKIE_NAME", REMEMBER_COOKIE_NAME
760 )
761 has_remember_cookie = (
762 remember_cookie_name in request.cookies
763 and session.get("remember") != "clear"
764 )
765 # Set cookie if successfully logged in with flask_login's remember cookie
766 if has_remember_cookie and current_user.is_authenticated:
767 op = "set"
768 else:
769 return response
770
771 if op == "clear":
772 response.delete_cookie(
773 csrf_cookie["key"],
774 path=csrf_cookie.get("path", "/"),
775 domain=csrf_cookie.get("domain", None),
776 )
777 session.pop("fs_cc")
778 return response
779
780 # Send a cookie if any of:
781 # 1) CSRF_COOKIE_REFRESH_EACH_REQUEST is true
782 # 2) fs_cc == "set" - this is on first login
783 # 3) existing cookie has expired
784 send = False
785 if op == "set":
786 send = True
787 session["fs_cc"] = "sent"
788 elif config_value("CSRF_COOKIE_REFRESH_EACH_REQUEST"):
789 send = True
790 elif current_app.config["WTF_CSRF_TIME_LIMIT"]:
791 current_cookie = request.cookies.get(csrf_cookie["key"], None)
792 if current_cookie:
793 # Lets make sure it isn't expired if app doesn't set TIME_LIMIT to None.
794 try:
795 csrf.validate_csrf(current_cookie)
796 except ValidationError:
797 send = True
798
799 if send:
800 kwargs = {k: v for k, v in csrf_cookie.items()}
801 kwargs.pop("key")
802 kwargs["value"] = csrf.generate_csrf()
803 response.set_cookie(csrf_cookie["key"], **kwargs)
804 return response
805
806
807 def base_render_json(
808 form,
809 include_user=True,
810 include_auth_token=False,
811 additional=None,
812 error_status_code=400,
813 ):
814 has_errors = len(form.errors) > 0
815
816 user = form.user if hasattr(form, "user") else None
817 if has_errors:
818 code = error_status_code
819 payload = json_error_response(errors=form.errors)
820 else:
821 code = 200
822 payload = dict()
823 if user:
824 # This allows anonymous GETs via JSON
825 if include_user:
826 payload["user"] = user.get_security_payload()
827
828 if include_auth_token:
829 # view wants to return auth_token - check behavior config
830 if (
831 config_value("BACKWARDS_COMPAT_AUTH_TOKEN")
832 or "include_auth_token" in request.args
833 ):
834 token = user.get_auth_token()
835 payload["user"]["authentication_token"] = token
836
837 # Return csrf_token on each JSON response - just as every form
838 # has it rendered.
839 payload["csrf_token"] = csrf.generate_csrf()
840 if additional:
841 payload.update(additional)
842
843 return _security._render_json(payload, code, headers=None, user=user)
844
845
846 def default_want_json(req):
847 """ Return True if response should be in json
848 N.B. do not call this directly - use security.want_json()
849
850 :param req: Flask/Werkzeug Request
851 """
852 if req.is_json:
853 return True
854 # TODO should this handle json sub-types?
855 accept_mimetypes = req.accept_mimetypes
856 if not hasattr(req.accept_mimetypes, "best"): # pragma: no cover
857 # Alright. we dont have the best property, lets add it ourselves.
858 # This is for quart compatibility
859 setattr(accept_mimetypes, "best", best)
860 if accept_mimetypes.best == "application/json":
861 return True
862 return False
863
864
865 def json_error_response(errors):
866 """ Helper to create an error response that adheres to the openapi spec.
867 """
868 # Python 2 and 3 compatibility for checking if something is a string.
869 try: # pragma: no cover
870 basestring
871 string_type_check = (basestring, unicode)
872 except NameError: # pragma: no cover
873 string_type_check = str
874
875 if isinstance(errors, string_type_check):
876 # When the errors is a string, use the response/error/message format
877 response_json = dict(error=errors)
878 elif isinstance(errors, dict):
879 # When the errors is a dict, use the DefaultJsonErrorResponse
880 # (response/errors/name/messages) format
881 response_json = dict(errors=errors)
882 else:
883 raise TypeError("The errors argument should be either a str or dict.")
884
885 return response_json
886
887
888 class FsJsonEncoder(JSONEncoder):
889 """ Flask-Security JSON encoder.
890 Extends Flask's JSONencoder to handle lazy-text.
891
892 .. versionadded:: 3.3.0
893 """
894
895 def default(self, obj):
896 if is_lazy_string(obj):
897 return str(obj)
898 else:
899 return JSONEncoder.default(self, obj)
900
901
902 @contextmanager
903 def capture_passwordless_login_requests():
904 login_requests = []
905
906 def _on(app, **data):
907 login_requests.append(data)
908
909 login_instructions_sent.connect(_on)
910
911 try:
912 yield login_requests
913 finally:
914 login_instructions_sent.disconnect(_on)
915
916
917 @contextmanager
918 def capture_registrations():
919 """Testing utility for capturing registrations.
920 """
921 registrations = []
922
923 def _on(app, **data):
924 registrations.append(data)
925
926 user_registered.connect(_on)
927
928 try:
929 yield registrations
930 finally:
931 user_registered.disconnect(_on)
932
933
934 @contextmanager
935 def capture_reset_password_requests(reset_password_sent_at=None):
936 """Testing utility for capturing password reset requests.
937
938 :param reset_password_sent_at: An optional datetime object to set the
939 user's `reset_password_sent_at` to
940 """
941 reset_requests = []
942
943 def _on(app, **data):
944 reset_requests.append(data)
945
946 reset_password_instructions_sent.connect(_on)
947
948 try:
949 yield reset_requests
950 finally:
951 reset_password_instructions_sent.disconnect(_on)
952
953
954 @contextmanager
955 def capture_flashes():
956 """Testing utility for capturing flashes."""
957 flashes = []
958
959 def _on(app, **data):
960 flashes.append(data)
961
962 message_flashed.connect(_on)
963
964 try:
965 yield flashes
966 finally:
967 message_flashed.disconnect(_on)
968
969
970 class SmsSenderBaseClass(object):
971 __metaclass__ = abc.ABCMeta
972
973 def __init__(self):
974 pass
975
976 @abc.abstractmethod
977 def send_sms(self, from_number, to_number, msg): # pragma: no cover
978 """ Abstract method for sending sms messages
979
980 .. versionadded:: 3.2.0
981 """
982 return
983
984
985 class DummySmsSender(SmsSenderBaseClass):
986 def send_sms(self, from_number, to_number, msg): # pragma: no cover
987 """ Do nothing. """
988 return
989
990
991 class SmsSenderFactory(object):
992 senders = {"Dummy": DummySmsSender}
993
994 @classmethod
995 def createSender(cls, name, *args, **kwargs):
996 """ Initialize an SMS sender.
997
998 :param name: Name as registered in SmsSenderFactory:senders (e.g. 'Twilio')
999
1000 .. versionadded:: 3.2.0
1001 """
1002 return cls.senders[name](*args, **kwargs)
1003
1004
1005 try: # pragma: no cover
1006 from twilio.rest import Client
1007
1008 class TwilioSmsSender(SmsSenderBaseClass):
1009 def __init__(self):
1010 self.account_sid = config_value("SMS_SERVICE_CONFIG")["ACCOUNT_SID"]
1011 self.auth_token = config_value("SMS_SERVICE_CONFIG")["AUTH_TOKEN"]
1012
1013 def send_sms(self, from_number, to_number, msg):
1014 """ Send message via twilio account. """
1015 client = Client(self.account_sid, self.auth_token)
1016 client.messages.create(to=to_number, from_=from_number, body=msg)
1017
1018 SmsSenderFactory.senders["Twilio"] = TwilioSmsSender
1019 except Exception:
1020 pass
1021
1022
1023 def password_length_validator(password):
1024 """ Test password for length.
1025
1026 :param password: Plain text password to check
1027
1028 :return: ``None`` if password conforms to length requirements,
1029 a list of error/suggestions if not.
1030
1031 .. versionadded:: 3.4.0
1032
1033 """
1034 if len(password) < config_value("PASSWORD_LENGTH_MIN") or len(password) > 128:
1035 return [
1036 get_message(
1037 "PASSWORD_INVALID_LENGTH", length=config_value("PASSWORD_LENGTH_MIN")
1038 )[0]
1039 ]
1040 return None
1041
1042
1043 def password_complexity_validator(password, is_register, **kwargs):
1044 """ Test password for complexity.
1045
1046 Currently just supports 'zxcvbn'.
1047
1048 :param password: Plain text password to check
1049 :param is_register: if True then kwargs are arbitrary additional info. (e.g.
1050 info from a registration form). If False, must be a SINGLE key "user" that
1051 corresponds to the current_user. All string values will be extracted and
1052 sent to the complexity checker.
1053 :param kwargs:
1054
1055 :return: ``None`` if password is complex enough, a list of error/suggestions if not.
1056 Be aware that zxcvbn does not (easily) provide a way to localize messages.
1057
1058 .. versionadded:: 3.4.0
1059 """
1060
1061 if config_value("PASSWORD_COMPLEXITY_CHECKER") == "zxcvbn":
1062 import zxcvbn
1063
1064 user_info = []
1065 if not is_register:
1066 for v in kwargs["user"].__dict__.values():
1067 if v and isinstance(v, str):
1068 user_info.append(v)
1069 else:
1070 # This is usually all register form values that are in the user_model
1071 if kwargs:
1072 user_info = kwargs.values()
1073 results = zxcvbn.zxcvbn(password, user_inputs=user_info)
1074 if results["score"] > 2:
1075 # Good or Strong
1076 return None
1077 # Should we return suggestions? Default forms don't really know what to do.
1078 if results["feedback"]["warning"]:
1079 # Note that these come from zxcvbn and
1080 # aren't localizable via Flask-Security
1081 return [results["feedback"]["warning"]]
1082 return [get_message("PASSWORD_TOO_SIMPLE")[0]]
1083 else:
1084 return None
1085
1086
1087 def password_breached_validator(password):
1088 """ Check if password on breached list.
1089 Does nothing unless :py:data:`SECURITY_PASSWORD_CHECK_BREACHED` is set.
1090 If password is found on the breached list, return an error if the count is
1091 greater than or equal to :py:data:`SECURITY_PASSWORD_BREACHED_COUNT`.
1092
1093 :param password: Plain text password to check
1094
1095 :return: ``None`` if password passes breached tests, else a list of error messages.
1096
1097 .. versionadded:: 3.4.0
1098 """
1099 pwn = config_value("PASSWORD_CHECK_BREACHED")
1100 if pwn:
1101 try:
1102 cnt = pwned(password)
1103 if cnt >= config_value("PASSWORD_BREACHED_COUNT"):
1104 return [get_message("PASSWORD_BREACHED")[0]]
1105 except Exception:
1106 if pwn == "strict":
1107 return [get_message("PASSWORD_BREACHED_SITE_ERROR")[0]]
1108 return None
1109
1110
1111 def default_password_validator(password, is_register, **kwargs):
1112 """
1113 Password validation.
1114 Called in app/request context.
1115
1116 N.B. do not call this directly - use security._password_validator
1117 """
1118 notok = password_length_validator(password)
1119 if notok:
1120 return notok
1121
1122 notok = password_breached_validator(password)
1123 if notok:
1124 return notok
1125
1126 return password_complexity_validator(password, is_register, **kwargs)
1127
1128
1129 def pwned(password):
1130 """
1131 Check password against pwnedpasswords API using k-Anonymity.
1132 https://haveibeenpwned.com/API/v3
1133
1134 :return: Count of password in DB (0 means hasn't been compromised)
1135 Can raise HTTPError
1136
1137 Only implemented for python 3
1138
1139 .. versionadded:: 3.4.0
1140 """
1141
1142 def convert_password_tuple(value):
1143 hash_suffix, count = value.split(":")
1144 return hash_suffix, int(count)
1145
1146 sha1 = hashlib.sha1(password.encode("utf8")).hexdigest()
1147
1148 if PY3:
1149 import urllib.request
1150 import urllib.error
1151
1152 req = urllib.request.Request(
1153 url="https://api.pwnedpasswords.com/range/{}".format(sha1[:5].upper()),
1154 headers={"User-Agent": "Flask-Security (Python)"},
1155 )
1156 # Might raise HTTPError
1157 with urllib.request.urlopen(req) as f:
1158 response = f.read()
1159
1160 raw = response.decode("utf-8-sig")
1161
1162 entries = dict(map(convert_password_tuple, raw.upper().split("\r\n")))
1163 return entries.get(sha1[5:].upper(), 0)
1164
1165 raise NotImplementedError()
0 # -*- coding: utf-8 -*-
1 """
2 flask_security.views
3 ~~~~~~~~~~~~~~~~~~~~
4
5 Flask-Security views module
6
7 :copyright: (c) 2012 by Matt Wright.
8 :copyright: (c) 2019-2020 by J. Christopher Wagner (jwag).
9 :license: MIT, see LICENSE for more details.
10
11 CSRF is tricky. By default all our forms have CSRF protection built in via
12 Flask-WTF. This is regardless of authentication method or whether the request
13 is Form or JSON based. Form-based 'just works' since when rendering the form
14 (on GET), the CSRF token is automatically populated.
15 We want to handle:
16 - JSON requests where CSRF token is in a header (e.g. X-CSRF-Token)
17 - Option to skip CSRF when using a token to authenticate (rather than session)
18 (CSRF_PROTECT_MECHANISMS)
19 - Option to skip CSRF for 'login'/unauthenticated requests
20 (CSRF_IGNORE_UNAUTH_ENDPOINTS)
21 This is complicated by the fact that the only way to disable form CSRF is to
22 pass in meta={csrf: false} at form instantiation time.
23
24 Be aware that for CSRF to work, caller MUST pass in session cookie. So
25 for pure API, and no session cookie - there is no way to support CSRF-Login
26 so app must set CSRF_IGNORE_UNAUTH_ENDPOINTS (or use CSRF/session cookie for logging
27 in then once they have a token, no need for cookie).
28
29 TODO: two-factor routes such as tf_setup need work. They seem to support both
30 authenticated (via session?) as well as unauthenticated access.
31 """
32
33 import sys
34 import time
35
36 from flask import (
37 Blueprint,
38 abort,
39 after_this_request,
40 current_app,
41 jsonify,
42 request,
43 session,
44 )
45 from flask_login import current_user
46 from werkzeug.datastructures import MultiDict
47 from werkzeug.local import LocalProxy
48
49 from .changeable import change_user_password
50 from .confirmable import (
51 confirm_email_token_status,
52 confirm_user,
53 send_confirmation_instructions,
54 )
55 from .decorators import anonymous_user_required, auth_required, unauth_csrf
56 from .passwordless import login_token_status, send_login_instructions
57 from .quart_compat import get_quart_status
58 from .unified_signin import (
59 us_signin,
60 us_signin_send_code,
61 us_qrcode,
62 us_setup,
63 us_setup_validate,
64 us_verify,
65 us_verify_link,
66 us_verify_send_code,
67 )
68 from .recoverable import (
69 reset_password_token_status,
70 send_reset_password_instructions,
71 update_password,
72 )
73 from .registerable import register_user
74 from .twofactor import (
75 complete_two_factor_process,
76 tf_clean_session,
77 tf_disable,
78 tf_login,
79 )
80 from .utils import (
81 base_render_json,
82 config_value,
83 do_flash,
84 get_message,
85 get_post_login_redirect,
86 get_post_logout_redirect,
87 get_post_register_redirect,
88 get_post_verify_redirect,
89 get_url,
90 json_error_response,
91 login_user,
92 logout_user,
93 slash_url_suffix,
94 suppress_form_csrf,
95 url_for_security,
96 )
97
98 if get_quart_status(): # pragma: no cover
99 from quart import make_response, redirect
100 else:
101 from flask import make_response, redirect
102
103 # Convenient references
104 _security = LocalProxy(lambda: current_app.extensions["security"])
105 _datastore = LocalProxy(lambda: _security.datastore)
106
107
108 def default_render_json(payload, code, headers, user):
109 """ Default JSON response handler.
110 """
111 # Force Content-Type header to json.
112 if headers is None:
113 headers = dict()
114 headers["Content-Type"] = "application/json"
115 payload = dict(meta=dict(code=code), response=payload)
116 return make_response(jsonify(payload), code, headers)
117
118
119 PY3 = sys.version_info[0] == 3
120 if PY3 and get_quart_status(): # pragma: no cover
121 from .async_compat import _commit # noqa: F401
122 else:
123
124 def _commit(response=None):
125 _datastore.commit()
126 return response
127
128
129 def _ctx(endpoint):
130 return _security._run_ctx_processor(endpoint)
131
132
133 @unauth_csrf(fall_through=True)
134 def login():
135 """View function for login view
136
137 Allow already authenticated users. For GET this is useful for
138 single-page-applications on refresh - session still active but need to
139 access user info and csrf-token.
140 For POST - redirects to POST_LOGIN_VIEW (forms) or returns 400 (json).
141 """
142
143 if current_user.is_authenticated and request.method == "POST":
144 # Just redirect current_user to POST_LOGIN_VIEW (or next).
145 # While its tempting to try to logout the current user and login the
146 # new requested user - that simply doesn't work with CSRF.
147
148 # While this is close to anonymous_user_required - it differs in that
149 # it uses get_post_login_redirect which correctly handles 'next'.
150 # TODO: consider changing anonymous_user_required to also call
151 # get_post_login_redirect - not sure why it never has?
152 if _security._want_json(request):
153 payload = json_error_response(
154 errors=get_message("ANONYMOUS_USER_REQUIRED")[0]
155 )
156 return _security._render_json(payload, 400, None, None)
157 else:
158 return redirect(get_post_login_redirect())
159
160 form_class = _security.login_form
161
162 if request.is_json:
163 # Allow GET so we can return csrf_token for pre-login.
164 if request.content_length:
165 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
166 else:
167 form = form_class(MultiDict([]), meta=suppress_form_csrf())
168 else:
169 form = form_class(request.form, meta=suppress_form_csrf())
170
171 if form.validate_on_submit():
172 remember_me = form.remember.data if "remember" in form else None
173 if config_value("TWO_FACTOR") and (
174 config_value("TWO_FACTOR_REQUIRED")
175 or (form.user.tf_totp_secret and form.user.tf_primary_method)
176 ):
177 return tf_login(
178 form.user, remember=remember_me, primary_authn_via="password"
179 )
180
181 login_user(form.user, remember=remember_me, authn_via=["password"])
182 after_this_request(_commit)
183
184 if not _security._want_json(request):
185 return redirect(get_post_login_redirect())
186
187 if _security._want_json(request):
188 if current_user.is_authenticated:
189 form.user = current_user
190 return base_render_json(form, include_auth_token=True)
191
192 if current_user.is_authenticated:
193 # Basically a no-op if authenticated - just perform the same
194 # post-login redirect as if user just logged in.
195 return redirect(get_post_login_redirect())
196 else:
197 return _security.render_template(
198 config_value("LOGIN_USER_TEMPLATE"), login_user_form=form, **_ctx("login")
199 )
200
201
202 @auth_required()
203 def verify():
204 """View function which handles a authentication verification request.
205 """
206 form_class = _security.verify_form
207
208 if request.is_json:
209 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
210 else:
211 form = form_class(meta=suppress_form_csrf())
212
213 if form.validate_on_submit():
214 # form may have called verify_and_update_password()
215 after_this_request(_commit)
216
217 # verified - so set freshness time.
218 session["fs_paa"] = time.time()
219
220 if _security._want_json(request):
221 return base_render_json(form)
222 do_flash(*get_message("REAUTHENTICATION_SUCCESSFUL"))
223 return redirect(get_post_verify_redirect())
224
225 if _security._want_json(request):
226 assert form.user == current_user
227 return base_render_json(form)
228
229 return _security.render_template(
230 config_value("VERIFY_TEMPLATE"), verify_form=form, **_ctx("verify")
231 )
232
233
234 def logout():
235 """View function which handles a logout request."""
236 tf_clean_session()
237
238 if current_user.is_authenticated:
239 logout_user()
240
241 # No body is required - so if a POST and json - return OK
242 if request.method == "POST" and _security._want_json(request):
243 return _security._render_json({}, 200, headers=None, user=None)
244
245 return redirect(get_post_logout_redirect())
246
247
248 @anonymous_user_required
249 def register():
250 """View function which handles a registration request."""
251
252 # For some unknown historic reason - if you don't require confirmation
253 # (via email) then you need to type in your password twice. That might
254 # make sense if you can't reset your password but in modern (2020) UX models
255 # don't ask twice.
256 if _security.confirmable or request.is_json:
257 form_class = _security.confirm_register_form
258 else:
259 form_class = _security.register_form
260
261 if request.is_json:
262 form_data = MultiDict(request.get_json())
263 else:
264 form_data = request.form
265
266 form = form_class(form_data, meta=suppress_form_csrf())
267 if form.validate_on_submit():
268 did_login = False
269 user = register_user(form)
270 form.user = user
271
272 # The 'auto-login' feature probably should be removed - I can't imagine
273 # an application that would want random email accounts. It has been like this
274 # since the beginning. Note that we still enforce 2FA - however for unified
275 # signin - we adhere to historic behavior.
276 if not _security.confirmable or _security.login_without_confirmation:
277 if config_value("TWO_FACTOR") and config_value("TWO_FACTOR_REQUIRED"):
278 return tf_login(user, primary_authn_via="register")
279 after_this_request(_commit)
280 login_user(user, authn_via=["register"])
281 did_login = True
282
283 if not _security._want_json(request):
284 return redirect(get_post_register_redirect())
285
286 # Only include auth token if in fact user is permitted to login
287 return base_render_json(form, include_auth_token=did_login)
288 if _security._want_json(request):
289 return base_render_json(form)
290
291 return _security.render_template(
292 config_value("REGISTER_USER_TEMPLATE"),
293 register_user_form=form,
294 **_ctx("register")
295 )
296
297
298 @unauth_csrf(fall_through=True)
299 def send_login():
300 """View function that sends login instructions for passwordless login"""
301
302 form_class = _security.passwordless_login_form
303
304 if request.is_json:
305 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
306 else:
307 form = form_class(meta=suppress_form_csrf())
308
309 if form.validate_on_submit():
310 send_login_instructions(form.user)
311 if not _security._want_json(request):
312 do_flash(*get_message("LOGIN_EMAIL_SENT", email=form.user.email))
313
314 if _security._want_json(request):
315 return base_render_json(form)
316
317 return _security.render_template(
318 config_value("SEND_LOGIN_TEMPLATE"), send_login_form=form, **_ctx("send_login")
319 )
320
321
322 @anonymous_user_required
323 def token_login(token):
324 """View function that handles passwordless login via a token
325 Like reset-password and confirm - this is usually a GET via an email
326 so from the request we can't differentiate form-based apps from non.
327 """
328
329 expired, invalid, user = login_token_status(token)
330
331 if not user or invalid:
332 m, c = get_message("INVALID_LOGIN_TOKEN")
333 if _security.redirect_behavior == "spa":
334 return redirect(get_url(_security.login_error_view, qparams={c: m}))
335 do_flash(m, c)
336 return redirect(url_for_security("login"))
337 if expired:
338 send_login_instructions(user)
339 m, c = get_message(
340 "LOGIN_EXPIRED", email=user.email, within=_security.login_within
341 )
342 if _security.redirect_behavior == "spa":
343 return redirect(
344 get_url(
345 _security.login_error_view,
346 qparams=user.get_redirect_qparams({c: m}),
347 )
348 )
349 do_flash(m, c)
350 return redirect(url_for_security("login"))
351
352 login_user(user, authn_via=["token"])
353 after_this_request(_commit)
354 if _security.redirect_behavior == "spa":
355 return redirect(
356 get_url(_security.post_login_view, qparams=user.get_redirect_qparams())
357 )
358
359 do_flash(*get_message("PASSWORDLESS_LOGIN_SUCCESSFUL"))
360
361 return redirect(get_post_login_redirect())
362
363
364 @unauth_csrf(fall_through=True)
365 def send_confirmation():
366 """View function which sends confirmation instructions."""
367
368 form_class = _security.send_confirmation_form
369
370 if request.is_json:
371 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
372 else:
373 form = form_class(meta=suppress_form_csrf())
374
375 if form.validate_on_submit():
376 send_confirmation_instructions(form.user)
377 if not _security._want_json(request):
378 do_flash(*get_message("CONFIRMATION_REQUEST", email=form.user.email))
379
380 if _security._want_json(request):
381 return base_render_json(form)
382
383 return _security.render_template(
384 config_value("SEND_CONFIRMATION_TEMPLATE"),
385 send_confirmation_form=form,
386 **_ctx("send_confirmation")
387 )
388
389
390 def confirm_email(token):
391 """View function which handles a email confirmation request."""
392
393 expired, invalid, user = confirm_email_token_status(token)
394
395 if not user or invalid:
396 m, c = get_message("INVALID_CONFIRMATION_TOKEN")
397 if _security.redirect_behavior == "spa":
398 return redirect(get_url(_security.confirm_error_view, qparams={c: m}))
399 do_flash(m, c)
400 return redirect(
401 get_url(_security.confirm_error_view)
402 or url_for_security("send_confirmation")
403 )
404
405 already_confirmed = user.confirmed_at is not None
406
407 if expired or already_confirmed:
408 if already_confirmed:
409 m, c = get_message("ALREADY_CONFIRMED")
410 else:
411 send_confirmation_instructions(user)
412 m, c = get_message(
413 "CONFIRMATION_EXPIRED",
414 email=user.email,
415 within=_security.confirm_email_within,
416 )
417
418 if _security.redirect_behavior == "spa":
419 return redirect(
420 get_url(
421 _security.confirm_error_view,
422 qparams=user.get_redirect_qparams({c: m}),
423 )
424 )
425
426 do_flash(m, c)
427 return redirect(
428 get_url(_security.confirm_error_view)
429 or url_for_security("send_confirmation")
430 )
431
432 confirm_user(user)
433 after_this_request(_commit)
434
435 if user != current_user:
436 logout_user()
437 if config_value("AUTO_LOGIN_AFTER_CONFIRM"):
438 # N.B. this is a (small) security risk if email went to wrong place.
439 # and you have the LOGIN_WITH_CONFIRMATION flag since in that case
440 # you can be logged in and doing stuff - but another person could
441 # get the email.
442 if config_value("TWO_FACTOR") and config_value("TWO_FACTOR_REQUIRED"):
443 return tf_login(user, primary_authn_via="confirm")
444 login_user(user, authn_via=["confirm"])
445
446 m, c = get_message("EMAIL_CONFIRMED")
447 if _security.redirect_behavior == "spa":
448 return redirect(
449 get_url(
450 _security.post_confirm_view, qparams=user.get_redirect_qparams({c: m})
451 )
452 )
453 do_flash(m, c)
454 return redirect(
455 get_url(_security.post_confirm_view)
456 or get_url(
457 _security.post_login_view
458 if config_value("AUTO_LOGIN_AFTER_CONFIRM")
459 else _security.login_url
460 )
461 )
462
463
464 @anonymous_user_required
465 @unauth_csrf(fall_through=True)
466 def forgot_password():
467 """View function that handles a forgotten password request."""
468
469 form_class = _security.forgot_password_form
470
471 if request.is_json:
472 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
473 else:
474 form = form_class(meta=suppress_form_csrf())
475
476 if form.validate_on_submit():
477 send_reset_password_instructions(form.user)
478 if not _security._want_json(request):
479 do_flash(*get_message("PASSWORD_RESET_REQUEST", email=form.user.email))
480
481 if _security._want_json(request):
482 return base_render_json(form, include_user=False)
483
484 return _security.render_template(
485 config_value("FORGOT_PASSWORD_TEMPLATE"),
486 forgot_password_form=form,
487 **_ctx("forgot_password")
488 )
489
490
491 @anonymous_user_required
492 @unauth_csrf(fall_through=True)
493 def reset_password(token):
494 """View function that handles a reset password request.
495
496 This is usually called via GET as part of an email link and redirects to
497 a reset-password form
498 It is called via POST to actually update the password (and then redirects to
499 a post reset/login view)
500 If in either case the token is either invalid or expired it redirects to
501 the 'forgot-password' form.
502
503 In the case of non-form based configuration:
504 For GET normal case - redirect to RESET_VIEW?token={token}&email={email}
505 For GET invalid case - redirect to RESET_ERROR_VIEW?error={error}&email={email}
506 For POST normal/successful case - return 200 with new authentication token
507 For POST error case return 400 with form.errors
508 """
509
510 expired, invalid, user = reset_password_token_status(token)
511 form_class = _security.reset_password_form
512 if request.is_json:
513 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
514 else:
515 form = form_class(meta=suppress_form_csrf())
516 form.user = user
517
518 if request.method == "GET":
519 if not user or invalid:
520 m, c = get_message("INVALID_RESET_PASSWORD_TOKEN")
521 if _security.redirect_behavior == "spa":
522 return redirect(get_url(_security.reset_error_view, qparams={c: m}))
523 do_flash(m, c)
524 return redirect(url_for_security("forgot_password"))
525 if expired:
526 send_reset_password_instructions(user)
527 m, c = get_message(
528 "PASSWORD_RESET_EXPIRED",
529 email=user.email,
530 within=_security.reset_password_within,
531 )
532 if _security.redirect_behavior == "spa":
533 return redirect(
534 get_url(
535 _security.reset_error_view,
536 qparams=user.get_redirect_qparams({c: m}),
537 )
538 )
539 do_flash(m, c)
540 return redirect(url_for_security("forgot_password"))
541
542 # All good - for SPA - redirect to the ``reset_view``
543 if _security.redirect_behavior == "spa":
544 return redirect(
545 get_url(
546 _security.reset_view,
547 qparams=user.get_redirect_qparams({"token": token}),
548 )
549 )
550 # for forms - render the reset password form
551 return _security.render_template(
552 config_value("RESET_PASSWORD_TEMPLATE"),
553 reset_password_form=form,
554 reset_password_token=token,
555 **_ctx("reset_password")
556 )
557
558 # This is the POST case.
559 m = None
560 if not user or invalid:
561 invalid = True
562 m, c = get_message("INVALID_RESET_PASSWORD_TOKEN")
563 if not _security._want_json(request):
564 do_flash(m, c)
565
566 if expired:
567 send_reset_password_instructions(user)
568 m, c = get_message(
569 "PASSWORD_RESET_EXPIRED",
570 email=user.email,
571 within=_security.reset_password_within,
572 )
573 if not _security._want_json(request):
574 do_flash(m, c)
575
576 if invalid or expired:
577 if _security._want_json(request):
578 return _security._render_json(json_error_response(m), 400, None, None)
579 else:
580 return redirect(url_for_security("forgot_password"))
581
582 if form.validate_on_submit():
583 after_this_request(_commit)
584 update_password(user, form.password.data)
585 if config_value("TWO_FACTOR") and (
586 config_value("TWO_FACTOR_REQUIRED")
587 or (form.user.tf_totp_secret and form.user.tf_primary_method)
588 ):
589 return tf_login(user, primary_authn_via="reset")
590 login_user(user, authn_via=["reset"])
591 if _security._want_json(request):
592 login_form = _security.login_form(MultiDict({"email": user.email}))
593 setattr(login_form, "user", user)
594 return base_render_json(login_form, include_auth_token=True)
595 else:
596 do_flash(*get_message("PASSWORD_RESET"))
597 return redirect(
598 get_url(_security.post_reset_view) or get_url(_security.post_login_view)
599 )
600
601 # validation failure case - for forms - we try again including the token
602 # for non-forms - we just return errors and assume caller remembers token.
603 if _security._want_json(request):
604 return base_render_json(form)
605 return _security.render_template(
606 config_value("RESET_PASSWORD_TEMPLATE"),
607 reset_password_form=form,
608 reset_password_token=token,
609 **_ctx("reset_password")
610 )
611
612
613 @auth_required("basic", "token", "session")
614 def change_password():
615 """View function which handles a change password request."""
616
617 form_class = _security.change_password_form
618
619 if request.is_json:
620 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
621 else:
622 form = form_class(meta=suppress_form_csrf())
623
624 if form.validate_on_submit():
625 after_this_request(_commit)
626 change_user_password(current_user._get_current_object(), form.new_password.data)
627 if not _security._want_json(request):
628 do_flash(*get_message("PASSWORD_CHANGE"))
629 return redirect(
630 get_url(_security.post_change_view)
631 or get_url(_security.post_login_view)
632 )
633
634 if _security._want_json(request):
635 form.user = current_user
636 return base_render_json(form, include_auth_token=True)
637
638 return _security.render_template(
639 config_value("CHANGE_PASSWORD_TEMPLATE"),
640 change_password_form=form,
641 **_ctx("change_password")
642 )
643
644
645 @unauth_csrf(fall_through=True)
646 def two_factor_setup():
647 """View function for two-factor setup.
648
649 This is used both for GET to fetch forms and POST to actually set configuration
650 (and send token).
651
652 There are 3 cases for setting up:
653 1) initial login and application requires 2FA
654 2) changing existing 2FA information
655 3) user wanting to enable or disable 2FA (assuming application doesn't require it)
656
657 In order to CHANGE/ENABLE/DISABLE a 2FA information, user must be properly logged in
658 AND must perform a fresh password validation by
659 calling POST /tf-confirm (which sets 'tf_confirmed' in the session).
660
661 For initial login when 2FA required of course user can't be logged in - in this
662 case we need to have been sent some
663 state via the session as part of login to show a) who and b) that they successfully
664 authenticated.
665 """
666 form_class = _security.two_factor_setup_form
667
668 if request.is_json:
669 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
670 else:
671 form = form_class(meta=suppress_form_csrf())
672
673 if not current_user.is_authenticated:
674 # This is the initial login case
675 # We can also get here from setup if they want to change
676 if not all(k in session for k in ["tf_user_id", "tf_state"]) or session[
677 "tf_state"
678 ] not in ["setup_from_login", "validating_profile"]:
679 # illegal call on this endpoint
680 tf_clean_session()
681 return _tf_illegal_state(form, _security.login_url)
682
683 user = _datastore.get_user(session["tf_user_id"])
684 if not user:
685 tf_clean_session()
686 return _tf_illegal_state(form, _security.login_url)
687
688 else:
689 # all other cases require user to be logged in and have performed
690 # additional password verification as signified by 'tf_confirmed'
691 # in the session.
692 if "tf_confirmed" not in session:
693 tf_clean_session()
694 return _tf_illegal_state(form, _security.two_factor_confirm_url)
695 user = current_user
696
697 if form.validate_on_submit():
698 # Before storing in DB and therefore requiring 2FA we need to
699 # make sure it actually works.
700 # Requiring 2FA is triggered by having BOTH tf_totp_secret and
701 # tf_primary_method in the user record (or having the application
702 # global config TWO_FACTOR_REQUIRED)
703 # Until we correctly validate the 2FA - we don't set primary_method in
704 # user model but use the session to store it.
705 pm = form.setup.data
706 if pm == "disable":
707 tf_disable(user)
708 after_this_request(_commit)
709 do_flash(*get_message("TWO_FACTOR_DISABLED"))
710 if not _security._want_json(request):
711 return redirect(get_url(_security.post_login_view))
712 else:
713 return base_render_json(form)
714
715 # Regenerate the TOTP secret on every call of 2FA setup unless it is
716 # within the same session and method (e.g. upon entering the phone number)
717 if pm != session.get("tf_primary_method", None):
718 session["tf_totp_secret"] = _security._totp_factory.generate_totp_secret()
719
720 session["tf_primary_method"] = pm
721 session["tf_state"] = "validating_profile"
722 new_phone = form.phone.data if len(form.phone.data) > 0 else None
723 if new_phone:
724 user.tf_phone_number = new_phone
725 _datastore.put(user)
726 after_this_request(_commit)
727
728 # This form is sort of bizarre - for SMS and authenticator
729 # you select, then get more info, and submit again.
730 # For authenticator of course, we don't actually send anything
731 # and for SMS it is the second time around that we get the phone number
732 if pm == "email" or (pm == "sms" and new_phone):
733 msg = user.tf_send_security_token(
734 method=pm,
735 totp_secret=session["tf_totp_secret"],
736 phone_number=user.tf_phone_number,
737 )
738 if msg:
739 # send code didn't work
740 form.setup.errors = list()
741 form.setup.errors.append(msg)
742 if _security._want_json(request):
743 return base_render_json(
744 form, include_user=False, error_status_code=500
745 )
746 code_form = _security.two_factor_verify_code_form()
747 if not _security._want_json(request):
748 return _security.render_template(
749 config_value("TWO_FACTOR_SETUP_TEMPLATE"),
750 two_factor_setup_form=form,
751 two_factor_verify_code_form=code_form,
752 choices=config_value("TWO_FACTOR_ENABLED_METHODS"),
753 chosen_method=pm,
754 **_ctx("tf_setup")
755 )
756
757 # We get here on GET and POST with failed validation.
758 # For things like phone number - we've already done one POST
759 # that succeeded and now if failed - so retain the initial info
760 if _security._want_json(request):
761 return base_render_json(form, include_user=False)
762
763 code_form = _security.two_factor_verify_code_form()
764 choices = config_value("TWO_FACTOR_ENABLED_METHODS")
765 if not config_value("TWO_FACTOR_REQUIRED"):
766 choices.append("disable")
767
768 return _security.render_template(
769 config_value("TWO_FACTOR_SETUP_TEMPLATE"),
770 two_factor_setup_form=form,
771 two_factor_verify_code_form=code_form,
772 choices=choices,
773 chosen_method=form.setup.data,
774 two_factor_required=config_value("TWO_FACTOR_REQUIRED"),
775 **_ctx("tf_setup")
776 )
777
778
779 @unauth_csrf(fall_through=True)
780 def two_factor_token_validation():
781 """View function for two-factor token validation
782
783 Two cases:
784 1) normal login case - everything setup correctly; normal 2FA validation
785 In this case - user not logged in -
786 but 'tf_state' == 'ready' or 'validating_profile'
787 2) validating after CHANGE/ENABLE 2FA. In this case user logged in/authenticated
788 they must have 'tf_confirmed' set meaning they re-entered their passwd
789
790 """
791
792 form_class = _security.two_factor_verify_code_form
793
794 if request.is_json:
795 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
796 else:
797 form = form_class(meta=suppress_form_csrf())
798
799 changing = current_user.is_authenticated
800 if not changing:
801 # This is the normal login case
802 if (
803 not all(k in session for k in ["tf_user_id", "tf_state"])
804 or session["tf_state"] not in ["ready", "validating_profile"]
805 or (
806 session["tf_state"] == "validating_profile"
807 and "tf_primary_method" not in session
808 )
809 ):
810 # illegal call on this endpoint
811 tf_clean_session()
812 return _tf_illegal_state(form, _security.login_url)
813
814 user = _datastore.get_user(session["tf_user_id"])
815 form.user = user
816 if not user:
817 tf_clean_session()
818 return _tf_illegal_state(form, _security.login_url)
819
820 if session["tf_state"] == "ready":
821 pm = user.tf_primary_method
822 totp_secret = user.tf_totp_secret
823 else:
824 pm = session["tf_primary_method"]
825 totp_secret = session["tf_totp_secret"]
826 else:
827 if (
828 not all(
829 k in session for k in ["tf_confirmed", "tf_state", "tf_primary_method"]
830 )
831 or session["tf_state"] != "validating_profile"
832 ):
833 tf_clean_session()
834 # logout since this seems like attack-ish/logic error
835 logout_user()
836 return _tf_illegal_state(form, _security.login_url)
837 pm = session["tf_primary_method"]
838 totp_secret = session["tf_totp_secret"]
839 form.user = current_user
840
841 setattr(form, "primary_method", pm)
842 setattr(form, "tf_totp_secret", totp_secret)
843 if form.validate_on_submit():
844 # Success - log in user and clear all session variables
845 completion_message = complete_two_factor_process(
846 form.user, pm, totp_secret, changing, session.pop("tf_remember_login", None)
847 )
848 after_this_request(_commit)
849 if not _security._want_json(request):
850 do_flash(*get_message(completion_message))
851 return redirect(get_post_login_redirect())
852
853 # GET or not successful POST
854 if _security._want_json(request):
855 return base_render_json(form)
856
857 # if we were trying to validate a new method
858 if changing:
859 setup_form = _security.two_factor_setup_form()
860
861 return _security.render_template(
862 config_value("TWO_FACTOR_SETUP_TEMPLATE"),
863 two_factor_setup_form=setup_form,
864 two_factor_verify_code_form=form,
865 choices=config_value("TWO_FACTOR_ENABLED_METHODS"),
866 **_ctx("tf_setup")
867 )
868
869 # if we were trying to validate an existing method
870 else:
871 rescue_form = _security.two_factor_rescue_form()
872
873 return _security.render_template(
874 config_value("TWO_FACTOR_VERIFY_CODE_TEMPLATE"),
875 two_factor_rescue_form=rescue_form,
876 two_factor_verify_code_form=form,
877 problem=None,
878 **_ctx("tf_token_validation")
879 )
880
881
882 @anonymous_user_required
883 @unauth_csrf(fall_through=True)
884 def two_factor_rescue():
885 """ Function that handles a situation where user can't
886 enter his two-factor validation code
887
888 User must have already provided valid username/password.
889 User must have already established 2FA
890
891 """
892
893 form_class = _security.two_factor_rescue_form
894
895 if request.is_json:
896 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
897 else:
898 form = form_class(meta=suppress_form_csrf())
899
900 if (
901 not all(k in session for k in ["tf_user_id", "tf_state"])
902 or session["tf_state"] != "ready"
903 ):
904 tf_clean_session()
905 return _tf_illegal_state(form, _security.login_url)
906
907 user = _datastore.get_user(session["tf_user_id"])
908 form.user = user
909 if not user:
910 tf_clean_session()
911 return _tf_illegal_state(form, _security.login_url)
912
913 rproblem = ""
914 if form.validate_on_submit():
915 problem = form.data["help_setup"]
916 rproblem = problem
917 # if the problem is that user can't access his device, w
918 # e send him code through mail
919 if problem == "lost_device":
920 msg = form.user.tf_send_security_token(
921 method="email",
922 totp_secret=form.user.tf_totp_secret,
923 phone_number=form.user.tf_phone_number,
924 )
925 if msg:
926 rproblem = ""
927 form.help_setup.errors.append(msg)
928 if _security._want_json(request):
929 return base_render_json(
930 form, include_user=False, error_status_code=500
931 )
932 # send app provider a mail message regarding trouble
933 elif problem == "no_mail_access":
934 _security._send_mail(
935 config_value("EMAIL_SUBJECT_TWO_FACTOR_RESCUE"),
936 config_value("TWO_FACTOR_RESCUE_MAIL"),
937 "two_factor_rescue",
938 user=form.user,
939 )
940 else:
941 return "", 404
942
943 if _security._want_json(request):
944 return base_render_json(form, include_user=False)
945
946 code_form = _security.two_factor_verify_code_form()
947 return _security.render_template(
948 config_value("TWO_FACTOR_VERIFY_CODE_TEMPLATE"),
949 two_factor_verify_code_form=code_form,
950 two_factor_rescue_form=form,
951 rescue_mail=config_value("TWO_FACTOR_RESCUE_MAIL"),
952 problem=rproblem,
953 **_ctx("tf_token_validation")
954 )
955
956
957 @auth_required("basic", "session", "token")
958 def two_factor_verify_password():
959 """View function which handles a password verification request."""
960 form_class = _security.two_factor_verify_password_form
961
962 if request.is_json:
963 form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
964 else:
965 form = form_class(meta=suppress_form_csrf())
966
967 if form.validate_on_submit():
968 # form called verify_and_update_password()
969 after_this_request(_commit)
970 session["tf_confirmed"] = True
971 m, c = get_message("TWO_FACTOR_PASSWORD_CONFIRMATION_DONE")
972 if not _security._want_json(request):
973 do_flash(m, c)
974 return redirect(url_for_security("two_factor_setup"))
975 else:
976 return _security._render_json(json_error_response(m), 400, None, None)
977
978 if _security._want_json(request):
979 assert form.user == current_user
980 # form.user = current_user
981 return base_render_json(form)
982
983 return _security.render_template(
984 config_value("TWO_FACTOR_VERIFY_PASSWORD_TEMPLATE"),
985 two_factor_verify_password_form=form,
986 **_ctx("tf_verify_password")
987 )
988
989
990 @unauth_csrf(fall_through=True)
991 def two_factor_qrcode():
992 if current_user.is_authenticated:
993 user = current_user
994 else:
995 if "tf_user_id" not in session:
996 abort(404)
997 user = _datastore.get_user(session["tf_user_id"])
998 if not user:
999 # Seems like we should be careful here if user_id is gone.
1000 tf_clean_session()
1001 abort(404)
1002
1003 if "authenticator" not in config_value("TWO_FACTOR_ENABLED_METHODS"):
1004 return abort(404)
1005 if (
1006 "tf_primary_method" not in session
1007 or session["tf_primary_method"] != "authenticator"
1008 ):
1009 return abort(404)
1010
1011 totp = user.tf_totp_secret
1012 if "tf_totp_secret" in session:
1013 totp = session["tf_totp_secret"]
1014 try:
1015 import pyqrcode
1016
1017 # By convention, the URI should have the username that the user
1018 # logs in with.
1019 username = user.calc_username()
1020 url = pyqrcode.create(
1021 _security._totp_factory.get_totp_uri(
1022 username if username else "Unknown", totp
1023 )
1024 )
1025 except ImportError:
1026 # For TWO_FACTOR - this should have been checked at app init.
1027 raise
1028 from io import BytesIO
1029
1030 stream = BytesIO()
1031 url.svg(stream, scale=3)
1032 return (
1033 stream.getvalue(),
1034 200,
1035 {
1036 "Content-Type": "image/svg+xml",
1037 "Cache-Control": "no-cache, no-store, must-revalidate",
1038 "Pragma": "no-cache",
1039 "Expires": "0",
1040 },
1041 )
1042
1043
1044 def _tf_illegal_state(form, redirect_to):
1045 m, c = get_message("TWO_FACTOR_PERMISSION_DENIED")
1046 if not _security._want_json(request):
1047 do_flash(m, c)
1048 return redirect(get_url(redirect_to))
1049 else:
1050 return _security._render_json(json_error_response(m), 400, None, None)
1051
1052
1053 def create_blueprint(app, state, import_name, json_encoder=None):
1054 """Creates the security extension blueprint"""
1055
1056 bp = Blueprint(
1057 state.blueprint_name,
1058 import_name,
1059 url_prefix=state.url_prefix,
1060 subdomain=state.subdomain,
1061 template_folder="templates",
1062 )
1063 if json_encoder:
1064 bp.json_encoder = json_encoder
1065
1066 if state.logout_methods is not None:
1067 bp.route(state.logout_url, methods=state.logout_methods, endpoint="logout")(
1068 logout
1069 )
1070
1071 if state.passwordless:
1072 bp.route(state.login_url, methods=["GET", "POST"], endpoint="login")(send_login)
1073 bp.route(
1074 state.login_url + slash_url_suffix(state.login_url, "<token>"),
1075 endpoint="token_login",
1076 )(token_login)
1077 elif config_value("US_SIGNIN_REPLACES_LOGIN", app=app):
1078 bp.route(state.login_url, methods=["GET", "POST"], endpoint="login")(us_signin)
1079
1080 else:
1081 bp.route(state.login_url, methods=["GET", "POST"], endpoint="login")(login)
1082
1083 bp.route(state.verify_url, methods=["GET", "POST"], endpoint="verify")(verify)
1084
1085 if state.unified_signin:
1086 bp.route(state.us_signin_url, methods=["GET", "POST"], endpoint="us_signin")(
1087 us_signin
1088 )
1089 bp.route(
1090 state.us_signin_send_code_url,
1091 methods=["GET", "POST"],
1092 endpoint="us_signin_send_code",
1093 )(us_signin_send_code)
1094 bp.route(state.us_setup_url, methods=["GET", "POST"], endpoint="us_setup")(
1095 us_setup
1096 )
1097 bp.route(
1098 state.us_setup_url + slash_url_suffix(state.us_setup_url, "<token>"),
1099 methods=["GET", "POST"],
1100 endpoint="us_setup_validate",
1101 )(us_setup_validate)
1102
1103 # Freshness verification
1104 if config_value("FRESHNESS", app=app).total_seconds() >= 0:
1105 bp.route(
1106 state.us_verify_url, methods=["GET", "POST"], endpoint="us_verify"
1107 )(us_verify)
1108 bp.route(
1109 state.us_verify_send_code_url,
1110 methods=["GET", "POST"],
1111 endpoint="us_verify_send_code",
1112 )(us_verify_send_code)
1113
1114 bp.route(state.us_verify_link_url, methods=["GET"], endpoint="us_verify_link")(
1115 us_verify_link
1116 )
1117 bp.route(
1118 state.us_qrcode_url + slash_url_suffix(state.us_setup_url, "<token>"),
1119 endpoint="us_qrcode",
1120 )(us_qrcode)
1121
1122 if state.two_factor:
1123 tf_token_validation = "two_factor_token_validation"
1124 tf_qrcode = "two_factor_qrcode"
1125 bp.route(
1126 state.two_factor_setup_url,
1127 methods=["GET", "POST"],
1128 endpoint="two_factor_setup",
1129 )(two_factor_setup)
1130 bp.route(
1131 state.two_factor_token_validation_url,
1132 methods=["GET", "POST"],
1133 endpoint=tf_token_validation,
1134 )(two_factor_token_validation)
1135 bp.route(state.two_factor_qrcode_url, endpoint=tf_qrcode)(two_factor_qrcode)
1136 bp.route(
1137 state.two_factor_rescue_url,
1138 methods=["GET", "POST"],
1139 endpoint="two_factor_rescue",
1140 )(two_factor_rescue)
1141 bp.route(
1142 state.two_factor_confirm_url,
1143 methods=["GET", "POST"],
1144 endpoint="two_factor_verify_password",
1145 )(two_factor_verify_password)
1146
1147 if state.registerable:
1148 bp.route(state.register_url, methods=["GET", "POST"], endpoint="register")(
1149 register
1150 )
1151
1152 if state.recoverable:
1153 bp.route(state.reset_url, methods=["GET", "POST"], endpoint="forgot_password")(
1154 forgot_password
1155 )
1156 bp.route(
1157 state.reset_url + slash_url_suffix(state.reset_url, "<token>"),
1158 methods=["GET", "POST"],
1159 endpoint="reset_password",
1160 )(reset_password)
1161
1162 if state.changeable:
1163 bp.route(state.change_url, methods=["GET", "POST"], endpoint="change_password")(
1164 change_password
1165 )
1166
1167 if state.confirmable:
1168 bp.route(
1169 state.confirm_url, methods=["GET", "POST"], endpoint="send_confirmation"
1170 )(send_confirmation)
1171 bp.route(
1172 state.confirm_url + slash_url_suffix(state.confirm_url, "<token>"),
1173 methods=["GET", "POST"],
1174 endpoint="confirm_email",
1175 )(confirm_email)
1176
1177 return bp
00 #!/usr/bin/make -f
11
22 #export DH_VERBOSE = 1
3 export PYBUILD_BEFORE_TEST=cp -r debian/python-modules/* $(CURDIR)/.pybuild/cpython3_3.9/build/
4 export PYBUILD_AFTER_TEST=rm -rf $(CURDIR)/.pybuild/cpython3_3.9/build/flask_security
5 export PYBUILD_BEFORE_TEST=cp -r debian/python-modules/* $(CURDIR)/.pybuild/cpython3_3.9/build/faraday
6 export PYBUILD_AFTER_TEST=rm -rf $(CURDIR)/.pybuild/cpython3_3.9/build/faraday/flask_security
37 export PYBUILD_TEST_ARGS="-k-TestGetExploits -k-test_update_command -k-test_start_and_kill_faraday_server-k-test_create_agent_without_name_fails -k-test_create_agent_invalid_payload -k-test_vuln_filter_exception -k-test_vuln_restless_sort_by_ -k-test_vuln_web_filter_exception -k-test_filter_by_creator_command_id -k-test_openapi_format"
48
59 %:
0 debian/python-modules/flask_security/translations/de_DE/LC_MESSAGES/flask_security.mo
1 debian/python-modules/flask_security/translations/pt_PT/LC_MESSAGES/flask_security.mo
2 debian/python-modules/flask_security/translations/fr_FR/LC_MESSAGES/flask_security.mo
3 debian/python-modules/flask_security/translations/tr_TR/LC_MESSAGES/flask_security.mo
4 debian/python-modules/flask_security/translations/nl_NL/LC_MESSAGES/flask_security.mo
5 debian/python-modules/flask_security/translations/zh_Hans_CN/LC_MESSAGES/flask_security.mo
6 debian/python-modules/flask_security/translations/pt_BR/LC_MESSAGES/flask_security.mo
7 debian/python-modules/flask_security/translations/ru_RU/LC_MESSAGES/flask_security.mo
8 debian/python-modules/flask_security/translations/da_DK/LC_MESSAGES/flask_security.mo
9 debian/python-modules/flask_security/translations/ca_ES/LC_MESSAGES/flask_security.mo
10 debian/python-modules/flask_security/translations/ja_JP/LC_MESSAGES/flask_security.mo
11 debian/python-modules/flask_security/translations/es_ES/LC_MESSAGES/flask_security.mo