Skip to content

Commit f08280d

Browse files
Merge pull request #38 from mdsol/develop
Develop => Master for 1.4.0
2 parents 7801215 + 3ae28ce commit f08280d

File tree

10 files changed

+286
-220
lines changed

10 files changed

+286
-220
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.4.0
2+
- Add `MAuthWSGIMiddleware` for authenticating requests in WSGI frameworks like Flask.
3+
- Remove `FlaskAuthenticator`.
4+
15
# 1.3.0
26
- Add `MAuthASGIMiddleware` for authenticating requests in ASGI frameworks like FastAPI.
37
- Remove Support for EOL Python 3.6

README.md

+24-17
Original file line numberDiff line numberDiff line change
@@ -112,30 +112,37 @@ authentic, status_code, message = authenticator.is_authentic()
112112
app_uuid = authenticator.get_app_uuid()
113113
```
114114

115-
#### Flask applications
115+
#### WSGI Applications
116116

117-
You will need to create an application instance and initialize it with `FlaskAuthenticator`.
118-
To specify routes that need to be authenticated use the `requires_authentication` decorator.
117+
To apply to a WSGI application you should use the `MAuthWSGIMiddleware`. You
118+
can make certain paths exempt from authentication by passing the `exempt`
119+
option with a set of paths to exempt.
120+
121+
Here is an example for Flask. Note that requesting app's UUID and the
122+
protocol version will be added to the request environment for successfully
123+
authenticated requests.
119124

120125
```python
121-
from flask import Flask
122-
from mauth_client.flask_authenticator import FlaskAuthenticator, requires_authentication
126+
from flask import Flask, request, jsonify
127+
from mauth_client.consts import ENV_APP_UUID, ENV_PROTOCOL_VERSION
128+
from mauth_client.middlewares import MAuthWSGIMiddleware
123129

124-
app = Flask("Some Sample App")
125-
authenticator = FlaskAuthenticator()
126-
authenticator.init_app(app)
130+
app = Flask("MyApp")
131+
app.wsgi_app = MAuthWSGIMiddleware(app.wsgi_app, exempt={"/app_status"})
127132

128-
@app.route("/some/private/route", methods=["GET"])
129-
@requires_authentication
130-
def private_route():
131-
return "Wibble"
133+
@app.get("/")
134+
def root():
135+
return jsonify({
136+
"msg": "authenticated",
137+
"app_uuid": request.environ[ENV_APP_UUID],
138+
"protocol_version": request.environ[ENV_PROTOCOL_VERSION],
139+
})
132140

133-
@app.route("/app_status", methods=["GET"])
134-
def app_status():
135-
return "OK"
141+
@app.get("/app_status")
142+
return "this route is exempt from authentication"
136143
```
137144

138-
#### ASGI applications
145+
#### ASGI Applications
139146

140147
To apply to an ASGI application you should use the `MAuthASGIMiddleware`. You
141148
can make certain paths exempt from authentication by passing the `exempt`
@@ -147,7 +154,7 @@ authenticated requests.
147154

148155
```python
149156
from fastapi import FastAPI, Request
150-
from mauth_client.constants import ENV_APP_UUID, ENV_PROTOCOL_VERSION
157+
from mauth_client.consts import ENV_APP_UUID, ENV_PROTOCOL_VERSION
151158
from mauth_client.middlewares import MAuthASGIMiddleware
152159

153160
app = FastAPI()

mauth_client/flask_authenticator/__init__.py

-1
This file was deleted.

mauth_client/flask_authenticator/flask_authenticator.py

-81
This file was deleted.

mauth_client/middlewares/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .asgi import MAuthASGIMiddleware
2+
from .wsgi import MAuthWSGIMiddleware

mauth_client/middlewares/wsgi.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import json
2+
import logging
3+
4+
from mauth_client.authenticator import LocalAuthenticator
5+
from mauth_client.config import Config
6+
from mauth_client.consts import (
7+
ENV_APP_UUID,
8+
ENV_AUTHENTIC,
9+
ENV_PROTOCOL_VERSION,
10+
)
11+
12+
from mauth_client.signable import RequestSignable
13+
from mauth_client.signed import Signed
14+
15+
logger = logging.getLogger("mauth_wsgi")
16+
17+
18+
class MAuthWSGIMiddleware:
19+
def __init__(self, app, exempt=None):
20+
self._validate_configs()
21+
self.app = app
22+
self.exempt = exempt.copy() if exempt else set()
23+
24+
def __call__(self, environ, start_response):
25+
req = environ["werkzeug.request"]
26+
27+
if req.path in self.exempt:
28+
return self.app(environ, start_response)
29+
30+
signable = RequestSignable(
31+
method=req.method,
32+
url=req.url,
33+
body=self._read_body(environ),
34+
)
35+
signed = Signed.from_headers(dict(req.headers))
36+
authenticator = LocalAuthenticator(signable, signed, logger)
37+
is_authentic, status, message = authenticator.is_authentic()
38+
39+
if is_authentic:
40+
environ[ENV_APP_UUID] = signed.app_uuid
41+
environ[ENV_AUTHENTIC] = True
42+
environ[ENV_PROTOCOL_VERSION] = signed.protocol_version()
43+
return self.app(environ, start_response)
44+
45+
start_response(status, [("content-type", "application/json")])
46+
body = {"errors": {"mauth": [message]}}
47+
return [json.dumps(body).encode("utf-8")]
48+
49+
def _validate_configs(self):
50+
# Validate the client settings (APP_UUID, PRIVATE_KEY)
51+
if not all([Config.APP_UUID, Config.PRIVATE_KEY]):
52+
raise TypeError("MAuthWSGIMiddleware requires APP_UUID and PRIVATE_KEY")
53+
# Validate the mauth settings (MAUTH_BASE_URL, MAUTH_API_VERSION)
54+
if not all([Config.MAUTH_URL, Config.MAUTH_API_VERSION]):
55+
raise TypeError("MAuthWSGIMiddleware requires MAUTH_URL and MAUTH_API_VERSION")
56+
57+
def _read_body(self, environ):
58+
input = environ["wsgi.input"]
59+
input.seek(0)
60+
body = input.read()
61+
input.seek(0)
62+
return body

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "mauth-client"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
description = "MAuth Client for Python"
55
repository = "https://github.com/mdsol/mauth-client-python"
66
authors = ["Medidata Solutions <[email protected]>"]
@@ -13,10 +13,10 @@ classifiers = [
1313
"License :: OSI Approved :: MIT License",
1414
"Operating System :: OS Independent",
1515
"Programming Language :: Python",
16-
"Programming Language :: Python :: 3.6",
1716
"Programming Language :: Python :: 3.7",
1817
"Programming Language :: Python :: 3.8",
1918
"Programming Language :: Python :: 3.9",
19+
"Programming Language :: Python :: 3.10",
2020
"Topic :: Internet :: WWW/HTTP",
2121
"Topic :: Software Development :: Libraries :: Python Modules",
2222
]

tests/flask_authenticator/__init__.py

Whitespace-only changes.

tests/flask_authenticator/flask_authenticator_test.py

-119
This file was deleted.

0 commit comments

Comments
 (0)