We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API tokens: Remove @token and pypi: cases
At some point in the future (perhaps at the end of the API token beta?), support for `@token` as a token username and `pypi:` as a token prefix should end in favor of `__token__` and `pypi-`, respectively.

See #6287, #6342.

cc @brainwane @di @ewdurbin @dstufft 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `warehouse/macaroons/services.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 import datetime
14 import json
15 import uuid
16 
17 import pymacaroons
18 
19 from pymacaroons.exceptions import MacaroonDeserializationException
20 from sqlalchemy.orm import joinedload
21 from sqlalchemy.orm.exc import NoResultFound
22 from zope.interface import implementer
23 
24 from warehouse.accounts.models import User
25 from warehouse.macaroons.caveats import InvalidMacaroon, Verifier
26 from warehouse.macaroons.interfaces import IMacaroonService
27 from warehouse.macaroons.models import Macaroon
28 
29 
30 @implementer(IMacaroonService)
31 class DatabaseMacaroonService:
32     def __init__(self, db_session):
33         self.db = db_session
34 
35     def _extract_raw_macaroon(self, prefixed_macaroon):
36         """
37         Returns the base64-encoded macaroon component of a PyPI macaroon,
38         dropping the prefix.
39 
40         Returns None if the macaroon is None, has no prefix, or has the
41         wrong prefix.
42         """
43         if prefixed_macaroon is None:
44             return None
45 
46         prefix, split, raw_macaroon = prefixed_macaroon.partition("-")
47         # TODO: Remove ':' as an acceptable delimiter for tokens (GH-6345)
48         if prefix != "pypi" or not split:
49             prefix, _, raw_macaroon = prefixed_macaroon.partition(":")
50 
51         if prefix != "pypi":
52             return None
53 
54         return raw_macaroon
55 
56     def find_macaroon(self, macaroon_id):
57         """
58         Returns a macaroon model from the DB by its identifier.
59         Returns None if no macaroon has the given ID.
60         """
61         try:
62             dm = (
63                 self.db.query(Macaroon)
64                 .options(joinedload("user"))
65                 .filter(Macaroon.id == uuid.UUID(macaroon_id))
66                 .one()
67             )
68         except NoResultFound:
69             return None
70 
71         return dm
72 
73     def find_userid(self, raw_macaroon):
74         """
75         Returns the id of the user associated with the given raw (serialized)
76         macaroon.
77         """
78         raw_macaroon = self._extract_raw_macaroon(raw_macaroon)
79         if raw_macaroon is None:
80             return None
81 
82         try:
83             m = pymacaroons.Macaroon.deserialize(raw_macaroon)
84         except MacaroonDeserializationException:
85             return None
86 
87         dm = self.find_macaroon(m.identifier.decode())
88 
89         if dm is None:
90             return None
91 
92         return dm.user.id
93 
94     def verify(self, raw_macaroon, context, principals, permission):
95         """
96         Returns True if the given raw (serialized) macaroon is
97         valid for the context, principals, and requested permission.
98 
99         Raises InvalidMacaroon if the macaroon is not valid.
100         """
101         raw_macaroon = self._extract_raw_macaroon(raw_macaroon)
102         if raw_macaroon is None:
103             raise InvalidMacaroon("malformed or nonexistent macaroon")
104 
105         try:
106             m = pymacaroons.Macaroon.deserialize(raw_macaroon)
107         except MacaroonDeserializationException:
108             raise InvalidMacaroon("malformed macaroon")
109 
110         dm = self.find_macaroon(m.identifier.decode())
111 
112         if dm is None:
113             raise InvalidMacaroon("deleted or nonexistent macaroon")
114 
115         verifier = Verifier(m, context, principals, permission)
116         if verifier.verify(dm.key):
117             dm.last_used = datetime.datetime.now()
118             return True
119 
120         raise InvalidMacaroon("invalid macaroon")
121 
122     def create_macaroon(self, location, user_id, description, caveats):
123         """
124         Returns a tuple of a new raw (serialized) macaroon and its DB model.
125         The description provided is not embedded into the macaroon, only stored
126         in the DB model.
127         """
128         user = self.db.query(User).filter(User.id == user_id).one()
129 
130         dm = Macaroon(user=user, description=description, caveats=caveats)
131         self.db.add(dm)
132         self.db.flush()
133 
134         m = pymacaroons.Macaroon(
135             location=location,
136             identifier=str(dm.id),
137             key=dm.key,
138             version=pymacaroons.MACAROON_V2,
139         )
140         m.add_first_party_caveat(json.dumps(caveats))
141         serialized_macaroon = f"pypi-{m.serialize()}"
142         return serialized_macaroon, dm
143 
144     def delete_macaroon(self, macaroon_id):
145         """
146         Deletes a macaroon from the DB by its identifier.
147         """
148         dm = self.find_macaroon(macaroon_id)
149         self.db.delete(dm)
150         self.db.flush()
151 
152     def get_macaroon_by_description(self, user_id, description):
153         """
154         Returns a macaroon model from the DB with the given description,
155         if one exists for the given user.
156 
157         Returns None if the user doesn't have a macaroon with this description.
158         """
159         try:
160             dm = (
161                 self.db.query(Macaroon)
162                 .options(joinedload("user"))
163                 .filter(Macaroon.description == description)
164                 .filter(Macaroon.user_id == user_id)
165                 .one()
166             )
167         except NoResultFound:
168             return None
169 
170         return dm
171 
172 
173 def database_macaroon_factory(context, request):
174     return DatabaseMacaroonService(request.db)
175
```

