now works.

This commit is contained in:
Moon Man 2024-10-05 10:38:36 +00:00
parent d30292d141
commit 762c918223
2 changed files with 80 additions and 20 deletions

94
auth.py
View File

@ -4,19 +4,66 @@ from markupsafe import escape
from mastodon import Mastodon from mastodon import Mastodon
from email.headerregistry import Address from email.headerregistry import Address
import jwt import jwt
import shelve
from gevent.pywsgi import WSGIServer from gevent.pywsgi import WSGIServer
import os import os
import sqlite3
import zulip
import random
import string
SECRET = os.environ["SECRET"] SECRET = os.environ["SECRET"]
ZULIP = f"https://{os.environ['ZULIP']}/accounts/login/jwt/" ZULIP = f"https://{os.environ['ZULIP']}/accounts/login/jwt/"
REDIRECT = f"https://{os.environ['ZULIP']}/callback" REDIRECT = f"https://{os.environ['ZULIP']}/fedi-auth/callback"
SHELVE_LOCATION = os.environ.get("DB_DIR", "/var/lib/fedi-zulip") DB = os.environ.get("DB", "/var/lib/fedi-zulip/db/applications")
PORT = int(os.environ.get("PORT", "5000"))
print(f"""
Zulip is: {os.environ['ZULIP']}
DB location is: {DB}
""")
con = sqlite3.connect(DB)
cur = con.cursor()
zulip_client = zulip.Client()
def get_zulip_user(handle):
zulip_client.call_endpoint(
url=f"/users/{handle}",
method="GET"
)
def create_zulip_user(handle):
password = ''.join(random.choices(string.ascii_uppercase + string.digits, k=40))
return zulip_client.create_user({
"email": handle,
"password": password,
"full_name": handle.split('@')[0]
})
def get_or_create_zulip_user(handle):
user = get_zulip_user(handle)
if user is None:
print(f"User: {handle} created.")
user = create_zulip_user(handle)
else:
print(f"User: {handle} already exists.")
return user
cur.execute("CREATE TABLE IF NOT EXISTS applications(instance TEXT PRIMARY KEY, client TEXT, secret TEXT, disabled BOOLEAN DEFAULT FALSE)")
def get_application(instance):
res = cur.execute("SELECT client, secret FROM applications WHERE instance = ?", [instance])
return res.fetchone();
def set_application(instance, client, secret):
res = cur.execute("INSERT INTO applications(instance, client, secret) values (?, ?, ?)", (instance, client, secret));
con.commit();
app = Flask(__name__) app = Flask(__name__)
@app.get("/") @app.get("/fedi-auth/")
def index(): def index():
return f""" return f"""
<!DOCTYPE html> <!DOCTYPE html>
@ -27,7 +74,7 @@ def index():
You can use this page to login to {os.environ['ZULIP']} using your You can use this page to login to {os.environ['ZULIP']} using your
Pleroma, Akkoma or Mastodon handle. Format is <code>nickname@server</code>. Pleroma, Akkoma or Mastodon handle. Format is <code>nickname@server</code>.
</p> </p>
<form action="/login" method="post"> <form action="/fedi-auth/login" method="post">
<label for="nickname">Fediverse handle</label> <label for="nickname">Fediverse handle</label>
<br> <br>
<input type="email" id="nickname" name="nickname"> <input type="email" id="nickname" name="nickname">
@ -38,27 +85,31 @@ def index():
""" """
@app.post("/login") @app.post("/fedi-auth/login")
def login(): def login():
print("Login POST", flush=True)
instance = Address(addr_spec=request.form["nickname"]).domain instance = Address(addr_spec=request.form["nickname"]).domain
print(f"Instance is: {instance}.", flush=True)
try: try:
with shelve.open(SHELVE_LOCATION) as apps: app = get_application(instance)
app = apps.get(instance)
if app == None: if app == None:
print(f"There is no OAuth application for {instance} so creating one.", flush=True)
(client, secret) = Mastodon.create_app( (client, secret) = Mastodon.create_app(
"zulip", "zulip",
scopes=["read"], scopes=["read"],
redirect_uris=REDIRECT, redirect_uris=REDIRECT,
api_base_url=f"https://{instance}", api_base_url=f"https://{instance}",
) )
apps[instance] = (client, secret) app = (client, secret)
set_application(instance, client, secret)
(client, secret) = apps[instance] (client, secret) = app
masto = Mastodon( masto = Mastodon(
client_id=client, client_id=client,
client_secret=secret, client_secret=secret,
api_base_url=f"https://{instance}", api_base_url=f"https://{instance}",
) )
print(f"Getting login URI for {instance}.", flush=True)
oauth = Mastodon.auth_request_url( oauth = Mastodon.auth_request_url(
masto, masto,
scopes=["read"], scopes=["read"],
@ -66,20 +117,21 @@ def login():
redirect_uris=REDIRECT, redirect_uris=REDIRECT,
state=instance, state=instance,
) )
print(f"Sending user to: {REDIRECT}", flush=True)
return redirect(oauth) return redirect(oauth)
except Exception as e: except Exception as e:
print(e) print(e)
return Response("fail", status=400) return Response("fail", status=400)
@app.get("/callback") @app.get("/fedi-auth/callback")
def callback(): def callback():
oauth = request.args.get("code") oauth = request.args.get("code")
instance = request.args.get("state") instance = request.args.get("state")
print(f"oauth: {oauth is not None} instance: {instance is not None}")
if oauth != None and instance != None: if oauth != None and instance != None:
try: try:
with shelve.open(SHELVE_LOCATION) as apps: app = get_application(instance)
app = apps.get(instance)
if app != None: if app != None:
masto = Mastodon( masto = Mastodon(
client_id=app[0], client_id=app[0],
@ -88,8 +140,13 @@ def callback():
) )
Mastodon.log_in(masto, code=oauth, scopes=["read"]) Mastodon.log_in(masto, code=oauth, scopes=["read"])
creds = Mastodon.account_verify_credentials(masto) creds = Mastodon.account_verify_credentials(masto)
handle = f"{creds.acct}@{instance}"
zulip_user = get_or_create_zulip_user(handle)
print(zulip_user)
token = jwt.encode( token = jwt.encode(
{"email": f"{creds.acct}@{instance}"}, {"email": handle},
SECRET, SECRET,
algorithm="HS256", algorithm="HS256",
) )
@ -107,9 +164,8 @@ def callback():
</body> </body>
</html> </html>
""" """
except: except Exception as e:
print(e)
pass pass
print("Some field wasn't set.")
return Response("fail", status=400) return Response("fail", status=400)
http_server = WSGIServer(("127.0.0.1", PORT), app)
http_server.serve_forever()

View File

@ -1,3 +1,7 @@
flask flask
markupsafe markupsafe
mastodon mastodon.py
pyjwt
gevent
gunicorn
zulip