fix: Connect ohne forward_auth, Callback bestätigt direkt (#2)
This commit was merged in pull request #2.
This commit is contained in:
4 files changed
+50
-5
No files matched your search
@@ -150,14 +150,22 @@ def oidc_callback():
|
|||||||
if not OAUTH:
|
if not OAUTH:
|
||||||
abort(404)
|
abort(404)
|
||||||
if request.args.get("error"):
|
if request.args.get("error"):
|
||||||
return redirect(url_for("index", error=request.args["error"]))
|
return render_template("index.html", email_address=EMAIL_USERNAME, oauth=True, connected=False,
|
||||||
|
error=request.args["error"]), 400
|
||||||
try:
|
try:
|
||||||
OAUTH.finish(request.args.get("state", ""), request.args.get("code", ""))
|
OAUTH.finish(request.args.get("state", ""), request.args.get("code", ""))
|
||||||
except OAuthError as e:
|
except OAuthError as e:
|
||||||
logger.warning("Mailbox connect failed: %s", e)
|
logger.warning("Mailbox connect failed: %s", e)
|
||||||
return redirect(url_for("index", error=str(e)))
|
return render_template("index.html", email_address=EMAIL_USERNAME, oauth=True, connected=False,
|
||||||
|
error=str(e)), 400
|
||||||
logger.info("Collection mailbox %s connected via OAuth", EMAIL_USERNAME)
|
logger.info("Collection mailbox %s connected via OAuth", EMAIL_USERNAME)
|
||||||
return redirect(url_for("index"))
|
# the browser session now belongs to the technical account, which forward_auth does not let in:
|
||||||
|
# confirm here instead of redirecting to the start page
|
||||||
|
return (
|
||||||
|
"<!doctype html><meta charset=utf-8><title>Connected</title>"
|
||||||
|
"<p>Collection mailbox connected. Close this private window and open the Unsubscriber again "
|
||||||
|
"in your normal browser.</p>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/disconnect", methods=["POST"])
|
@app.route("/disconnect", methods=["POST"])
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@
|
|||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>The collection mailbox is not connected yet. Open this page in a private window and sign in
|
<p>The collection mailbox is not connected yet. Open <code>/connect</code> of this site in a private
|
||||||
as <strong>{{ email_address }}</strong> (technical account) – not with your own account.</p>
|
window and sign in as <strong>{{ email_address }}</strong> (technical account).</p>
|
||||||
<form action="/connect" method="get">
|
<form action="/connect" method="get">
|
||||||
<button type="submit" class="btn-primary">Connect mailbox</button>
|
<button type="submit" class="btn-primary">Connect mailbox</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from cryptography.fernet import Fernet, InvalidToken
|
|||||||
SCOPES = "openid profile email offline_access"
|
SCOPES = "openid profile email offline_access"
|
||||||
REFRESH_MARGIN_SEC = 60
|
REFRESH_MARGIN_SEC = 60
|
||||||
PENDING_TTL_SEC = 600
|
PENDING_TTL_SEC = 600
|
||||||
|
MAX_PENDING = 20 # /connect is reachable without login: bound the open requests
|
||||||
|
|
||||||
|
|
||||||
class OAuthError(Exception):
|
class OAuthError(Exception):
|
||||||
@@ -108,6 +109,8 @@ class MailboxOAuth:
|
|||||||
now = self.clock()
|
now = self.clock()
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._pending = {k: v for k, v in self._pending.items() if now - v[1] < PENDING_TTL_SEC}
|
self._pending = {k: v for k, v in self._pending.items() if now - v[1] < PENDING_TTL_SEC}
|
||||||
|
while len(self._pending) >= MAX_PENDING:
|
||||||
|
del self._pending[min(self._pending, key=lambda k: self._pending[k][1])]
|
||||||
self._pending[state] = (verifier, now)
|
self._pending[state] = (verifier, now)
|
||||||
query = {
|
query = {
|
||||||
"response_type": "code",
|
"response_type": "code",
|
||||||
|
|||||||
@@ -139,3 +139,37 @@ def test_index_shows_connect_when_not_connected(monkeypatch):
|
|||||||
monkeypatch.setattr(unsub, "OAUTH", FakeOAuth())
|
monkeypatch.setattr(unsub, "OAUTH", FakeOAuth())
|
||||||
html = unsub.app.test_client().get("/").get_data(as_text=True)
|
html = unsub.app.test_client().get("/").get_data(as_text=True)
|
||||||
assert 'action="/connect"' in html and 'action="/result"' not in html
|
assert 'action="/connect"' in html and 'action="/result"' not in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_confirms_without_redirect(monkeypatch):
|
||||||
|
import app as unsub
|
||||||
|
|
||||||
|
class FakeOAuth:
|
||||||
|
def finish(self, state, code):
|
||||||
|
assert (state, code) == ("s", "c")
|
||||||
|
|
||||||
|
monkeypatch.setattr(unsub, "OAUTH", FakeOAuth())
|
||||||
|
r = unsub.app.test_client().get("/oidc/callback?state=s&code=c")
|
||||||
|
assert r.status_code == 200 and "connected" in r.get_data(as_text=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_callback_error_is_shown(monkeypatch):
|
||||||
|
import app as unsub
|
||||||
|
|
||||||
|
class FakeOAuth:
|
||||||
|
def finish(self, state, code):
|
||||||
|
raise oauth.OAuthError("Signed in as philipp@example.org, expected unsubscribe-bot@example.org")
|
||||||
|
|
||||||
|
def connected(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
monkeypatch.setattr(unsub, "OAUTH", FakeOAuth())
|
||||||
|
r = unsub.app.test_client().get("/oidc/callback?state=s&code=c")
|
||||||
|
assert r.status_code == 400 and "expected unsubscribe-bot" in r.get_data(as_text=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pending_requests_are_bounded(tmp_path):
|
||||||
|
mb = _mb(tmp_path, [])
|
||||||
|
for _ in range(oauth.MAX_PENDING + 15):
|
||||||
|
mb.start()
|
||||||
|
assert len(mb._pending) == oauth.MAX_PENDING
|
||||||
Reference in new issue
Block a user