Path: `warehouse/macaroons/auth_policy.py`
Content:
```
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12 
13 import base64
14 
15 from pyramid.authentication import CallbackAuthenticationPolicy
16 from pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy
17 from pyramid.security import Denied
18 from pyramid.threadlocal import get_current_request
19 from zope.interface import implementer
20 
21 from warehouse.cache.http import add_vary_callback
22 from warehouse.macaroons.interfaces import IMacaroonService
23 from warehouse.macaroons.services import InvalidMacaroon
24 
25 
26 def _extract_basic_macaroon(auth):
27     """
28     A helper function for extracting a macaroon from a
29     HTTP Basic Authentication-style header.
30 
31     Returns None if the header doesn't contain a structurally
32     valid macaroon, or the candidate (not yet verified) macaroon
33     in a serialized form.
34     """
35     try:
36         authorization = base64.b64decode(auth).decode()
37         auth_method, _, auth = authorization.partition(":")
38     except ValueError:
39         return None
40 
41     # TODO: Remove @token as an acceptable token username (GH-6345)
42     if auth_method != "@token" and auth_method != "__token__":
43         return None
44 
45     return auth
46 
47 
48 def _extract_http_macaroon(request):
49     """
50     A helper function for the extraction of HTTP Macaroon from a given request.
51     Returns either a None if no macaroon could be found, or the string
52     that represents our serialized macaroon.
53     """
54     authorization = request.headers.get("Authorization")
55     if not authorization:
56         return None
57 
58     try:
59         auth_method, auth = authorization.split(" ", 1)
60     except ValueError:
61         return None
62 
63     if auth_method.lower() == "basic":
64         return _extract_basic_macaroon(auth)
65     elif auth_method.lower() == "token":
66         return auth
67 
68     return None
69 
70 
71 @implementer(IAuthenticationPolicy)
72 class MacaroonAuthenticationPolicy(CallbackAuthenticationPolicy):
73     def __init__(self, callback=None):
74         self.callback = callback
75 
76     def unauthenticated_userid(self, request):
77         # If we're calling into this API on a request, then we want to register
78         # a callback which will ensure that the response varies based on the
79         # Authorization header.
80         request.add_response_callback(add_vary_callback("Authorization"))
81 
82         # We need to extract our Macaroon from the request.
83         macaroon = _extract_http_macaroon(request)
84         if macaroon is None:
85             return None
86 
87         # Check to see if our Macaroon exists in the database, and if so
88         # fetch the user that is associated with it.
89         macaroon_service = request.find_service(IMacaroonService, context=None)
90         userid = macaroon_service.find_userid(macaroon)
91         if userid is not None:
92             return str(userid)
93 
94     def remember(self, request, userid, **kw):
95         # This is a NO-OP because our Macaroon header policy doesn't allow
96         # the ability for authentication to "remember" the user id. This
97         # assumes it has been configured in clients somewhere out of band.
98         return []
99 
100     def forget(self, request):
101         # This is a NO-OP because our Macaroon header policy doesn't allow
102         # the ability for authentication to "forget" the user id. This
103         # assumes it has been configured in clients somewhere out of band.
104         return []
105 
106 
107 @implementer(IAuthorizationPolicy)
108 class MacaroonAuthorizationPolicy:
109     def __init__(self, policy):
110         self.policy = policy
111 
112     def permits(self, context, principals, permission):
113         # The Pyramid API doesn't let us access the request here, so we have to pull it
114         # out of the thread local instead.
115         # TODO: Work with Pyramid devs to figure out if there is a better way to support
116         #       the worklow we are using here or not.
117         request = get_current_request()
118 
119         # Our request could possibly be a None, if there isn't an active request, in
120         # that case we're going to always deny, because without a request, we can't
121         # determine if this request is authorized or not.
122         if request is None:
123             return Denied("There was no active request.")
124 
125         # Re-extract our Macaroon from the request, it sucks to have to do this work
126         # twice, but I believe it is inevitable unless we pass the Macaroon back as
127         # a principal-- which doesn't seem to be the right fit for it.
128         macaroon = _extract_http_macaroon(request)
129 
130         # This logic will only happen on requests that are being authenticated with
131         # Macaroons. Any other request will just fall back to the standard Authorization
132         # policy.
133         if macaroon is not None:
134             macaroon_service = request.find_service(IMacaroonService, context=None)
135 
136             try:
137                 macaroon_service.verify(macaroon, context, principals, permission)
138             except InvalidMacaroon as exc:
139                 return Denied(f"The supplied token was invalid: {str(exc)!r}")
140 
141         # If our Macaroon is verified, then we'll pass this request to our underlying
142         # Authorization policy, so it can handle its own authorization logic on
143         # the prinicpal.
144         return self.policy.permits(context, principals, permission)
145 
146     def principals_allowed_by_permission(self, context, permission):
147         # We just dispatch this, because Macaroons don't restrict what principals are
148         # allowed by a particular permission, they just restrict specific requests
149         # to not have that permission.
150         return self.policy.principals_allowed_by_permission(context, permission)
151
```
--- 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/warehouse/macaroons/auth_policy.py b/warehouse/macaroons/auth_policy.py
--- a/warehouse/macaroons/auth_policy.py
+++ b/warehouse/macaroons/auth_policy.py
@@ -38,8 +38,7 @@
     except ValueError:
         return None
 
