aiohttp-msal 0.6.8__tar.gz → 0.7.1__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 (25) hide show
  1. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/PKG-INFO +1 -1
  2. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/__init__.py +32 -5
  3. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/routes.py +5 -5
  4. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/settings.py +1 -1
  5. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/PKG-INFO +1 -1
  6. aiohttp_msal-0.7.1/tests/test_init.py +79 -0
  7. aiohttp_msal-0.6.8/tests/test_init.py +0 -4
  8. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/LICENSE +0 -0
  9. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/README.md +0 -0
  10. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/msal_async.py +0 -0
  11. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/redis_tools.py +0 -0
  12. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/settings_base.py +0 -0
  13. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal/user_info.py +0 -0
  14. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/SOURCES.txt +0 -0
  15. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/dependency_links.txt +0 -0
  16. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/requires.txt +0 -0
  17. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/top_level.txt +0 -0
  18. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/aiohttp_msal.egg-info/zip-safe +0 -0
  19. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/pyproject.toml +0 -0
  20. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/setup.cfg +0 -0
  21. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/setup.py +0 -0
  22. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/tests/__init__.py +0 -0
  23. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/tests/test_msal_async.py +0 -0
  24. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/tests/test_redis_tools.py +0 -0
  25. {aiohttp_msal-0.6.8 → aiohttp_msal-0.7.1}/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.8
3
+ Version: 0.7.1
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
@@ -14,7 +14,7 @@ from aiohttp_msal.settings import ENV
14
14
 
15
15
  _LOGGER = logging.getLogger(__name__)
16
16
 
17
- VERSION = "0.6.8"
17
+ VERSION = "0.7.1"
18
18
 
19
19
 
