We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
exception in accounts plugin authentication: Unicode-objects must be encoded before hashing
Running a fresh docker image built from master (36dbcc0) with postgresql storage

After:
```
echo '{"data": {"password": "PASSWD"}}' | http PUT http://localhost:8889/v1/accounts/eric
```
Running
```
http GET http://localhost:8889/v1/ --auth eric:coucou3333
```
Triggers following exception :
```
Unicode-objects must be encoded before hashing 
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/pyramid/tweens.py", line 22, in excview_tween
    response = handler(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 119, in tm_tween
    reraise(*exc_info)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/compat.py", line 15, in reraise
    raise value
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 98, in tm_tween
    response = handler(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/router.py", line 155, in handle_request
    view_name
  File "/usr/local/lib/python3.5/dist-packages/pyramid/view.py", line 612, in _call_view
    response = view_callable(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/config/views.py", line 181, in __call__
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 389, in attr_view
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 367, in predicate_wrapper
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 438, in rendered_view
    result = view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 147, in _requestonly_view
    response = view(request)
  File "/usr/local/lib/python3.5/dist-packages/cornice/service.py", line 493, in wrapper
    response = view_(request)
  File "/code/kinto/core/views/hello.py", line 45, in get_hello
    if Authenticated in request.effective_principals:
  File "/usr/local/lib/python3.5/dist-packages/pyramid/security.py", line 375, in effective_principals
    return policy.effective_principals(self)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_multiauth/__init__.py", line 119, in effective_principals
    userid = policy.authenticated_userid(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 92, in authenticated_userid
    callback_ok = self.callback(userid, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 1123, in callback
    return self.check(username, password, request)
  File "/code/kinto/plugins/accounts/authentication.py", line 18, in account_check
    if hashed == bcrypt.hashpw(pwd_str, hashed):
  File "/usr/local/lib/python3.5/dist-packages/bcrypt/__init__.py", line 62, in hashpw
    raise TypeError("Unicode-objects must be encoded before hashing")
```

