kinto 19.6.0__py3-none-any.whl → 20.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kinto might be problematic. Click here for more details.

Files changed (40) hide show
  1. kinto/__main__.py +0 -17
  2. kinto/config/kinto.tpl +0 -13
  3. kinto/contribute.json +27 -0
  4. kinto/core/initialization.py +0 -14
  5. kinto/core/storage/postgresql/pool.py +1 -1
  6. kinto/core/views/errors.py +2 -0
  7. kinto/plugins/accounts/__init__.py +2 -19
  8. kinto/plugins/accounts/authentication.py +8 -54
  9. kinto/plugins/accounts/utils.py +0 -133
  10. kinto/plugins/accounts/{views/__init__.py → views.py} +7 -62
  11. kinto/plugins/admin/VERSION +1 -1
  12. kinto/plugins/admin/build/VERSION +1 -1
  13. kinto/plugins/admin/build/assets/asn1-EdZsLKOL.js +1 -0
  14. kinto/plugins/admin/build/assets/index-Bq62Gei8.js +165 -0
  15. kinto/plugins/admin/build/assets/{index-BdpYyatM.css → index-Cs7JVwIg.css} +1 -1
  16. kinto/plugins/admin/build/assets/javascript-qCveANmP.js +1 -0
  17. kinto/plugins/admin/build/assets/mllike-CXdrOF99.js +1 -0
  18. kinto/plugins/admin/build/assets/sql-D0XecflT.js +1 -0
  19. kinto/plugins/admin/build/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  20. kinto/plugins/admin/build/index.html +2 -2
  21. kinto/views/contribute.py +12 -12
  22. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/METADATA +1 -3
  23. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/RECORD +27 -33
  24. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/WHEEL +1 -1
  25. kinto/plugins/accounts/mails.py +0 -96
  26. kinto/plugins/accounts/views/validation.py +0 -136
  27. kinto/plugins/admin/build/assets/asn1-CGOzndHr.js +0 -1
  28. kinto/plugins/admin/build/assets/index-n-QM_iZE.js +0 -165
  29. kinto/plugins/admin/build/assets/javascript-iSgyE4tI.js +0 -1
  30. kinto/plugins/admin/build/assets/mllike-C_8OmSiT.js +0 -1
  31. kinto/plugins/admin/build/assets/sql-C4g8LzGK.js +0 -1
  32. kinto/plugins/admin/build/assets/ttcn-cfg-BIkV9KBc.js +0 -1
  33. kinto/plugins/quotas/__init__.py +0 -21
  34. kinto/plugins/quotas/listener.py +0 -226
  35. kinto/plugins/quotas/scripts.py +0 -80
  36. kinto/plugins/quotas/utils.py +0 -7
  37. kinto/scripts.py +0 -41
  38. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/LICENSE +0 -0
  39. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/entry_points.txt +0 -0
  40. {kinto-19.6.0.dist-info → kinto-20.0.0.dist-info}/top_level.txt +0 -0
kinto/__main__.py CHANGED
@@ -9,7 +9,6 @@ from pyramid.paster import bootstrap
9
9
  from pyramid.scripts import pserve
10
10
 
11
11
  from kinto import __version__
12
- from kinto import scripts as kinto_scripts
13
12
  from kinto.config import init
14
13
  from kinto.core import scripts as core_scripts
15
14
  from kinto.plugins.accounts import scripts as accounts_scripts
@@ -33,7 +32,6 @@ def main(args=None):
33
32
  "migrate",
34
33
  "flush-cache",
35
34
  "version",
36
- "rebuild-quotas",
37
35
  "create-user",
38
36
  )
39
37
  subparsers = parser.add_subparsers(
@@ -107,16 +105,6 @@ def main(args=None):
107
105
  default=False,
108
106
  )
109
107
 
110
- elif command == "rebuild-quotas":
111
- subparser.add_argument(
112
- "--dry-run",
113
- action="store_true",
114
- help="Simulate the rebuild operation and show information",
115
- dest="dry_run",
116
- required=False,
117
- default=False,
118
- )
119
-
120
108
  elif command == "start":
