aiohttp-msal 0.6.5__tar.gz → 0.6.6__tar.gz

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.
Files changed (24) hide show
  1. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/PKG-INFO +1 -1
  2. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/__init__.py +3 -3
  3. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/msal_async.py +1 -1
  4. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/redis_tools.py +9 -1
  5. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/routes.py +2 -8
  6. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/settings.py +1 -1
  7. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/user_info.py +1 -1
  8. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/PKG-INFO +1 -1
  9. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/SOURCES.txt +1 -0
  10. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/pyproject.toml +1 -1
  11. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/tests/test_msal_async.py +1 -0
  12. aiohttp_msal-0.6.6/tests/test_redis_tools.py +59 -0
  13. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/LICENSE +0 -0
  14. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/README.md +0 -0
  15. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal/settings_base.py +0 -0
  16. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/dependency_links.txt +0 -0
  17. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/requires.txt +0 -0
  18. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/top_level.txt +0 -0
  19. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/aiohttp_msal.egg-info/zip-safe +0 -0
  20. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/setup.cfg +0 -0
  21. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/setup.py +0 -0
  22. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/tests/__init__.py +0 -0
  23. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/tests/test_init.py +0 -0
  24. {aiohttp_msal-0.6.5 → aiohttp_msal-0.6.6}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aiohttp_msal
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: Helper Library to use the Microsoft Authentication Library (MSAL) with aiohttp
5
5
  Home-page: https://github.com/kellerza/aiohttp_msal
6
6
  Author: Johann Kellerman
@@ -8,12 +8,12 @@ from aiohttp import ClientSession, web
8
8
  from aiohttp_session import get_session
9
9
  from aiohttp_session import setup as _setup
10
10
 
11
- from .msal_async import AsyncMSAL
12
- from .settings import ENV
11
+ from aiohttp_msal.msal_async import AsyncMSAL
12
+ from aiohttp_msal.settings import ENV
13
13
 
14
14
  _LOGGER = logging.getLogger(__name__)
15
15
 
16
- VERSION = "0.6.5"
16
+ VERSION = "0.6.6"
17
17
 
18
18
 
19
19
  def msal_session(*args: Callable[[AsyncMSAL], Union[Any, Awaitable[Any]]]) -> Callable:
@@ -14,7 +14,7 @@ from aiohttp.client import ClientResponse, ClientSession, _RequestContextManager
14
14
  from aiohttp_session import Session
15
15
  from msal import ConfidentialClientApplication, SerializableTokenCache
16
16
 
17
- from .settings import ENV
17
+ from aiohttp_msal.settings import ENV
18
18
 
19
19
  HTTP_GET = "get"
20
20
  HTTP_POST = "post"
@@ -42,10 +42,13 @@ async def session_iter(
42
42
  match: Filter based on session content (i.e. mail/name)
43
43
  key_match: Filter the Redis keys. Defaults to ENV.cookie_name
44
44
  """
45
+ if match and not all(isinstance(v, str) for v in match.values()):
46
+ raise ValueError("match values must be strings")
45
47
  async for key in redis.scan_iter(
46
48
  count=100, match=key_match or f"{ENV.COOKIE_NAME}*"
47
49
  ):
48
50
  sval = await redis.get(key)
51
+ _LOGGER.debug("Session: %s = %s", key, sval)
49
52
  created, ses = 0, {}
50
53
  try:
51
54
  val = json.loads(sval) # type: ignore
@@ -55,7 +58,12 @@ async def session_iter(
55
58
  pass
56
59
  if match:
57
60
  # Ensure we match all the supplied terms
58
- if not all(k in ses and v in ses[k] for k, v in match.items()):
61
+ matches = 0
62
+ for mkey, mval in match.items():
63
+ if not (isinstance(ses.get(mkey), str) and mval in ses[mkey]):
64
+ break
65
+ matches += 1
66
+ if matches != len(match):
59
67
  continue
60
68
  yield key, created, ses
61
69
 
@@ -7,19 +7,16 @@ from urllib.parse import urljoin
7
7
  from aiohttp import web
8
8
  from aiohttp_session import get_session, new_session
9
9
 
10
+ from aiohttp_msal import _LOGGER, ENV, authenticated, msal_session
11
+ from aiohttp_msal.msal_async import FLOW_CACHE, AsyncMSAL
10
12
  from aiohttp_msal.user_info import get_manager_info, get_user_info
11
13
 
12
- from . import _LOGGER, ENV, authenticated, msal_session
13
- from .msal_async import FLOW_CACHE, AsyncMSAL
14
-
15
14
  ROUTES = web.RouteTableDef()
16
15
 
17
16
  URI_USER_LOGIN = "/user/login"
18
17
  URI_USER_AUTHORIZED = "/user/authorized"
19
18
  SESSION_REDIRECT = "redirect"
20
19
 
21
- # ic.disable() # remove debug prints
22
-
23
20
 
24
21
  def get_route(request: web.Request, url: str) -> str:
25
22
  """Retrieve server route from request.
@@ -70,14 +67,12 @@ async def user_authorized(request: web.Request) -> web.Response:
70
67
  )
71
68
 
72
69
  if not request.cookies.get(ENV.COOKIE_NAME):
73
- # ic(request.cookies.keys())
74
70
  cookies = dict(request.cookies.items())