It is fixed by encoding the value received from the session at [this line in authentication.py](https://github.com/Kinto/kinto/blob/master/kinto/plugins/accounts/authentication.py#L16):
```
 hashed = existing['password'].encode(encoding='utf8')
```

exception in accounts plugin authentication: Unicode-objects must be encoded before hashing
Running a fresh docker image built from master (36dbcc0) with postgresql storage

After:
```
echo '{"data": {"password": "PASSWD"}}' | http PUT http://localhost:8889/v1/accounts/eric
```
Running
```
http GET http://localhost:8889/v1/ --auth eric:coucou3333
```
Triggers following exception :
```
Unicode-objects must be encoded before hashing 
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/pyramid/tweens.py", line 22, in excview_tween
    response = handler(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 119, in tm_tween
    reraise(*exc_info)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/compat.py", line 15, in reraise
    raise value
  File "/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py", line 98, in tm_tween
    response = handler(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/router.py", line 155, in handle_request
    view_name
  File "/usr/local/lib/python3.5/dist-packages/pyramid/view.py", line 612, in _call_view
    response = view_callable(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/config/views.py", line 181, in __call__
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 389, in attr_view
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 367, in predicate_wrapper
    return view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 438, in rendered_view
    result = view(context, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py", line 147, in _requestonly_view
    response = view(request)
  File "/usr/local/lib/python3.5/dist-packages/cornice/service.py", line 493, in wrapper
    response = view_(request)
  File "/code/kinto/core/views/hello.py", line 45, in get_hello
    if Authenticated in request.effective_principals:
  File "/usr/local/lib/python3.5/dist-packages/pyramid/security.py", line 375, in effective_principals
    return policy.effective_principals(self)
  File "/usr/local/lib/python3.5/dist-packages/pyramid_multiauth/__init__.py", line 119, in effective_principals
    userid = policy.authenticated_userid(request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 92, in authenticated_userid
    callback_ok = self.callback(userid, request)
  File "/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py", line 1123, in callback
    return self.check(username, password, request)
  File "/code/kinto/plugins/accounts/authentication.py", line 18, in account_check
    if hashed == bcrypt.hashpw(pwd_str, hashed):
  File "/usr/local/lib/python3.5/dist-packages/bcrypt/__init__.py", line 62, in hashpw
    raise TypeError("Unicode-objects must be encoded before hashing")
```

It is fixed by encoding the value received from the session at [this line in authentication.py](https://github.com/Kinto/kinto/blob/master/kinto/plugins/accounts/authentication.py#L16):
```
 hashed = existing['password'].encode(encoding='utf8')
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kinto/plugins/accounts/authentication.py`
Content:
```
1 import bcrypt
2 from pyramid import authentication as base_auth
3 
4 from kinto.core.storage import exceptions as storage_exceptions
5 
6 
7 def account_check(username, password, request):
8     parent_id = username
9     try:
10         existing = request.registry.storage.get(parent_id=parent_id,
11                                                 collection_id='account',
12                                                 object_id=username)
13     except storage_exceptions.RecordNotFoundError:
14         return None
15 
16     hashed = existing['password']
17     pwd_str = password.encode(encoding='utf-8')
18     if hashed == bcrypt.hashpw(pwd_str, hashed):
19         return True  # Match! Return anything but None.
20 
21 
22 class AccountsAuthenticationPolicy(base_auth.BasicAuthAuthenticationPolicy):
23     """Accounts authentication policy.
24 
25     It will check that the credentials exist in the account resource.
26     """
27     def __init__(self, *args, **kwargs):
28         super().__init__(account_check, *args, **kwargs)
29 
30     def effective_principals(self, request):
31         # Bypass default Pyramid construction of principals because
32         # Pyramid multiauth already adds userid, Authenticated and Everyone
33         # principals.
34         return []
35
```

Path: `kinto/plugins/accounts/views.py`
Content:
```
1 import bcrypt
2 import colander
3 from pyramid import httpexceptions
4 from pyramid.decorator import reify
5 from pyramid.security import Authenticated, Everyone
6 from pyramid.settings import aslist
7 
8 from kinto.views import NameGenerator
9 from kinto.core import resource
10 from kinto.core.errors import raise_invalid, http_error
11 
12 
13 class AccountSchema(resource.ResourceSchema):
14     password = colander.SchemaNode(colander.String())
15 
16 
17 @resource.register()
18 class Account(resource.ShareableResource):
19 
20     schema = AccountSchema
21 
22     def __init__(self, request, context):
23         # Store if current user is administrator (before accessing get_parent_id())
24         allowed_from_settings = request.registry.settings.get('account_write_principals', [])
25         context.is_administrator = len(set(aslist(allowed_from_settings)) &
26                                        set(request.prefixed_principals)) > 0
27         # Shortcut to check if current is anonymous (before get_parent_id()).
28         context.is_anonymous = Authenticated not in request.effective_principals
29 
30         super().__init__(request, context)
31 
32         # Overwrite the current principal set by ShareableResource.
33         if self.model.current_principal == Everyone:
34             # Creation is anonymous, but author with write perm is this:
35             # XXX: only works if policy name is account in settings.
36             self.model.current_principal = 'account:{}'.format(self.model.parent_id)
37 
38     @reify
39     def id_generator(self):
40         # This generator is used for ID validation.
41         return NameGenerator()
42 
43     def get_parent_id(self, request):
44         # The whole challenge here is that we want to isolate what
45         # authenticated users can list, but give access to everything to
46         # administrators.
47         # Plus when anonymous create accounts, we have to set their parent id
48         # to the same value they would obtain when authenticated.
49         if self.context.is_administrator:
50             if self.context.on_collection:
51                 # Admin see all accounts.
52                 return '*'
53             else:
54                 # No pattern matching for admin on single record.
55                 return request.matchdict['id']
56 
57         if not self.context.is_anonymous:
58             # Authenticated users see their own account only.
59             return request.selected_userid
60 
61         # Anonymous creation with PUT.
62         if 'id' in request.matchdict:
63             return request.matchdict['id']
64 
65         try:
66             # Anonymous creation with POST.
67             return request.json['data']['id']
68         except (ValueError, KeyError):
69             # Bad POST data.
70             if request.method.lower() == 'post':
71                 error_details = {
72                     'name': 'data.id',
73                     'description': 'data.id in body: Required'
74                 }
75                 raise_invalid(request, **error_details)
76             # Anonymous GET
77             error_msg = 'Cannot read accounts.'
78             raise http_error(httpexceptions.HTTPUnauthorized(), error=error_msg)
79 
80     def collection_post(self):
81         result = super(Account, self).collection_post()
82         if self.context.is_anonymous and self.request.response.status_code == 200:
83             error_details = {
84                 'message': 'Account ID %r already exists' % result['data']['id']
85             }
86             raise http_error(httpexceptions.HTTPForbidden(), **error_details)
87         return result
88 
89     def process_record(self, new, old=None):
90         new = super(Account, self).process_record(new, old)
91 
92         # Store password safely in database.
93         pwd_str = new["password"].encode(encoding='utf-8')
94         new["password"] = bcrypt.hashpw(pwd_str, bcrypt.gensalt())
95 
96         # Administrators can reach other accounts and anonymous have no
97         # selected_userid. So do not try to enforce.
98         if self.context.is_administrator or self.context.is_anonymous:
99             return new
100 
101         # Otherwise, we force the id to match the authenticated username.
102         if new[self.model.id_field] != self.request.selected_userid:
103             error_details = {
104                 'name': 'data.id',
105                 'description': 'Username and account ID do not match.',
106             }
107             raise_invalid(self.request, **error_details)
108 
109         return new
110
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kinto/plugins/accounts/authentication.py b/kinto/plugins/accounts/authentication.py
--- a/kinto/plugins/accounts/authentication.py
+++ b/kinto/plugins/accounts/authentication.py
@@ -13,7 +13,7 @@
     except storage_exceptions.RecordNotFoundError:
         return None
 
-    hashed = existing['password']
+    hashed = existing['password'].encode(encoding='utf-8')
     pwd_str = password.encode(encoding='utf-8')
     if hashed == bcrypt.hashpw(pwd_str, hashed):
         return True  # Match! Return anything but None.
diff --git a/kinto/plugins/accounts/views.py b/kinto/plugins/accounts/views.py
--- a/kinto/plugins/accounts/views.py
+++ b/kinto/plugins/accounts/views.py
@@ -89,9 +89,11 @@
     def process_record(self, new, old=None):
         new = super(Account, self).process_record(new, old)
 
-        # Store password safely in database.
+        # Store password safely in database as str
+        # (bcrypt.hashpw returns base64 bytes).
         pwd_str = new["password"].encode(encoding='utf-8')
-        new["password"] = bcrypt.hashpw(pwd_str, bcrypt.gensalt())
+        hashed = bcrypt.hashpw(pwd_str, bcrypt.gensalt())
+        new["password"] = hashed.decode(encoding='utf-8')
 
         # Administrators can reach other accounts and anonymous have no
         # selected_userid. So do not try to enforce.

{"golden_diff": "diff --git a/kinto/plugins/accounts/authentication.py b/kinto/plugins/accounts/authentication.py\n--- a/kinto/plugins/accounts/authentication.py\n+++ b/kinto/plugins/accounts/authentication.py\n@@ -13,7 +13,7 @@\n     except storage_exceptions.RecordNotFoundError:\n         return None\n \n-    hashed = existing['password']\n+    hashed = existing['password'].encode(encoding='utf-8')\n     pwd_str = password.encode(encoding='utf-8')\n     if hashed == bcrypt.hashpw(pwd_str, hashed):\n         return True  # Match! Return anything but None.\ndiff --git a/kinto/plugins/accounts/views.py b/kinto/plugins/accounts/views.py\n--- a/kinto/plugins/accounts/views.py\n+++ b/kinto/plugins/accounts/views.py\n@@ -89,9 +89,11 @@\n     def process_record(self, new, old=None):\n         new = super(Account, self).process_record(new, old)\n \n-        # Store password safely in database.\n+        # Store password safely in database as str\n+        # (bcrypt.hashpw returns base64 bytes).\n         pwd_str = new[\"password\"].encode(encoding='utf-8')\n-        new[\"password\"] = bcrypt.hashpw(pwd_str, bcrypt.gensalt())\n+        hashed = bcrypt.hashpw(pwd_str, bcrypt.gensalt())\n+        new[\"password\"] = hashed.decode(encoding='utf-8')\n \n         # Administrators can reach other accounts and anonymous have no\n         # selected_userid. So do not try to enforce.\n", "issue": "exception in accounts plugin authentication: Unicode-objects must be encoded before hashing\nRunning a fresh docker image built from master (36dbcc0) with postgresql storage\r\n\r\nAfter:\r\n```\r\necho '{\"data\": {\"password\": \"PASSWD\"}}' | http PUT http://localhost:8889/v1/accounts/eric\r\n```\r\nRunning\r\n```\r\nhttp GET http://localhost:8889/v1/ --auth eric:coucou3333\r\n```\r\nTriggers following exception :\r\n```\r\nUnicode-objects must be encoded before hashing \r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/tweens.py\", line 22, in excview_tween\r\n    response = handler(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py\", line 119, in tm_tween\r\n    reraise(*exc_info)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/compat.py\", line 15, in reraise\r\n    raise value\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py\", line 98, in tm_tween\r\n    response = handler(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/router.py\", line 155, in handle_request\r\n    view_name\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/view.py\", line 612, in _call_view\r\n    response = view_callable(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/config/views.py\", line 181, in __call__\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 389, in attr_view\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 367, in predicate_wrapper\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 438, in rendered_view\r\n    result = view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 147, in _requestonly_view\r\n    response = view(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/cornice/service.py\", line 493, in wrapper\r\n    response = view_(request)\r\n  File \"/code/kinto/core/views/hello.py\", line 45, in get_hello\r\n    if Authenticated in request.effective_principals:\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/security.py\", line 375, in effective_principals\r\n    return policy.effective_principals(self)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_multiauth/__init__.py\", line 119, in effective_principals\r\n    userid = policy.authenticated_userid(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py\", line 92, in authenticated_userid\r\n    callback_ok = self.callback(userid, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py\", line 1123, in callback\r\n    return self.check(username, password, request)\r\n  File \"/code/kinto/plugins/accounts/authentication.py\", line 18, in account_check\r\n    if hashed == bcrypt.hashpw(pwd_str, hashed):\r\n  File \"/usr/local/lib/python3.5/dist-packages/bcrypt/__init__.py\", line 62, in hashpw\r\n    raise TypeError(\"Unicode-objects must be encoded before hashing\")\r\n```\r\n\r\nIt is fixed by encoding the value received from the session at [this line in authentication.py](https://github.com/Kinto/kinto/blob/master/kinto/plugins/accounts/authentication.py#L16):\r\n```\r\n hashed = existing['password'].encode(encoding='utf8')\r\n```\r\n\nexception in accounts plugin authentication: Unicode-objects must be encoded before hashing\nRunning a fresh docker image built from master (36dbcc0) with postgresql storage\r\n\r\nAfter:\r\n```\r\necho '{\"data\": {\"password\": \"PASSWD\"}}' | http PUT http://localhost:8889/v1/accounts/eric\r\n```\r\nRunning\r\n```\r\nhttp GET http://localhost:8889/v1/ --auth eric:coucou3333\r\n```\r\nTriggers following exception :\r\n```\r\nUnicode-objects must be encoded before hashing \r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/tweens.py\", line 22, in excview_tween\r\n    response = handler(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py\", line 119, in tm_tween\r\n    reraise(*exc_info)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/compat.py\", line 15, in reraise\r\n    raise value\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_tm/__init__.py\", line 98, in tm_tween\r\n    response = handler(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/router.py\", line 155, in handle_request\r\n    view_name\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/view.py\", line 612, in _call_view\r\n    response = view_callable(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/config/views.py\", line 181, in __call__\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 389, in attr_view\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 367, in predicate_wrapper\r\n    return view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 438, in rendered_view\r\n    result = view(context, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/viewderivers.py\", line 147, in _requestonly_view\r\n    response = view(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/cornice/service.py\", line 493, in wrapper\r\n    response = view_(request)\r\n  File \"/code/kinto/core/views/hello.py\", line 45, in get_hello\r\n    if Authenticated in request.effective_principals:\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/security.py\", line 375, in effective_principals\r\n    return policy.effective_principals(self)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid_multiauth/__init__.py\", line 119, in effective_principals\r\n    userid = policy.authenticated_userid(request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py\", line 92, in authenticated_userid\r\n    callback_ok = self.callback(userid, request)\r\n  File \"/usr/local/lib/python3.5/dist-packages/pyramid/authentication.py\", line 1123, in callback\r\n    return self.check(username, password, request)\r\n  File \"/code/kinto/plugins/accounts/authentication.py\", line 18, in account_check\r\n    if hashed == bcrypt.hashpw(pwd_str, hashed):\r\n  File \"/usr/local/lib/python3.5/dist-packages/bcrypt/__init__.py\", line 62, in hashpw\r\n    raise TypeError(\"Unicode-objects must be encoded before hashing\")\r\n```\r\n\r\nIt is fixed by encoding the value received from the session at [this line in authentication.py](https://github.com/Kinto/kinto/blob/master/kinto/plugins/accounts/authentication.py#L16):\r\n```\r\n hashed = existing['password'].encode(encoding='utf8')\r\n```\r\n\n", "before_files": [{"content": "import bcrypt\nfrom pyramid import authentication as base_auth\n\nfrom kinto.core.storage import exceptions as storage_exceptions\n\n\ndef account_check(username, password, request):\n    parent_id = username\n    try:\n        existing = request.registry.storage.get(parent_id=parent_id,\n                                                collection_id='account',\n                                                object_id=username)\n    except storage_exceptions.RecordNotFoundError:\n        return None\n\n    hashed = existing['password']\n    pwd_str = password.encode(encoding='utf-8')\n    if hashed == bcrypt.hashpw(pwd_str, hashed):\n        return True  # Match! Return anything but None.\n\n\nclass AccountsAuthenticationPolicy(base_auth.BasicAuthAuthenticationPolicy):\n    \"\"\"Accounts authentication policy.\n\n    It will check that the credentials exist in the account resource.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super().__init__(account_check, *args, **kwargs)\n\n    def effective_principals(self, request):\n        # Bypass default Pyramid construction of principals because\n        # Pyramid multiauth already adds userid, Authenticated and Everyone\n        # principals.\n        return []\n", "path": "kinto/plugins/accounts/authentication.py"}, {"content": "import bcrypt\nimport colander\nfrom pyramid import httpexceptions\nfrom pyramid.decorator import reify\nfrom pyramid.security import Authenticated, Everyone\nfrom pyramid.settings import aslist\n\nfrom kinto.views import NameGenerator\nfrom kinto.core import resource\nfrom kinto.core.errors import raise_invalid, http_error\n\n\nclass AccountSchema(resource.ResourceSchema):\n    password = colander.SchemaNode(colander.String())\n\n\n@resource.register()\nclass Account(resource.ShareableResource):\n\n    schema = AccountSchema\n\n    def __init__(self, request, context):\n        # Store if current user is administrator (before accessing get_parent_id())\n        allowed_from_settings = request.registry.settings.get('account_write_principals', [])\n        context.is_administrator = len(set(aslist(allowed_from_settings)) &\n                                       set(request.prefixed_principals)) > 0\n        # Shortcut to check if current is anonymous (before get_parent_id()).\n        context.is_anonymous = Authenticated not in request.effective_principals\n\n        super().__init__(request, context)\n\n        # Overwrite the current principal set by ShareableResource.\n        if self.model.current_principal == Everyone:\n            # Creation is anonymous, but author with write perm is this:\n            # XXX: only works if policy name is account in settings.\n            self.model.current_principal = 'account:{}'.format(self.model.parent_id)\n\n    @reify\n    def id_generator(self):\n        # This generator is used for ID validation.\n        return NameGenerator()\n\n    def get_parent_id(self, request):\n        # The whole challenge here is that we want to isolate what\n        # authenticated users can list, but give access to everything to\n        # administrators.\n        # Plus when anonymous create accounts, we have to set their parent id\n        # to the same value they would obtain when authenticated.\n        if self.context.is_administrator:\n            if self.context.on_collection:\n                # Admin see all accounts.\n                return '*'\n            else:\n                # No pattern matching for admin on single record.\n                return request.matchdict['id']\n\n        if not self.context.is_anonymous:\n            # Authenticated users see their own account only.\n            return request.selected_userid\n\n        # Anonymous creation with PUT.\n        if 'id' in request.matchdict:\n            return request.matchdict['id']\n\n        try:\n            # Anonymous creation with POST.\n            return request.json['data']['id']\n        except (ValueError, KeyError):\n            # Bad POST data.\n            if request.method.lower() == 'post':\n                error_details = {\n                    'name': 'data.id',\n                    'description': 'data.id in body: Required'\n                }\n                raise_invalid(request, **error_details)\n            # Anonymous GET\n            error_msg = 'Cannot read accounts.'\n            raise http_error(httpexceptions.HTTPUnauthorized(), error=error_msg)\n\n    def collection_post(self):\n        result = super(Account, self).collection_post()\n        if self.context.is_anonymous and self.request.response.status_code == 200:\n            error_details = {\n                'message': 'Account ID %r already exists' % result['data']['id']\n            }\n            raise http_error(httpexceptions.HTTPForbidden(), **error_details)\n        return result\n\n    def process_record(self, new, old=None):\n        new = super(Account, self).process_record(new, old)\n\n        # Store password safely in database.\n        pwd_str = new[\"password\"].encode(encoding='utf-8')\n        new[\"password\"] = bcrypt.hashpw(pwd_str, bcrypt.gensalt())\n\n        # Administrators can reach other accounts and anonymous have no\n        # selected_userid. So do not try to enforce.\n        if self.context.is_administrator or self.context.is_anonymous:\n            return new\n\n        # Otherwise, we force the id to match the authenticated username.\n        if new[self.model.id_field] != self.request.selected_userid:\n            error_details = {\n                'name': 'data.id',\n                'description': 'Username and account ID do not match.',\n            }\n            raise_invalid(self.request, **error_details)\n\n        return new\n", "path": "kinto/plugins/accounts/views.py"}], "after_files": [{"content": "import bcrypt\nfrom pyramid import authentication as base_auth\n\nfrom kinto.core.storage import exceptions as storage_exceptions\n\n\ndef account_check(username, password, request):\n    parent_id = username\n    try:\n        existing = request.registry.storage.get(parent_id=parent_id,\n                                                collection_id='account',\n                                                object_id=username)\n    except storage_exceptions.RecordNotFoundError:\n        return None\n\n    hashed = existing['password'].encode(encoding='utf-8')\n    pwd_str = password.encode(encoding='utf-8')\n    if hashed == bcrypt.hashpw(pwd_str, hashed):\n        return True  # Match! Return anything but None.\n\n\nclass AccountsAuthenticationPolicy(base_auth.BasicAuthAuthenticationPolicy):\n    \"\"\"Accounts authentication policy.\n\n    It will check that the credentials exist in the account resource.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super().__init__(account_check, *args, **kwargs)\n\n    def effective_principals(self, request):\n        # Bypass default Pyramid construction of principals because\n        # Pyramid multiauth already adds userid, Authenticated and Everyone\n        # principals.\n        return []\n", "path": "kinto/plugins/accounts/authentication.py"}, {"content": "import bcrypt\nimport colander\nfrom pyramid import httpexceptions\nfrom pyramid.decorator import reify\nfrom pyramid.security import Authenticated, Everyone\nfrom pyramid.settings import aslist\n\nfrom kinto.views import NameGenerator\nfrom kinto.core import resource\nfrom kinto.core.errors import raise_invalid, http_error\n\n\nclass AccountSchema(resource.ResourceSchema):\n    password = colander.SchemaNode(colander.String())\n\n\n@resource.register()\nclass Account(resource.ShareableResource):\n\n    schema = AccountSchema\n\n    def __init__(self, request, context):\n        # Store if current user is administrator (before accessing get_parent_id())\n        allowed_from_settings = request.registry.settings.get('account_write_principals', [])\n        context.is_administrator = len(set(aslist(allowed_from_settings)) &\n                                       set(request.prefixed_principals)) > 0\n        # Shortcut to check if current is anonymous (before get_parent_id()).\n        context.is_anonymous = Authenticated not in request.effective_principals\n\n        super().__init__(request, context)\n\n        # Overwrite the current principal set by ShareableResource.\n        if self.model.current_principal == Everyone:\n            # Creation is anonymous, but author with write perm is this:\n            # XXX: only works if policy name is account in settings.\n            self.model.current_principal = 'account:{}'.format(self.model.parent_id)\n\n    @reify\n    def id_generator(self):\n        # This generator is used for ID validation.\n        return NameGenerator()\n\n    def get_parent_id(self, request):\n        # The whole challenge here is that we want to isolate what\n        # authenticated users can list, but give access to everything to\n        # administrators.\n        # Plus when anonymous create accounts, we have to set their parent id\n        # to the same value they would obtain when authenticated.\n        if self.context.is_administrator:\n            if self.context.on_collection:\n                # Admin see all accounts.\n                return '*'\n            else:\n                # No pattern matching for admin on single record.\n                return request.matchdict['id']\n\n        if not self.context.is_anonymous:\n            # Authenticated users see their own account only.\n            return request.selected_userid\n\n        # Anonymous creation with PUT.\n        if 'id' in request.matchdict:\n            return request.matchdict['id']\n\n        try:\n            # Anonymous creation with POST.\n            return request.json['data']['id']\n        except (ValueError, KeyError):\n            # Bad POST data.\n            if request.method.lower() == 'post':\n                error_details = {\n                    'name': 'data.id',\n                    'description': 'data.id in body: Required'\n                }\n                raise_invalid(request, **error_details)\n            # Anonymous GET\n            error_msg = 'Cannot read accounts.'\n            raise http_error(httpexceptions.HTTPUnauthorized(), error=error_msg)\n\n    def collection_post(self):\n        result = super(Account, self).collection_post()\n        if self.context.is_anonymous and self.request.response.status_code == 200:\n            error_details = {\n                'message': 'Account ID %r already exists' % result['data']['id']\n            }\n            raise http_error(httpexceptions.HTTPForbidden(), **error_details)\n        return result\n\n    def process_record(self, new, old=None):\n        new = super(Account, self).process_record(new, old)\n\n        # Store password safely in database as str\n        # (bcrypt.hashpw returns base64 bytes).\n        pwd_str = new[\"password\"].encode(encoding='utf-8')\n        hashed = bcrypt.hashpw(pwd_str, bcrypt.gensalt())\n        new[\"password\"] = hashed.decode(encoding='utf-8')\n\n        # Administrators can reach other accounts and anonymous have no\n        # selected_userid. So do not try to enforce.\n        if self.context.is_administrator or self.context.is_anonymous:\n            return new\n\n        # Otherwise, we force the id to match the authenticated username.\n        if new[self.model.id_field] != self.request.selected_userid:\n            error_details = {\n                'name': 'data.id',\n                'description': 'Username and account ID do not match.',\n            }\n            raise_invalid(self.request, **error_details)\n\n        return new\n", "path": "kinto/plugins/accounts/views.py"}]}