121
109
  subparser.add_argument(
122
110
  "--reload",
@@ -214,11 +202,6 @@ def main(args=None):
214
202
  env = bootstrap(config_file, options={"command": "flush-cache"})
215
203
  core_scripts.flush_cache(env)
216
204
 
217
- elif which_command == "rebuild-quotas":
218
- dry_run = parsed_args["dry_run"]
219
- env = bootstrap(config_file, options={"command": "rebuild-quotas"})
220
- return kinto_scripts.rebuild_quotas(env, dry_run=dry_run)
221
-
222
205
  elif which_command == "create-user":
223
206
  username = parsed_args["username"]
224
207
  password = parsed_args["password"]
kinto/config/kinto.tpl CHANGED
@@ -36,7 +36,6 @@ kinto.includes = kinto.plugins.default_bucket
36
36
  kinto.plugins.admin
37
37
  kinto.plugins.accounts
38
38
  # kinto.plugins.history
39
- # kinto.plugins.quotas
40
39
 
41
40
  # Backends
42
41
  # https://kinto.readthedocs.io/en/latest/configuration/settings.html#storage
@@ -104,18 +103,6 @@ kinto.account_create_principals = system.Everyone
104
103
  kinto.account_write_principals = account:admin
105
104
  # Allow administrators to create buckets
106
105
  kinto.bucket_create_principals = account:admin
107
- # Enable the "account_validation" option.
108
- # kinto.account_validation = true
109
- # Set the sender for the validation email.
110
- # kinto.account_validation.email_sender = "admin@example.com"
111
- # Set the regular expression used to validate a proper email address.
112
- # kinto.account_validation.email_regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
113
-
114
- # Mail configuration (needed for the account validation option), see https://docs.pylonsproject.org/projects/pyramid_mailer/en/latest/#configuration
115
- # mail.host = localhost
116
- # mail.port = 25
117
- # mail.username = someusername
118
- # mail.password = somepassword
119
106
 
120
107
  # Notifications
121
108
  # https://kinto.readthedocs.io/en/latest/configuration/settings.html#notifications
kinto/contribute.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "Kinto",
3
+ "description": "Kinto is a minimalist JSON storage service with synchronisation and sharing abilities.",
4
+ "repository": {
5
+ "url": "https://github.com/Kinto/kinto",
6
+ "license": "Apache License (2.0)",
7
+ "tests": "https://github.com/Kinto/kinto/actions"
8
+ },
9
+ "participate": {
10
+ "home": "https://kinto.readthedocs.io/en/latest/community.html#how-to-contribute",
11
+ "docs": "https://kinto.readthedocs.io/",
12
+ "irc": "irc://irc.freenode.org/#kinto",
13
+ "irc-contacts": ["alexis", "leplatrem", "magopian", "natim", "NiKo`"]
14
+ },
15
+ "bugs": {
16
+ "list": "https://github.com/Kinto/kinto/issues",
17
+ "report": "https://github.com/Kinto/kinto/issues/new"
18
+ },
19
+ "keywords": [
20
+ "JSON",
21
+ "Python",
22
+ "Pyramid",
23
+ "REST",
24
+ "API",
25
+ "Database"
26
+ ]
27
+ }
@@ -334,16 +334,6 @@ def setup_sentry(config):
334
334
  config.add_subscriber(on_app_created, ApplicationCreated)
335
335
 
336
336
 
337
- def setup_statsd(config):
338
- # It would be pretty rare to find users that have a custom ``kinto.initialization_sequence`` setting.
339
- # But just in case, warn that it will be removed in next major.
340
- warnings.warn(
341
- "``setup_statsd()`` is now deprecated. Use ``kinto.core.initialization.setup_metrics()`` instead.",
342
- DeprecationWarning,
343
- )
344
- setup_metrics(config)
345
-
346
-
347
337
  def install_middlewares(app, settings):
348
338
  "Install a set of middlewares defined in the ini file on the given app."
349
339
  # Setup new-relic.