75
71
  msg.append(f"<b>Expecting '{ENV.COOKIE_NAME}' in cookies</b>")
76
72
  _LOGGER.fatal("Cookie should be set with Samesite:None")
77
73
  msg.append(html_table(cookies))
78
74
 
79
75
  elif not session.get(FLOW_CACHE):
80
- # ic(session)
81
76
  msg.append(f"<b>Expecting '{FLOW_CACHE}' in session</b>")
82
77
  msg.append(f"- Session.new: {session.new}")
83
78
  msg.append(html_table(session))
@@ -145,7 +140,6 @@ async def user_debug(request: web.Request) -> web.Response:
145
140
  "X-Forw-For IP": request.headers.get("X-Forwarded-For", ""),
146
141
  },
147
142
  }
148
- # ic(debug)
149
143
  session["debug_previous"] = time.time()
150
144
  return web.json_response(debug)
151
145
 
@@ -1,7 +1,7 @@
1
1
  """Settings."""
2
2
  from typing import Any, Awaitable, Callable, Union
3
3
 
4
- from .settings_base import SettingsBase, Var
4
+ from aiohttp_msal.settings_base import SettingsBase, Var
5
5
 
6
6
 
7
7
  class MSALSettings(SettingsBase):
@@ -3,7 +3,7 @@ import asyncio
3
3
  from functools import wraps
4
4
  from typing import Any, Callable
5
5
 
6
- from .msal_async import AsyncMSAL
6
+ from aiohttp_msal.msal_async import AsyncMSAL
7
7
 
8
8
 
9
9
  def retry(func: Callable) -> Callable:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aiohttp-msal
3
- Version: 0.6.5
3
+ Version: 0.6.6
4
4
  Summary: Helper Library to use the Microsoft Authentication Library (MSAL) with aiohttp
5
5
  Home-page: https://github.com/kellerza/aiohttp_msal
6
6
  Author: Johann Kellerman
@@ -19,4 +19,5 @@ aiohttp_msal.egg-info/zip-safe
19
19
  tests/__init__.py
20
20
  tests/test_init.py
21
21
  tests/test_msal_async.py
22
+ tests/test_redis_tools.py
22
23
  tests/test_settings.py
@@ -1,4 +1,4 @@
1
1
  [tool.ruff]
2
- line-length = 88
2
+ line-length = 121
3
3
  # pyflakes, pycodestyle, isort
4
4
  select = ["F", "E", "W", "I001"]
@@ -1,3 +1,4 @@
1
+ """Test the AsyncMSAL class."""
1
2
  from aiohttp_msal.msal_async import AsyncMSAL, Session
2
3
 
3
4
 
@@ -0,0 +1,59 @@
1
+ """Test redis tools."""
2
+ from json import dumps
3
+ from typing import AsyncGenerator
4
+ from unittest.mock import AsyncMock, MagicMock, Mock, call
5
+
6
+ import pytest
7
+
8
+ from aiohttp_msal.redis_tools import Redis, session_iter
9
+
10
+
11
+ @pytest.fixture
12
+ def redis() -> Redis:
13
+ """Get a redis Mock instance."""
14
+ testdata = {
15
+ "a": dumps({"created": 1, "session": {"key": "a", "a": 1, "b": "2a"}}),
16
+ "b": dumps({"created": 2, "session": {"key": "b", "a": 1, "b": "2b"}}),
17
+ "c": dumps({"created": 3, "session": {"key": "c", "a": 5, "b": "6c"}}),
18
+ }
19
+
20
+ async def scan_iter(*, count: int, match: str) -> AsyncGenerator[str, None]:
21
+ """Mock keys."""
22
+ assert count == 100
23
+ assert match == "a*"
24
+ for key in testdata:
25
+ yield key
26
+
27
+ red = Mock()
28
+ red.scan_iter = MagicMock(side_effect=scan_iter)
29
+ red.get = AsyncMock(side_effect=list(testdata.values()))
30
+ return red
31
+
32
+
33
+ @pytest.mark.asyncio
34
+ async def test_session_iter_fail(redis: Redis) -> None:
35
+ """Test session iter."""
36
+ match = {"a": 1}
37
+ with pytest.raises(ValueError):
38
+ async for _ in session_iter(redis, match=match, key_match="a*"):
39
+ pass
40
+
41
+ match = {"a": "1"}
42
+ async for _ in session_iter(redis, match=match, key_match="a*"):
43
+ assert False, "no match expected"
44
+
45
+
46
+ @pytest.mark.asyncio
47
+ async def test_session_iter(redis: Redis) -> None:
48
+ """Test session iter."""
49
+ match = {"b": "2"}
50
+ expected = ["a", "b"]
51
+ async for key, created, ses in session_iter(redis, match=match, key_match="a*"):
52
+ assert expected.pop(0) == key
53
+ assert key == ses["key"]
54
+ assert created in (1, 2)
55
+ assert key in ("a", "b")
56
+
57
+ assert redis.scan_iter.call_args[1]["match"] == "a*"
58
+ assert redis.scan_iter.call_args[1]["count"] == 100
59
+ assert redis.scan_iter.call_args_list == [call(count=100, match="a*")]
File without changes
File without changes
File without changes
File without changes