Skip to content

Commit fc6f35d

Browse files
authored
Merge pull request openwallet-foundation#3344 from esune/fix/base-wallet-extra-routes
Restore `--base-wallet-routes` flag functionality
2 parents a00b5f5 + 88be790 commit fc6f35d

File tree

3 files changed

+55
-24
lines changed

3 files changed

+55
-24
lines changed

acapy_agent/admin/decorators/auth.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Authentication decorators for the admin API."""
22

33
import functools
4+
import re
5+
from typing import Optional, Pattern
46

57
from aiohttp import web
68

@@ -48,6 +50,8 @@ def tenant_authentication(handler):
4850
- check for a valid bearer token in the Autorization header if running
4951
in multi-tenant mode
5052
- check for a valid x-api-key header if running in single-tenant mode
53+
- check if the base wallet has access to the requested path if running
54+
in multi-tenant mode
5155
"""
5256

5357
@functools.wraps(handler)
@@ -61,11 +65,15 @@ async def tenant_auth(request):
6165
)
6266
insecure_mode = bool(profile.settings.get("admin.admin_insecure_mode"))
6367
multitenant_enabled = profile.settings.get("multitenant.enabled")
68+
base_wallet_allowed_route = _base_wallet_route_access(
69+
profile.settings.get("multitenant.base_wallet_routes"), request.path
70+
)
6471

6572
# CORS fix: allow OPTIONS method access to paths without a token
6673
if (
6774
(multitenant_enabled and authorization_header)
6875
or (not multitenant_enabled and valid_key)
76+
or (multitenant_enabled and valid_key and base_wallet_allowed_route)
6977
or insecure_mode
7078
or request.method == "OPTIONS"
7179
):
@@ -78,3 +86,25 @@ async def tenant_auth(request):
7886
)
7987

8088
return tenant_auth
89+
90+
91+
def _base_wallet_route_access(additional_routes: str, request_path: str) -> bool:
92+
"""Check if request path matches additional routes."""
93+
additional_routes_pattern = _build_additional_routes_pattern(additional_routes)
94+
return _matches_additional_routes(additional_routes_pattern, request_path)
95+
96+
97+
def _build_additional_routes_pattern(pattern_string: str) -> Optional[Pattern]:
98+
"""Build pattern from space delimited list of paths."""
99+
# create array and add word boundary to avoid false positives
100+
if pattern_string:
101+
paths = pattern_string.split(" ")
102+
return re.compile("^((?:)" + "|".join(paths) + ")$")
103+
return None
104+
105+
106+
def _matches_additional_routes(pattern: Pattern, path: str) -> bool:
107+
"""Matches request path to provided pattern."""
108+
if pattern and path:
109+
return bool(pattern.match(path))
110+
return False

acapy_agent/admin/server.py

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
import warnings
77
import weakref
8-
from typing import Callable, Coroutine, Optional, Pattern, Sequence, cast
8+
from typing import Callable, Coroutine, Optional
99

1010
import aiohttp_cors
1111
import jwt
@@ -280,29 +280,6 @@ def __init__(
280280
self.websocket_queues = {}
281281
self.site = None
282282
self.multitenant_manager = context.inject_or(BaseMultitenantManager)
283-
self._additional_route_pattern: Optional[Pattern] = None
284-
285-
@property
286-
def additional_routes_pattern(self) -> Optional[Pattern]:
287-
"""Pattern for configured additional routes to permit base wallet to access."""
288-
if self._additional_route_pattern:
289-
return self._additional_route_pattern
290-
291-
base_wallet_routes = self.context.settings.get("multitenant.base_wallet_routes")
292-
base_wallet_routes = cast(Sequence[str], base_wallet_routes)
293-
if base_wallet_routes:
294-
self._additional_route_pattern = re.compile(
295-
"^(?:" + "|".join(base_wallet_routes) + ")"
296-
)
297-
return None
298-
299-
def _matches_additional_routes(self, path: str) -> bool:
300-
"""Path matches additional_routes_pattern."""
301-
pattern = self.additional_routes_pattern
302-
if pattern:
303-
return bool(pattern.match(path))
304-
305-
return False
306283

307284
async def make_application(self) -> web.Application:
308285
"""Get the aiohttp application instance."""

acapy_agent/admin/tests/test_auth.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,27 @@ async def test_multi_tenant_valid_auth_header(self):
133133
decor_func = tenant_authentication(self.decorated_handler)
134134
await decor_func(self.request)
135135
self.decorated_handler.assert_called_once_with(self.request)
136+
137+
async def test_base_wallet_additional_route_allowed(self):
138+
self.profile.settings["multitenant.base_wallet_routes"] = "/extra-route"
139+
self.request = mock.MagicMock(
140+
__getitem__=lambda _, k: self.request_dict[k],
141+
headers={"x-api-key": "admin_api_key"},
142+
method="POST",
143+
path="/extra-route",
144+
)
145+
decor_func = tenant_authentication(self.decorated_handler)
146+
await decor_func(self.request)
147+
self.decorated_handler.assert_called_once_with(self.request)
148+
149+
async def test_base_wallet_additional_route_denied(self):
150+
self.profile.settings["multitenant.base_wallet_routes"] = "/extra-route"
151+
self.request = mock.MagicMock(
152+
__getitem__=lambda _, k: self.request_dict[k],
153+
headers={"x-api-key": "admin_api_key"},
154+
method="POST",
155+
path="/extra-route-wrong",
156+
)
157+
decor_func = tenant_authentication(self.decorated_handler)
158+
with self.assertRaises(web.HTTPUnauthorized):
159+
await decor_func(self.request)

0 commit comments

Comments
 (0)