-    # TODO: Remove @token as an acceptable token username (GH-6345)
-    if auth_method != "@token" and auth_method != "__token__":
+    if auth_method != "__token__":
         return None
 
     return auth
diff --git a/warehouse/macaroons/services.py b/warehouse/macaroons/services.py
--- a/warehouse/macaroons/services.py
+++ b/warehouse/macaroons/services.py
@@ -43,12 +43,8 @@
         if prefixed_macaroon is None:
             return None
 
-        prefix, split, raw_macaroon = prefixed_macaroon.partition("-")
-        # TODO: Remove ':' as an acceptable delimiter for tokens (GH-6345)
-        if prefix != "pypi" or not split:
-            prefix, _, raw_macaroon = prefixed_macaroon.partition(":")
-
-        if prefix != "pypi":
+        prefix, _, raw_macaroon = prefixed_macaroon.partition("-")
+        if prefix != "pypi" or not raw_macaroon:
             return None
 
         return raw_macaroon

{"golden_diff": "diff --git a/warehouse/macaroons/auth_policy.py b/warehouse/macaroons/auth_policy.py\n--- a/warehouse/macaroons/auth_policy.py\n+++ b/warehouse/macaroons/auth_policy.py\n@@ -38,8 +38,7 @@\n     except ValueError:\n         return None\n \n-    # TODO: Remove @token as an acceptable token username (GH-6345)\n-    if auth_method != \"@token\" and auth_method != \"__token__\":\n+    if auth_method != \"__token__\":\n         return None\n \n     return auth\ndiff --git a/warehouse/macaroons/services.py b/warehouse/macaroons/services.py\n--- a/warehouse/macaroons/services.py\n+++ b/warehouse/macaroons/services.py\n@@ -43,12 +43,8 @@\n         if prefixed_macaroon is None:\n             return None\n \n-        prefix, split, raw_macaroon = prefixed_macaroon.partition(\"-\")\n-        # TODO: Remove ':' as an acceptable delimiter for tokens (GH-6345)\n-        if prefix != \"pypi\" or not split:\n-            prefix, _, raw_macaroon = prefixed_macaroon.partition(\":\")\n-\n-        if prefix != \"pypi\":\n+        prefix, _, raw_macaroon = prefixed_macaroon.partition(\"-\")\n+        if prefix != \"pypi\" or not raw_macaroon:\n             return None\n \n         return raw_macaroon\n", "issue": "API tokens: Remove @token and pypi: cases\nAt some point in the future (perhaps at the end of the API token beta?), support for `@token` as a token username and `pypi:` as a token prefix should end in favor of `__token__` and `pypi-`, respectively.\r\n\r\nSee #6287, #6342.\r\n\r\ncc @brainwane @di @ewdurbin @dstufft \r\n\r\n\n", "before_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nimport json\nimport uuid\n\nimport pymacaroons\n\nfrom pymacaroons.exceptions import MacaroonDeserializationException\nfrom sqlalchemy.orm import joinedload\nfrom sqlalchemy.orm.exc import NoResultFound\nfrom zope.interface import implementer\n\nfrom warehouse.accounts.models import User\nfrom warehouse.macaroons.caveats import InvalidMacaroon, Verifier\nfrom warehouse.macaroons.interfaces import IMacaroonService\nfrom warehouse.macaroons.models import Macaroon\n\n\n@implementer(IMacaroonService)\nclass DatabaseMacaroonService:\n    def __init__(self, db_session):\n        self.db = db_session\n\n    def _extract_raw_macaroon(self, prefixed_macaroon):\n        \"\"\"\n        Returns the base64-encoded macaroon component of a PyPI macaroon,\n        dropping the prefix.\n\n        Returns None if the macaroon is None, has no prefix, or has the\n        wrong prefix.\n        \"\"\"\n        if prefixed_macaroon is None:\n            return None\n\n        prefix, split, raw_macaroon = prefixed_macaroon.partition(\"-\")\n        # TODO: Remove ':' as an acceptable delimiter for tokens (GH-6345)\n        if prefix != \"pypi\" or not split:\n            prefix, _, raw_macaroon = prefixed_macaroon.partition(\":\")\n\n        if prefix != \"pypi\":\n            return None\n\n        return raw_macaroon\n\n    def find_macaroon(self, macaroon_id):\n        \"\"\"\n        Returns a macaroon model from the DB by its identifier.\n        Returns None if no macaroon has the given ID.\n        \"\"\"\n        try:\n            dm = (\n                self.db.query(Macaroon)\n                .options(joinedload(\"user\"))\n                .filter(Macaroon.id == uuid.UUID(macaroon_id))\n                .one()\n            )\n        except NoResultFound:\n            return None\n\n        return dm\n\n    def find_userid(self, raw_macaroon):\n        \"\"\"\n        Returns the id of the user associated with the given raw (serialized)\n        macaroon.\n        \"\"\"\n        raw_macaroon = self._extract_raw_macaroon(raw_macaroon)\n        if raw_macaroon is None:\n            return None\n\n        try:\n            m = pymacaroons.Macaroon.deserialize(raw_macaroon)\n        except MacaroonDeserializationException:\n            return None\n\n        dm = self.find_macaroon(m.identifier.decode())\n\n        if dm is None:\n            return None\n\n        return dm.user.id\n\n    def verify(self, raw_macaroon, context, principals, permission):\n        \"\"\"\n        Returns True if the given raw (serialized) macaroon is\n        valid for the context, principals, and requested permission.\n\n        Raises InvalidMacaroon if the macaroon is not valid.\n        \"\"\"\n        raw_macaroon = self._extract_raw_macaroon(raw_macaroon)\n        if raw_macaroon is None:\n            raise InvalidMacaroon(\"malformed or nonexistent macaroon\")\n\n        try:\n            m = pymacaroons.Macaroon.deserialize(raw_macaroon)\n        except MacaroonDeserializationException:\n            raise InvalidMacaroon(\"malformed macaroon\")\n\n        dm = self.find_macaroon(m.identifier.decode())\n\n        if dm is None:\n            raise InvalidMacaroon(\"deleted or nonexistent macaroon\")\n\n        verifier = Verifier(m, context, principals, permission)\n        if verifier.verify(dm.key):\n            dm.last_used = datetime.datetime.now()\n            return True\n\n        raise InvalidMacaroon(\"invalid macaroon\")\n\n    def create_macaroon(self, location, user_id, description, caveats):\n        \"\"\"\n        Returns a tuple of a new raw (serialized) macaroon and its DB model.\n        The description provided is not embedded into the macaroon, only stored\n        in the DB model.\n        \"\"\"\n        user = self.db.query(User).filter(User.id == user_id).one()\n\n        dm = Macaroon(user=user, description=description, caveats=caveats)\n        self.db.add(dm)\n        self.db.flush()\n\n        m = pymacaroons.Macaroon(\n            location=location,\n            identifier=str(dm.id),\n            key=dm.key,\n            version=pymacaroons.MACAROON_V2,\n        )\n        m.add_first_party_caveat(json.dumps(caveats))\n        serialized_macaroon = f\"pypi-{m.serialize()}\"\n        return serialized_macaroon, dm\n\n    def delete_macaroon(self, macaroon_id):\n        \"\"\"\n        Deletes a macaroon from the DB by its identifier.\n        \"\"\"\n        dm = self.find_macaroon(macaroon_id)\n        self.db.delete(dm)\n        self.db.flush()\n\n    def get_macaroon_by_description(self, user_id, description):\n        \"\"\"\n        Returns a macaroon model from the DB with the given description,\n        if one exists for the given user.\n\n        Returns None if the user doesn't have a macaroon with this description.\n        \"\"\"\n        try:\n            dm = (\n                self.db.query(Macaroon)\n                .options(joinedload(\"user\"))\n                .filter(Macaroon.description == description)\n                .filter(Macaroon.user_id == user_id)\n                .one()\n            )\n        except NoResultFound:\n            return None\n\n        return dm\n\n\ndef database_macaroon_factory(context, request):\n    return DatabaseMacaroonService(request.db)\n", "path": "warehouse/macaroons/services.py"}, {"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\n\nfrom pyramid.authentication import CallbackAuthenticationPolicy\nfrom pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy\nfrom pyramid.security import Denied\nfrom pyramid.threadlocal import get_current_request\nfrom zope.interface import implementer\n\nfrom warehouse.cache.http import add_vary_callback\nfrom warehouse.macaroons.interfaces import IMacaroonService\nfrom warehouse.macaroons.services import InvalidMacaroon\n\n\ndef _extract_basic_macaroon(auth):\n    \"\"\"\n    A helper function for extracting a macaroon from a\n    HTTP Basic Authentication-style header.\n\n    Returns None if the header doesn't contain a structurally\n    valid macaroon, or the candidate (not yet verified) macaroon\n    in a serialized form.\n    \"\"\"\n    try:\n        authorization = base64.b64decode(auth).decode()\n        auth_method, _, auth = authorization.partition(\":\")\n    except ValueError:\n        return None\n\n    # TODO: Remove @token as an acceptable token username (GH-6345)\n    if auth_method != \"@token\" and auth_method != \"__token__\":\n        return None\n\n    return auth\n\n\ndef _extract_http_macaroon(request):\n    \"\"\"\n    A helper function for the extraction of HTTP Macaroon from a given request.\n    Returns either a None if no macaroon could be found, or the string\n    that represents our serialized macaroon.\n    \"\"\"\n    authorization = request.headers.get(\"Authorization\")\n    if not authorization:\n        return None\n\n    try:\n        auth_method, auth = authorization.split(\" \", 1)\n    except ValueError:\n        return None\n\n    if auth_method.lower() == \"basic\":\n        return _extract_basic_macaroon(auth)\n    elif auth_method.lower() == \"token\":\n        return auth\n\n    return None\n\n\n@implementer(IAuthenticationPolicy)\nclass MacaroonAuthenticationPolicy(CallbackAuthenticationPolicy):\n    def __init__(self, callback=None):\n        self.callback = callback\n\n    def unauthenticated_userid(self, request):\n        # If we're calling into this API on a request, then we want to register\n        # a callback which will ensure that the response varies based on the\n        # Authorization header.\n        request.add_response_callback(add_vary_callback(\"Authorization\"))\n\n        # We need to extract our Macaroon from the request.\n        macaroon = _extract_http_macaroon(request)\n        if macaroon is None:\n            return None\n\n        # Check to see if our Macaroon exists in the database, and if so\n        # fetch the user that is associated with it.\n        macaroon_service = request.find_service(IMacaroonService, context=None)\n        userid = macaroon_service.find_userid(macaroon)\n        if userid is not None:\n            return str(userid)\n\n    def remember(self, request, userid, **kw):\n        # This is a NO-OP because our Macaroon header policy doesn't allow\n        # the ability for authentication to \"remember\" the user id. This\n        # assumes it has been configured in clients somewhere out of band.\n        return []\n\n    def forget(self, request):\n        # This is a NO-OP because our Macaroon header policy doesn't allow\n        # the ability for authentication to \"forget\" the user id. This\n        # assumes it has been configured in clients somewhere out of band.\n        return []\n\n\n@implementer(IAuthorizationPolicy)\nclass MacaroonAuthorizationPolicy:\n    def __init__(self, policy):\n        self.policy = policy\n\n    def permits(self, context, principals, permission):\n        # The Pyramid API doesn't let us access the request here, so we have to pull it\n        # out of the thread local instead.\n        # TODO: Work with Pyramid devs to figure out if there is a better way to support\n        #       the worklow we are using here or not.\n        request = get_current_request()\n\n        # Our request could possibly be a None, if there isn't an active request, in\n        # that case we're going to always deny, because without a request, we can't\n        # determine if this request is authorized or not.\n        if request is None:\n            return Denied(\"There was no active request.\")\n\n        # Re-extract our Macaroon from the request, it sucks to have to do this work\n        # twice, but I believe it is inevitable unless we pass the Macaroon back as\n        # a principal-- which doesn't seem to be the right fit for it.\n        macaroon = _extract_http_macaroon(request)\n\n        # This logic will only happen on requests that are being authenticated with\n        # Macaroons. Any other request will just fall back to the standard Authorization\n        # policy.\n        if macaroon is not None:\n            macaroon_service = request.find_service(IMacaroonService, context=None)\n\n            try:\n                macaroon_service.verify(macaroon, context, principals, permission)\n            except InvalidMacaroon as exc:\n                return Denied(f\"The supplied token was invalid: {str(exc)!r}\")\n\n        # If our Macaroon is verified, then we'll pass this request to our underlying\n        # Authorization policy, so it can handle its own authorization logic on\n        # the prinicpal.\n        return self.policy.permits(context, principals, permission)\n\n    def principals_allowed_by_permission(self, context, permission):\n        # We just dispatch this, because Macaroons don't restrict what principals are\n        # allowed by a particular permission, they just restrict specific requests\n        # to not have that permission.\n        return self.policy.principals_allowed_by_permission(context, permission)\n", "path": "warehouse/macaroons/auth_policy.py"}], "after_files": [{"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nimport json\nimport uuid\n\nimport pymacaroons\n\nfrom pymacaroons.exceptions import MacaroonDeserializationException\nfrom sqlalchemy.orm import joinedload\nfrom sqlalchemy.orm.exc import NoResultFound\nfrom zope.interface import implementer\n\nfrom warehouse.accounts.models import User\nfrom warehouse.macaroons.caveats import InvalidMacaroon, Verifier\nfrom warehouse.macaroons.interfaces import IMacaroonService\nfrom warehouse.macaroons.models import Macaroon\n\n\n@implementer(IMacaroonService)\nclass DatabaseMacaroonService:\n    def __init__(self, db_session):\n        self.db = db_session\n\n    def _extract_raw_macaroon(self, prefixed_macaroon):\n        \"\"\"\n        Returns the base64-encoded macaroon component of a PyPI macaroon,\n        dropping the prefix.\n\n        Returns None if the macaroon is None, has no prefix, or has the\n        wrong prefix.\n        \"\"\"\n        if prefixed_macaroon is None:\n            return None\n\n        prefix, _, raw_macaroon = prefixed_macaroon.partition(\"-\")\n        if prefix != \"pypi\" or not raw_macaroon:\n            return None\n\n        return raw_macaroon\n\n    def find_macaroon(self, macaroon_id):\n        \"\"\"\n        Returns a macaroon model from the DB by its identifier.\n        Returns None if no macaroon has the given ID.\n        \"\"\"\n        try:\n            dm = (\n                self.db.query(Macaroon)\n                .options(joinedload(\"user\"))\n                .filter(Macaroon.id == uuid.UUID(macaroon_id))\n                .one()\n            )\n        except NoResultFound:\n            return None\n\n        return dm\n\n    def find_userid(self, raw_macaroon):\n        \"\"\"\n        Returns the id of the user associated with the given raw (serialized)\n        macaroon.\n        \"\"\"\n        raw_macaroon = self._extract_raw_macaroon(raw_macaroon)\n        if raw_macaroon is None:\n            return None\n\n        try:\n            m = pymacaroons.Macaroon.deserialize(raw_macaroon)\n        except MacaroonDeserializationException:\n            return None\n\n        dm = self.find_macaroon(m.identifier.decode())\n\n        if dm is None:\n            return None\n\n        return dm.user.id\n\n    def verify(self, raw_macaroon, context, principals, permission):\n        \"\"\"\n        Returns True if the given raw (serialized) macaroon is\n        valid for the context, principals, and requested permission.\n\n        Raises InvalidMacaroon if the macaroon is not valid.\n        \"\"\"\n        raw_macaroon = self._extract_raw_macaroon(raw_macaroon)\n        if raw_macaroon is None:\n            raise InvalidMacaroon(\"malformed or nonexistent macaroon\")\n\n        try:\n            m = pymacaroons.Macaroon.deserialize(raw_macaroon)\n        except MacaroonDeserializationException:\n            raise InvalidMacaroon(\"malformed macaroon\")\n\n        dm = self.find_macaroon(m.identifier.decode())\n\n        if dm is None:\n            raise InvalidMacaroon(\"deleted or nonexistent macaroon\")\n\n        verifier = Verifier(m, context, principals, permission)\n        if verifier.verify(dm.key):\n            dm.last_used = datetime.datetime.now()\n            return True\n\n        raise InvalidMacaroon(\"invalid macaroon\")\n\n    def create_macaroon(self, location, user_id, description, caveats):\n        \"\"\"\n        Returns a tuple of a new raw (serialized) macaroon and its DB model.\n        The description provided is not embedded into the macaroon, only stored\n        in the DB model.\n        \"\"\"\n        user = self.db.query(User).filter(User.id == user_id).one()\n\n        dm = Macaroon(user=user, description=description, caveats=caveats)\n        self.db.add(dm)\n        self.db.flush()\n\n        m = pymacaroons.Macaroon(\n            location=location,\n            identifier=str(dm.id),\n            key=dm.key,\n            version=pymacaroons.MACAROON_V2,\n        )\n        m.add_first_party_caveat(json.dumps(caveats))\n        serialized_macaroon = f\"pypi-{m.serialize()}\"\n        return serialized_macaroon, dm\n\n    def delete_macaroon(self, macaroon_id):\n        \"\"\"\n        Deletes a macaroon from the DB by its identifier.\n        \"\"\"\n        dm = self.find_macaroon(macaroon_id)\n        self.db.delete(dm)\n        self.db.flush()\n\n    def get_macaroon_by_description(self, user_id, description):\n        \"\"\"\n        Returns a macaroon model from the DB with the given description,\n        if one exists for the given user.\n\n        Returns None if the user doesn't have a macaroon with this description.\n        \"\"\"\n        try:\n            dm = (\n                self.db.query(Macaroon)\n                .options(joinedload(\"user\"))\n                .filter(Macaroon.description == description)\n                .filter(Macaroon.user_id == user_id)\n                .one()\n            )\n        except NoResultFound:\n            return None\n\n        return dm\n\n\ndef database_macaroon_factory(context, request):\n    return DatabaseMacaroonService(request.db)\n", "path": "warehouse/macaroons/services.py"}, {"content": "# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\n\nfrom pyramid.authentication import CallbackAuthenticationPolicy\nfrom pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy\nfrom pyramid.security import Denied\nfrom pyramid.threadlocal import get_current_request\nfrom zope.interface import implementer\n\nfrom warehouse.cache.http import add_vary_callback\nfrom warehouse.macaroons.interfaces import IMacaroonService\nfrom warehouse.macaroons.services import InvalidMacaroon\n\n\ndef _extract_basic_macaroon(auth):\n    \"\"\"\n    A helper function for extracting a macaroon from a\n    HTTP Basic Authentication-style header.\n\n    Returns None if the header doesn't contain a structurally\n    valid macaroon, or the candidate (not yet verified) macaroon\n    in a serialized form.\n    \"\"\"\n    try:\n        authorization = base64.b64decode(auth).decode()\n        auth_method, _, auth = authorization.partition(\":\")\n    except ValueError:\n        return None\n\n    if auth_method != \"__token__\":\n        return None\n\n    return auth\n\n\ndef _extract_http_macaroon(request):\n    \"\"\"\n    A helper function for the extraction of HTTP Macaroon from a given request.\n    Returns either a None if no macaroon could be found, or the string\n    that represents our serialized macaroon.\n    \"\"\"\n    authorization = request.headers.get(\"Authorization\")\n    if not authorization:\n        return None\n\n    try:\n        auth_method, auth = authorization.split(\" \", 1)\n    except ValueError:\n        return None\n\n    if auth_method.lower() == \"basic\":\n        return _extract_basic_macaroon(auth)\n    elif auth_method.lower() == \"token\":\n        return auth\n\n    return None\n\n\n@implementer(IAuthenticationPolicy)\nclass MacaroonAuthenticationPolicy(CallbackAuthenticationPolicy):\n    def __init__(self, callback=None):\n        self.callback = callback\n\n    def unauthenticated_userid(self, request):\n        # If we're calling into this API on a request, then we want to register\n        # a callback which will ensure that the response varies based on the\n        # Authorization header.\n        request.add_response_callback(add_vary_callback(\"Authorization\"))\n\n        # We need to extract our Macaroon from the request.\n        macaroon = _extract_http_macaroon(request)\n        if macaroon is None:\n            return None\n\n        # Check to see if our Macaroon exists in the database, and if so\n        # fetch the user that is associated with it.\n        macaroon_service = request.find_service(IMacaroonService, context=None)\n        userid = macaroon_service.find_userid(macaroon)\n        if userid is not None:\n            return str(userid)\n\n    def remember(self, request, userid, **kw):\n        # This is a NO-OP because our Macaroon header policy doesn't allow\n        # the ability for authentication to \"remember\" the user id. This\n        # assumes it has been configured in clients somewhere out of band.\n        return []\n\n    def forget(self, request):\n        # This is a NO-OP because our Macaroon header policy doesn't allow\n        # the ability for authentication to \"forget\" the user id. This\n        # assumes it has been configured in clients somewhere out of band.\n        return []\n\n\n@implementer(IAuthorizationPolicy)\nclass MacaroonAuthorizationPolicy:\n    def __init__(self, policy):\n        self.policy = policy\n\n    def permits(self, context, principals, permission):\n        # The Pyramid API doesn't let us access the request here, so we have to pull it\n        # out of the thread local instead.\n        # TODO: Work with Pyramid devs to figure out if there is a better way to support\n        #       the worklow we are using here or not.\n        request = get_current_request()\n\n        # Our request could possibly be a None, if there isn't an active request, in\n        # that case we're going to always deny, because without a request, we can't\n        # determine if this request is authorized or not.\n        if request is None:\n            return Denied(\"There was no active request.\")\n\n        # Re-extract our Macaroon from the request, it sucks to have to do this work\n        # twice, but I believe it is inevitable unless we pass the Macaroon back as\n        # a principal-- which doesn't seem to be the right fit for it.\n        macaroon = _extract_http_macaroon(request)\n\n        # This logic will only happen on requests that are being authenticated with\n        # Macaroons. Any other request will just fall back to the standard Authorization\n        # policy.\n        if macaroon is not None:\n            macaroon_service = request.find_service(IMacaroonService, context=None)\n\n            try:\n                macaroon_service.verify(macaroon, context, principals, permission)\n            except InvalidMacaroon as exc:\n                return Denied(f\"The supplied token was invalid: {str(exc)!r}\")\n\n        # If our Macaroon is verified, then we'll pass this request to our underlying\n        # Authorization policy, so it can handle its own authorization logic on\n        # the prinicpal.\n        return self.policy.permits(context, principals, permission)\n\n    def principals_allowed_by_permission(self, context, permission):\n        # We just dispatch this, because Macaroons don't restrict what principals are\n        # allowed by a particular permission, they just restrict specific requests\n        # to not have that permission.\n        return self.policy.principals_allowed_by_permission(context, permission)\n", "path": "warehouse/macaroons/auth_policy.py"}]}