20
20
  def msal_session(
@@ -32,10 +32,17 @@ def msal_session(
32
32
  ses = AsyncMSAL(session=await get_session(request))
33
33
  for c_b in callbacks:
34
34
  _ok = await c_b(ses) if iscoroutinefunction(c_b) else c_b(ses)
35
- if at_least_one and _ok:
36
- break
37
- if not at_least_one and not _ok:
35
+
36
+ if at_least_one:
37
+ if _ok:
38
+ return await func(request=request, ses=ses)
39
+ continue
40
+
41
+ if not _ok:
38
42
  raise web.HTTPForbidden
43
+
44
+ if at_least_one:
45
+ raise web.HTTPForbidden
39
46
  return await func(request=request, ses=ses)
40
47
 
41
48
  assert iscoroutinefunction(func), f"Function needs to be a coroutine: {func}"
@@ -46,11 +53,31 @@ def msal_session(
46
53
  return _session
47
54
 
48
55
 
49
- def authenticated(ses: AsyncMSAL) -> bool:
56
+ def auth_ok(ses: AsyncMSAL) -> bool:
50
57
  """Test if session was authenticated."""
51
58
  return bool(ses.mail)
52
59
 
53
60
 
61
+ def auth_or(
62
+ *args: typing.Callable[[AsyncMSAL], bool | typing.Awaitable[bool]]
63
+ ) -> typing.Callable[[AsyncMSAL], typing.Awaitable[bool]]:
64
+ """Ensure either of the methods is valid. An alternative to at_least_one=True.
65
+
66
+ Arguments can include a list of function to perform login tests etc."""
67
+
68
+ async def or_auth(ses: AsyncMSAL) -> bool:
69
+ """Or."""
70
+ for arg in args:
71
+ if iscoroutinefunction(arg):
72
+ if await arg(ses):
73
+ return True
74
+ elif arg(ses):
75
+ return True
76
+ raise web.HTTPForbidden
77
+
78
+ return or_auth
79
+
80
+
54
81
  async def app_init_redis_session(
55
82
  app: web.Application, max_age: int = 3600 * 24 * 90
56
83
  ) -> None:
@@ -8,7 +8,7 @@ from urllib.parse import urljoin
8
8
  from aiohttp import web
9
9
  from aiohttp_session import get_session, new_session
10
10
 
11
- from aiohttp_msal import _LOGGER, ENV, authenticated, msal_session
11
+ from aiohttp_msal import _LOGGER, ENV, auth_ok, msal_session
12
12
  from aiohttp_msal.msal_async import FLOW_CACHE, AsyncMSAL
13
13
  from aiohttp_msal.user_info import get_manager_info, get_user_info
14
14
 
@@ -145,14 +145,14 @@ async def user_debug(request: web.Request) -> web.Response:
145
145
  return web.json_response(debug)
146
146
 
147
147
 
148
- ENV.info["authenticated"] = authenticated
148
+ ENV.info["authenticated"] = auth_ok
149
149
 
150
150
 
151
151
  @ROUTES.get("/user/info")
152
152
  @msal_session()
153
153
  async def user_info(request: web.Request, ses: AsyncMSAL) -> web.Response:
154
154
  """User info handler."""
155
- if not authenticated(ses):
155
+ if not auth_ok(ses):
156
156
  return web.json_response({"authenticated": False})
157
157
 
158
158
  debug = request.query.get("debug", False)
@@ -182,7 +182,7 @@ async def user_info(request: web.Request, ses: AsyncMSAL) -> web.Response:
182
182
 
183
183
  @ROUTES.get("/user/logout")
184
184
  @ROUTES.get("/user/logout/{to:.+$}")
185
- @msal_session(authenticated)
185
+ @msal_session(auth_ok)
186
186
  async def user_logout(request: web.Request, ses: AsyncMSAL) -> web.Response:
187
187
  """Redirect to MS graph login page."""
188
188
  ses.session.clear()
@@ -202,7 +202,7 @@ async def user_logout(request: web.Request, ses: AsyncMSAL) -> web.Response:
202
202
 
203
203
 
204
204
  @ROUTES.get("/user/photo")
205
- @msal_session(authenticated)
205
+ @msal_session(auth_ok)
206
206
  async def user_photo(request: web.Request, ses: AsyncMSAL) -> web.StreamResponse:
207
207
  """Photo."""
208
208
  async with ses.get("https://graph.microsoft.com/v1.0/me/photo/$value") as res:
@@ -37,7 +37,7 @@ class MSALSettings(SettingsBase):
37
37
 
38
38
  REDIS = "redis://redis1:6379"
39
39
  """OPTIONAL: Redis database connection used by app_init_redis_session()."""
40
- database: Redis
40
+ database: Redis = None # type: ignore
41
41
  """Store the Redis connection when using app_init_redis_session()."""
42
42
 
43
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aiohttp_msal
3
- Version: 0.6.8
3
+ Version: 0.7.1
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
@@ -0,0 +1,79 @@
1
+ """Init."""
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ import pytest
6
+
7
+ import aiohttp_msal.routes # noqa
8
+ from aiohttp_msal import auth_ok, msal_session
9
+
10
+
11
+ def a_yes(ses):
12
+ return True
13
+
14
+
15
+ def a_no(ses):
16
+ return False
17
+
18
+
19
+ @msal_session(a_yes, a_yes)
20
+ async def t_2yes(request, ses):
21
+ return True
22
+
23
+
24
+ @msal_session(a_no, a_yes, at_least_one=True)
25
+ async def t_1no1yes_one(request, ses):
26
+ return True
27
+
28
+
29
+ @msal_session(a_no, a_no, at_least_one=True)
30
+ async def t_2no_one(request, ses):
31
+ return True
32
+
33
+
34
+ @patch("aiohttp_msal.get_session")
35
+ async def test_include_any(get_session: MagicMock):
36
+ get_session.return_value = {}
37
+
38
+ assert await t_2yes({})
39
+
40
+ with pytest.raises(Exception):
41
+ await t_1no1yes_one
42
+
43
+ with pytest.raises(Exception):
44
+ await t_2no_one
45
+
46
+
47
+ async def func(request, ses):
48
+ return True
49
+
50
+
51
+ @patch("aiohttp_msal.get_session")
52
+ async def test_msal_session_auth(get_session: MagicMock):
53
+ get_session.return_value = {}
54
+
55
+ assert await msal_session(a_yes, a_yes)(func)({})
56
+ assert await msal_session(a_yes, a_no, at_least_one=True)(func)({})
57
+ assert await msal_session(a_no, a_yes, at_least_one=True)(func)({})
58
+ assert await msal_session(a_no, a_no, a_no, a_yes, at_least_one=True)(func)({})
59
+
60
+ with pytest.raises(Exception):
61
+ await msal_session(a_yes, a_no)(func)({})
62
+
63
+ with pytest.raises(Exception):
64
+ await msal_session(a_yes, a_yes, a_no)(func)({})
65
+
66
+ with pytest.raises(Exception):
67
+ await msal_session(a_no, a_no, at_least_one=True)(func)({})
68
+
69
+
70
+ @patch("aiohttp_msal.get_session")
71
+ async def test_auth_ok(get_session: MagicMock):
72
+ get_session.return_value = {"mail": "yes!"}
73
+
74
+ assert await msal_session(a_yes)(func)({})
75
+
76
+ get_session.return_value = {}
77
+
78
+ with pytest.raises(Exception):
79
+ assert await msal_session(a_yes, auth_ok)(func)({})
@@ -1,4 +0,0 @@
1
- """Init."""
2
-
3
- import aiohttp_msal # noqa
4
- import aiohttp_msal.routes # noqa
File without changes
File without changes
File without changes
File without changes