@@ -544,10 +534,6 @@ def setup_metrics(config):
544
534
 
545
535
  config.add_subscriber(on_new_response, NewResponse)
546
536
 
547
- # While statsd is deprecated, we include its plugin by default for retro-compability.
548
- if settings["statsd_url"]:
549
- config.include("kinto.plugins.statsd")
550
-
551
537
 
552
538
  class EventActionFilter:
553
539
  def __init__(self, actions, config):
@@ -21,7 +21,7 @@ class _QueueWithMaxBacklog(Queue):
21
21
  with self.mutex:
22
22
  self.cur_backlog += 1
23
23
  try:
24
- if self.max_backlog >= 0:
24
+ if self.max_backlog >= 0: # pragma: no branch
25
25
  if self.cur_backlog > self.max_backlog:
26
26
  block = False
27
27
  timeout = None
@@ -22,6 +22,8 @@ def authorization_required(response, request):
22
22
  """
23
23
  if Authenticated not in request.effective_principals:
24
24
  if response.content_type != "application/json":
25
+ # This is always the case when `HTTPForbidden` is raised by Pyramid
26
+ # on protected views with unauthenticated requests.
25
27
  error_msg = "Please authenticate yourself to use this endpoint."
26
28
  response = http_error(
27
29
  httpexceptions.HTTPUnauthorized(),
@@ -6,19 +6,12 @@ from pyramid.exceptions import ConfigurationError
6
6
  from kinto.authorization import PERMISSIONS_INHERITANCE_TREE
7
7
 
8
8
  from .authentication import AccountsAuthenticationPolicy as AccountsPolicy
9
- from .utils import (
10
- ACCOUNT_CACHE_KEY,
11
- ACCOUNT_POLICY_NAME,
12
- ACCOUNT_RESET_PASSWORD_CACHE_KEY,
13
- ACCOUNT_VALIDATION_CACHE_KEY,
14
- )
9
+ from .utils import ACCOUNT_CACHE_KEY, ACCOUNT_POLICY_NAME
15
10
 
16
11
 
17
12
  __all__ = [
18
13
  "ACCOUNT_CACHE_KEY",
19
14
  "ACCOUNT_POLICY_NAME",
20
- "ACCOUNT_RESET_PASSWORD_CACHE_KEY",
21
- "ACCOUNT_VALIDATION_CACHE_KEY",
22
15
  "AccountsPolicy",
23
16
  ]
24
17
 
@@ -27,16 +20,13 @@ DOCS_URL = "https://kinto.readthedocs.io/en/stable/api/1.x/accounts.html"
27
20
 
28
21
  def includeme(config):
29
22
  settings = config.get_settings()
30
- validation_enabled = settings.get("account_validation", False)
31
23
  config.add_api_capability(
32
24
  "accounts",
33
25
  description="Manage user accounts.",
34
26
  url="https://kinto.readthedocs.io/en/latest/api/1.x/accounts.html",
35
- validation_enabled=validation_enabled,
27
+ validation_enabled=False,
36
28
  )
37
29
  kwargs = {}
38
- if not validation_enabled:
39
- kwargs["ignore"] = "kinto.plugins.accounts.views.validation"
40
30
  config.scan("kinto.plugins.accounts.views", **kwargs)
41
31
 
42
32
  PERMISSIONS_INHERITANCE_TREE["root"].update({"account:create": {}})
@@ -45,13 +35,6 @@ def includeme(config):
45
35
  "read": {"account": ["write", "read"]},
46
36
  }
47
37
 
48
- if validation_enabled:
49
- # Valid mailers other than the default are `debug` and `testing`
50
- # according to
51
- # https://docs.pylonsproject.org/projects/pyramid_mailer/en/latest/#debugging
52
- mailer = settings.get("mail.mailer", "")
53
- config.include("pyramid_mailer" + (f".{mailer}" if mailer else ""))
54
-
55
38
  # Check that the account policy is mentioned in config if included.
56
39
  accountClass = "AccountsPolicy"
57
40
  policy = None
@@ -5,31 +5,27 @@ from kinto.core import utils
5
5
  from kinto.core.storage import exceptions as storage_exceptions
6
6
 
7
7
  from .utils import (
8
+ ACCOUNT_CACHE_KEY,
8
9
  ACCOUNT_POLICY_NAME,
9
- cache_account,
10
- delete_cached_reset_password,
11
- get_account_cache_key,
12
- get_cached_account,
13
- get_cached_reset_password,
14
- is_validated,
15
- refresh_cached_account,
16
10
  )
17
11
 
18
12
 
19
13
  def account_check(username, password, request):
20
14
  settings = request.registry.settings
21
- validation_enabled = settings.get("account_validation", False)
22
- cache_key = get_account_cache_key(username, request.registry)
15
+ hmac_secret = settings["userid_hmac_secret"]
16
+ cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_CACHE_KEY.format(username))
17
+ cache_ttl = int(settings.get("account_cache_ttl_seconds", 30))
23
18
  hashed_password = utils.hmac_digest(cache_key, password)
24
19
 
25
20
  # Check cache to see whether somebody has recently logged in with the same
26
21
  # username and password.
27
- cache_result = get_cached_account(username, request.registry)
22
+ cache = request.registry.cache
23
+ cache_result = cache.get(cache_key)
28
24
 
29
25
  # Username and password have been verified previously. No need to compare hashes
30
26
  if cache_result == hashed_password:
31
27
  # Refresh the cache TTL.
32
- refresh_cached_account(username, request.registry)
28
+ cache.expire(cache_key, cache_ttl)
33
29
  return True
34
30
 
35
31
  # Back to standard procedure
@@ -41,53 +37,11 @@ def account_check(username, password, request):
41
37
  except storage_exceptions.ObjectNotFoundError:
42
38
  return None
43
39
 
44
- if validation_enabled and not is_validated(existing):
45
- return None
46
-
47
40
  hashed = existing["password"].encode(encoding="utf-8")
48
41
  pwd_str = password.encode(encoding="utf-8")
49
42
  # Check if password is valid (it is a very expensive computation)
50
43
  if bcrypt.checkpw(pwd_str, hashed):
51
- cache_account(hashed_password, username, request.registry)
52
- return True
53
-
54
- # Last chance, is this a "reset password" flow?
55
- return reset_password_flow(username, password, request)
56
-
57
-
58
- def reset_password_flow(username, password, request):
59
- cache_key = get_account_cache_key(username, request.registry)
60
- hashed_password = utils.hmac_digest(cache_key, password)
61
- pwd_str = password.encode(encoding="utf-8")
62
-
63
- cached_password = get_cached_reset_password(username, request.registry)
64
- if not cached_password:
65
- return None
66
-
67
- # The temporary reset password is only available for changing a user's password.
68
- if request.method.lower() not in ["post", "put", "patch"]:
69
- return None
70
-
71
- # Only allow modifying a user account, no other resource.
72
- uri = utils.strip_uri_prefix(request.path)
73
- resource_name, _ = utils.view_lookup(request, uri)
74
- if resource_name != "account":
75
- return None
76
-
77
- try:
78
- data = request.json["data"]
79
- except (ValueError, KeyError):
80
- return None
81
-
82
- # Request one and only one data field: the `password`.
83
- if not data or "password" not in data or len(data.keys()) > 1:
84
- return None
85
-
86
- cached_password_str = cached_password.encode(encoding="utf-8")
87
- if bcrypt.checkpw(pwd_str, cached_password_str):
88
- # Remove the temporary reset password from the cache.
89
- delete_cached_reset_password(username, request.registry)
90
- cache_account(hashed_password, username, request.registry)
44
+ cache.set(cache_key, hashed_password, ttl=cache_ttl)
91
45
  return True
92
46
 
93
47
 
@@ -1,14 +1,8 @@
1
1
  import bcrypt
2
2
 
3
- from kinto.core import utils
4
-
5
3
 
6
4
  ACCOUNT_CACHE_KEY = "accounts:{}:verified"
7
5
  ACCOUNT_POLICY_NAME = "account"
8
- ACCOUNT_RESET_PASSWORD_CACHE_KEY = "accounts:{}:reset-password"
9
- ACCOUNT_VALIDATION_CACHE_KEY = "accounts:{}:validation-key"
10
- DEFAULT_RESET_PASSWORD_CACHE_TTL_SECONDS = 7 * 24 * 60 * 60
11
- DEFAULT_VALIDATION_KEY_CACHE_TTL_SECONDS = 7 * 24 * 60 * 60
12
6
 
13
7
 
14
8
  def hash_password(password):
@@ -17,130 +11,3 @@ def hash_password(password):
17
11
  pwd_str = password.encode(encoding="utf-8")
18
12
  hashed = bcrypt.hashpw(pwd_str, bcrypt.gensalt())
19
13
  return hashed.decode(encoding="utf-8")
20
-
21
-
22
- def is_validated(user):
23
- """Is this user record validated?"""
24
- # An account is "validated" if it has the `validated` field set to True, or
25
- # no `validated` field at all (for accounts created before the "account
26
- # validation option" was enabled).
27
- return user.get("validated", True)
28
-
29
-
30
- def get_account_cache_key(username, registry):
31
- """Given a username, return the cache key for this account."""
32
- settings = registry.settings
33
- hmac_secret = settings["userid_hmac_secret"]
34
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_CACHE_KEY.format(username))
35
- return cache_key
36
-
37
-
38
- def cache_reset_password(reset_password, username, registry):
39
- """Store a reset-password in the cache."""
40
- settings = registry.settings
41
- hmac_secret = settings["userid_hmac_secret"]
42
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_RESET_PASSWORD_CACHE_KEY.format(username))
43
- # Store a reset password for 7 days by default.
44
- cache_ttl = int(
45
- settings.get(
46
- "account_validation.reset_password_cache_ttl_seconds",
47
- DEFAULT_RESET_PASSWORD_CACHE_TTL_SECONDS,
48
- )
49
- )
50
-
51
- cache = registry.cache
52
- cache_result = cache.set(cache_key, reset_password, ttl=cache_ttl)
53
- return cache_result
54
-
55
-
56
- def get_cached_reset_password(username, registry):
57
- """Given a username, get the reset-password from the cache."""
58
- hmac_secret = registry.settings["userid_hmac_secret"]
59
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_RESET_PASSWORD_CACHE_KEY.format(username))
60
-
61
- cache = registry.cache
62
- cache_result = cache.get(cache_key)
63
- return cache_result
64
-
65
-
66
- def delete_cached_reset_password(username, registry):
67
- """Given a username, delete the reset-password from the cache."""
68
- hmac_secret = registry.settings["userid_hmac_secret"]
69
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_RESET_PASSWORD_CACHE_KEY.format(username))
70
-
71
- cache = registry.cache
72
- cache_result = cache.delete(cache_key)
73
- return cache_result
74
-
75
-
76
- def cache_validation_key(activation_key, username, registry):
77
- """Store a validation_key in the cache."""
78
- settings = registry.settings
79
- hmac_secret = settings["userid_hmac_secret"]
80
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_VALIDATION_CACHE_KEY.format(username))
81
- # Store an activation key for 7 days by default.
82
- cache_ttl = int(
83
- settings.get(
84
- "account_validation.validation_key_cache_ttl_seconds",
85
- DEFAULT_VALIDATION_KEY_CACHE_TTL_SECONDS,
86
- )
87
- )
88
-
89
- cache = registry.cache
90
- cache_result = cache.set(cache_key, activation_key, ttl=cache_ttl)
91
- return cache_result
92
-
93
-
94
- def get_cached_validation_key(username, registry):
95
- """Given a username, get the validation key from the cache."""
96
- hmac_secret = registry.settings["userid_hmac_secret"]
97
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_VALIDATION_CACHE_KEY.format(username))
98
- cache = registry.cache
99
- activation_key = cache.get(cache_key)
100
- return activation_key
101
-
102
-
103
- def delete_cached_validation_key(username, registry):
104
- """Given a username, delete the validation key from the cache."""
105
- hmac_secret = registry.settings["userid_hmac_secret"]
106
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_VALIDATION_CACHE_KEY.format(username))
107
- cache = registry.cache
108
- cache_result = cache.delete(cache_key)
109
- return cache_result
110
-
111
-
112
- def cache_account(hashed_password, username, registry):
113
- """Store an authenticated account in the cache."""
114
- settings = registry.settings
115
- cache_ttl = int(settings.get("account_cache_ttl_seconds", 30))
116
- cache_key = get_account_cache_key(username, registry)
117
- cache = registry.cache
118
- cache_result = cache.set(cache_key, hashed_password, ttl=cache_ttl)
119
- return cache_result
120
-
121
-
122
- def get_cached_account(username, registry):
123
- """Given a username, get the account from the cache."""
124
- cache_key = get_account_cache_key(username, registry)
125
- cache = registry.cache
126
- cached_account = cache.get(cache_key)
127
- return cached_account
128
-
129
-
130
- def refresh_cached_account(username, registry):
131
- """Given a username, refresh the cache TTL."""
132
- settings = registry.settings
133
- cache_ttl = int(settings.get("account_cache_ttl_seconds", 30))
134
- cache_key = get_account_cache_key(username, registry)
135
- cache = registry.cache
136
- cache_result = cache.expire(cache_key, cache_ttl)
137
- return cache_result
138
-
139
-
140
- def delete_cached_account(username, registry):
141
- """Given a username, delete the account key from the cache."""
142
- hmac_secret = registry.settings["userid_hmac_secret"]
143
- cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_CACHE_KEY.format(username))
144
- cache = registry.cache
145
- cache_result = cache.delete(cache_key)
146
- return cache_result
@@ -1,6 +1,3 @@
1
- import re
2
- import uuid
3
-
4
1
  import colander
5
2
  from pyramid import httpexceptions
6
3
  from pyramid.authorization import Authenticated, Everyone
@@ -8,22 +5,12 @@ from pyramid.decorator import reify
8
5
  from pyramid.events import subscriber
9
6
  from pyramid.settings import aslist
10
7
 
11
- from kinto.core import resource
8
+ from kinto.core import resource, utils
12
9
  from kinto.core.errors import http_error, raise_invalid
13
10
  from kinto.core.events import ACTIONS, ResourceChanged
14
11
  from kinto.views import NameGenerator
15
12
 
16
- from ..mails import Emailer
17
- from ..utils import (
18
- ACCOUNT_POLICY_NAME,
19
- cache_validation_key,
20
- delete_cached_account,
21
- get_cached_validation_key,
22
- hash_password,
23
- )
24
-
25
-
26
- DEFAULT_EMAIL_REGEXP = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
13
+ from .utils import ACCOUNT_CACHE_KEY, ACCOUNT_POLICY_NAME, hash_password
27
14
 
28
15
 
29
16
  def _extract_posted_body_id(request):
@@ -63,13 +50,6 @@ class Account(resource.Resource):
63
50
  )
64
51
  # Shortcut to check if current is anonymous (before get_parent_id()).
65
52
  context.is_anonymous = Authenticated not in request.effective_principals
66
- # Is the "accounts validation" setting set?
67
- context.validation_enabled = settings.get("account_validation", False)
68
- # Account validation requires the user id to be an email.
69
- validation_email_regexp = settings.get(
70
- "account_validation.email_regexp", DEFAULT_EMAIL_REGEXP
71
- )
72
- context.validation_email_regexp = re.compile(validation_email_regexp)
73
53
 
74
54
  super().__init__(request, context)
75
55
 
@@ -122,26 +102,6 @@ class Account(resource.Resource):
122
102
  error_details = {"name": "data.id", "description": "Accounts must have an ID."}
123
103
  raise_invalid(self.request, **error_details)
124
104
 
125
- # Account validation requires that the record ID is an email address.
126
- # TODO: this might be better suited for a schema. Do we have a way to
127
- # dynamically change the schema according to the settings?
128
- if self.context.validation_enabled and old is None:
129
- email_regexp = self.context.validation_email_regexp
130
- # Account validation requires that the record ID is an email address.
131
- user_email = new[self.model.id_field]
132
- if not email_regexp.match(user_email):
133
- error_details = {
134
- "name": "data.id",
135
- "description": f"Account validation is enabled, and user id should match {email_regexp}",
136
- }
137
- raise_invalid(self.request, **error_details)
138
-
139
- activation_key = str(uuid.uuid4())
140
- new["validated"] = False
141
-
142
- # Store the activation key in the cache to be used in the `validate` endpoint.
143
- cache_validation_key(activation_key, new["id"], self.request.registry)
144
-
145
105
  # Administrators can reach other accounts and anonymous have no
146
106
  # selected_userid. So do not try to enforce.
147
107
  if self.context.is_administrator or self.context.is_anonymous:
@@ -164,28 +124,13 @@ class Account(resource.Resource):
164
124
  )
165
125
  def on_account_changed(event):
166
126
  request = event.request
127
+ cache = request.registry.cache
128
+ settings = request.registry.settings
129
+ hmac_secret = settings["userid_hmac_secret"]
167
130
 
168
131
  for obj in event.impacted_objects:
169
132
  # Extract username and password from current user
170
133
  username = obj["old"]["id"]
134
+ cache_key = utils.hmac_digest(hmac_secret, ACCOUNT_CACHE_KEY.format(username))
171
135
  # Delete cache
172
- delete_cached_account(username, request.registry)
173
-
174
-
175
- # Send activation code by email on account creation if account validation is enabled.
176
- @subscriber(ResourceChanged, for_resources=("account",), for_actions=(ACTIONS.CREATE,))
177
- def on_account_created(event):
178
- request = event.request
179
- settings = request.registry.settings
180
- if not settings.get("account_validation", False):
181
- return
182
-
183
- for impacted_object in event.impacted_objects:
184
- account = impacted_object["new"]
185
- user_email = account["id"]
186
- activation_key = get_cached_validation_key(user_email, request.registry)
187
- if activation_key is None:
188
- continue
189
-
190
- # Send an email to the user with the link to activate their account.
191
- Emailer(request, account).send_activation(activation_key)
136
+ cache.delete(cache_key)
@@ -1 +1 @@
1
- 3.5.1
1
+ 3.6.0
@@ -1 +1 @@
1
- 3.5.1
1
+ 3.6.0
@@ -0,0 +1 @@
1
+ function u(i){for(var s={},c=i.split(" "),T=0;T<c.length;++T)s[c[T]]=!0;return s}const o={keywords:u("DEFINITIONS OBJECTS IF DERIVED INFORMATION ACTION REPLY ANY NAMED CHARACTERIZED BEHAVIOUR REGISTERED WITH AS IDENTIFIED CONSTRAINED BY PRESENT BEGIN IMPORTS FROM UNITS SYNTAX MIN-ACCESS MAX-ACCESS MINACCESS MAXACCESS REVISION STATUS DESCRIPTION SEQUENCE SET COMPONENTS OF CHOICE DistinguishedName ENUMERATED SIZE MODULE END INDEX AUGMENTS EXTENSIBILITY IMPLIED EXPORTS"),cmipVerbs:u("ACTIONS ADD GET NOTIFICATIONS REPLACE REMOVE"),compareTypes:u("OPTIONAL DEFAULT MANAGED MODULE-TYPE MODULE_IDENTITY MODULE-COMPLIANCE OBJECT-TYPE OBJECT-IDENTITY OBJECT-COMPLIANCE MODE CONFIRMED CONDITIONAL SUBORDINATE SUPERIOR CLASS TRUE FALSE NULL TEXTUAL-CONVENTION"),status:u("current deprecated mandatory obsolete"),tags:u("APPLICATION AUTOMATIC EXPLICIT IMPLICIT PRIVATE TAGS UNIVERSAL"),storage:u("BOOLEAN INTEGER OBJECT IDENTIFIER BIT OCTET STRING UTCTime InterfaceIndex IANAifType CMIP-Attribute REAL PACKAGE PACKAGES IpAddress PhysAddress NetworkAddress BITS BMPString TimeStamp TimeTicks TruthValue RowStatus DisplayString GeneralString GraphicString IA5String NumericString PrintableString SnmpAdminString TeletexString UTF8String VideotexString VisibleString StringStore ISO646String T61String UniversalString Unsigned32 Integer32 Gauge Gauge32 Counter Counter32 Counter64"),modifier:u("ATTRIBUTE ATTRIBUTES MANDATORY-GROUP MANDATORY-GROUPS GROUP GROUPS ELEMENTS EQUALITY ORDERING SUBSTRINGS DEFINED"),accessTypes:u("not-accessible accessible-for-notify read-only read-create read-write"),multiLineStrings:!0};function g(i){var s=i.keywords||o.keywords,c=i.cmipVerbs||o.cmipVerbs,T=i.compareTypes||o.compareTypes,N=i.status||o.status,d=i.tags||o.tags,f=i.storage||o.storage,m=i.modifier||o.modifier,C=i.accessTypes||o.accessTypes;i.multiLineStrings||o.multiLineStrings;var R=i.indentStatements!==!1,A=/[\|\^]/,E;function y(e,n){var t=e.next();if(t=='"'||t=="'")return n.tokenize=D(t),n.tokenize(e,n);if(/[\[\]\(\){}:=,;]/.test(t))return E=t,"punctuation";if(t=="-"&&e.eat("-"))return e.skipToEnd(),"comment";if(/\d/.test(t))return e.eatWhile(/[\w\.]/),"number";if(A.test(t))return e.eatWhile(A),"operator";e.eatWhile(/[\w\-]/);var r=e.current();return s.propertyIsEnumerable(r)?"keyword":c.propertyIsEnumerable(r)?"variableName":T.propertyIsEnumerable(r)?"atom":N.propertyIsEnumerable(r)?"comment":d.propertyIsEnumerable(r)?"typeName":f.propertyIsEnumerable(r)||m.propertyIsEnumerable(r)||C.propertyIsEnumerable(r)?"modifier":"variableName"}function D(e){return function(n,t){for(var r=!1,S,O=!1;(S=n.next())!=null;){if(S==e&&!r){var I=n.peek();I&&(I=I.toLowerCase(),(I=="b"||I=="h"||I=="o")&&n.next()),O=!0;break}r=!r&&S=="\\"}return O&&(t.tokenize=null),"string"}}function p(e,n,t,r,S){this.indented=e,this.column=n,this.type=t,this.align=r,this.prev=S}function a(e,n,t){var r=e.indented;return e.context&&e.context.type=="statement"&&(r=e.context.indented),e.context=new p(r,n,t,null,e.context)}function l(e){var n=e.context.type;return(n==")"||n=="]"||n=="}")&&(e.indented=e.context.indented),e.context=e.context.prev}return{name:"asn1",startState:function(){return{tokenize:null,context:new p(-2,0,"top",!1),indented:0,startOfLine:!0}},token:function(e,n){var t=n.context;if(e.sol()&&(t.align==null&&(t.align=!1),n.indented=e.indentation(),n.startOfLine=!0),e.eatSpace())return null;E=null;var r=(n.tokenize||y)(e,n);if(r=="comment")return r;if(t.align==null&&(t.align=!0),(E==";"||E==":"||E==",")&&t.type=="statement")l(n);else if(E=="{")a(n,e.column(),"}");else if(E=="[")a(n,e.column(),"]");else if(E=="(")a(n,e.column(),")");else if(E=="}"){for(;t.type=="statement";)t=l(n);for(t.type=="}"&&(t=l(n));t.type=="statement";)t=l(n)}else E==t.type?l(n):R&&((t.type=="}"||t.type=="top")&&E!=";"||t.type=="statement"&&E=="newstatement")&&a(n,e.column(),"statement");return n.startOfLine=!1,r},languageData:{indentOnInput:/^\s*[{}]$/,commentTokens:{line:"--"}}}}export{g as asn1};