forked from posit-dev/shiny-assistant
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
signature.py
59 lines (48 loc) · 1.51 KB
/
signature.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from __future__ import annotations
import hmac
import os
from urllib.parse import parse_qs
from shiny import Inputs, Outputs, Session, module, reactive, ui
with open(os.path.join(os.path.dirname(__file__), "denied.md")) as f:
denied_message = ui.markdown(f.read())
def verify_hmac(key: bytes | str, email: str, sig: str) -> bool:
if isinstance(key, str):
key = bytes.fromhex(key)
correct_sig = hmac.digest(key, email.encode("utf-8"), "sha256").hex()
return hmac.compare_digest(sig, correct_sig)
@module.ui
def validate_email_ui() -> ui.Tag | None:
return ui.div()
@module.server
def validate_email_server(
input: Inputs,
output: Outputs,
session: Session,
*,
hostname: str,
querystring: str,
key: bytes | str | None,
) -> bool:
if key is None:
# No signature; anyone is allowed
return True
if not os.getenv("ENFORCE_SIG_ON_LOCALHOST") and hostname == "localhost":
# Bypass signature check for localhost. Note that this is the hostname
# as seen by the client, not the server.
return True
if querystring.startswith("?"):
querystring = querystring[1:]
qs = parse_qs(querystring)
email = qs.get("email", [""])[0]
digest = qs.get("sig", [""])[0]
if verify_hmac(key, email, digest):
return True
else:
ui.modal_show(
ui.modal(
denied_message,
title="Invitation required",
footer=[],
)
)
return False