-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.py
157 lines (112 loc) · 3.72 KB
/
main.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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python3
"""
A FastApi server with Google OAuth2 that
can manage code-server instances
"""
import json
import os
from random import choice
from string import ascii_letters, digits
import aiofiles
import uvicorn
from aiohttp import ClientSession
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.responses import HTMLResponse, RedirectResponse
from modules.oauth2 import generate_oauth2_url, github_oauth2
from modules.server_starter import start_code_server
from modules.startup_tasks import startup_tasks
class CApp(FastAPI):
"""
Custom FastAPI object
"""
http_sess: ClientSession
app = CApp()
load_dotenv()
startup_tasks()
LETTERS_AND_DIGITS = ascii_letters + digits
EXPIRE_TIME = int(os.getenv("EXPIRE_TIME", "30"))
SOCKET_FOLDER = os.getenv("SOCKET_FOLDER", "/run/code_server_sockets")
API_FOLDER = os.getenv("API_FOLDER", "/run/code_server_pm")
# read HTML template
with open("response.html", "r", encoding="utf8") as template_html:
TEMPLATE_HTML = template_html.read()
# read users' local usernames
with open("users.json", "r", encoding="utf8") as config:
allowed_users: dict = json.load(config)
@app.on_event("startup")
async def startup_event():
"""
Startup tasks
"""
# create session to reuse
app.http_sess = ClientSession()
@app.on_event("shutdown")
async def shutdown_event():
"""
Shutdown tasks
"""
# close session
await app.http_sess.close()
@app.get("/")
async def main_login():
"""
Login
Redirect user to login with Google for OAuth2
"""
return RedirectResponse(url=generate_oauth2_url(), status_code=302)
@app.get("/reset_session")
async def reset_login():
"""
Login
Redirect user to login with Google for OAuth2
"""
return RedirectResponse(
url=generate_oauth2_url(redirect_uri_extension="reset_session"), status_code=302
)
@app.get("/oauth2/callback")
async def callback(code: str):
"""
Login callback
Get code and exchange token
Use token to get user data
Check for user email and local username
Start a code-server instance for user
Return URL to use code-server
"""
user = await github_oauth2(app, code, allowed_users)
async with aiofiles.open(
API_FOLDER + "/routes.json", "r", encoding="utf8"
) as routes:
socket_paths = json.loads(await routes.read())
for session_id, socket_path in socket_paths.items():
if socket_path == f"{SOCKET_FOLDER}/{user}_code_server.sock":
return HTMLResponse(TEMPLATE_HTML.replace("%pls-replace-me%", session_id))
# create a path prefix
session_id = ""
for _ in range(64):
session_id += choice(LETTERS_AND_DIGITS)
# start code_server
socket_path = await start_code_server(user=user, expire_time=EXPIRE_TIME)
socket_paths[session_id] = socket_path
async with aiofiles.open(
API_FOLDER + "/routes.json", "w", encoding="utf8"
) as routes:
await routes.write(json.dumps(socket_paths))
# redirect user to code-server
return HTMLResponse(TEMPLATE_HTML.replace("%pls-replace-me%", session_id))
@app.get("/oauth2/callback/reset_session")
async def reset_session(code: str):
"""
Auth user through oauth2 and reset session
"""
user = await github_oauth2(app, code, allowed_users)
for session_id, socket_path in socket_paths.items():
if socket_path == f"{SOCKET_FOLDER}/{user}_code_server.sock":
socket_paths.pop(session_id)
async with aiofiles.open(
API_FOLDER + "/routes.json", "w", encoding="utf8"
) as routes:
await routes.write(json.dumps(socket_paths))
if __name__ == "__main__":
uvicorn.run("main:app", uds=API_FOLDER + "/auth-vsus.tobycm.ga.sock")