Add basic authorization support

This commit is contained in:
C5H12O5
2023-11-07 16:27:52 +08:00
parent 645e4562e6
commit bbba0797c1
3 changed files with 108 additions and 13 deletions

1
.gitignore vendored
View File

@ -382,3 +382,4 @@ cython_debug/
INFO
.cache_*
scrapeflows.conf
configserver/authorization

View File

@ -32,7 +32,7 @@ def render_index(saved=None):
"movie": "selected" if "movie" in types else "disabled",
"tvshow": "selected" if "tvshow" in types else "disabled",
"priority": len(sites),
"config": config_html
"config": config_html,
}
if saved_conf is not None:
saved_types = saved_conf["types"]
@ -101,33 +101,66 @@ _index_html = render_index()
class RequestHandler(http.server.SimpleHTTPRequestHandler):
"""Request handler for the HTTP server."""
def do_AUTH(self):
filepath = _basedir / "authorization"
if not filepath.exists():
return True
with open(filepath, "r", encoding="utf-8") as reader:
saved_auth = reader.read()
if self.headers.get("Authorization") is not None:
auth_header = self.headers.get("Authorization")
if auth_header.split("Basic ")[1] == saved_auth:
return True
self.send_response(401)
self.send_header("WWW-Authenticate", 'Basic realm="Login Required"')
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"Unauthorized")
return False
def do_GET(self):
if not self.do_AUTH():
return
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
conf_path = _basedir / "../scrapeflows.conf"
if conf_path.exists():
with open(conf_path, "r", encoding="utf-8") as reader:
filepath = _basedir / "../scrapeflows.conf"
if filepath.exists():
with open(filepath, "r", encoding="utf-8") as reader:
saved_conf = json.load(reader)
self.wfile.write(render_index(saved_conf).encode("utf-8"))
else:
self.wfile.write(_index_html.encode("utf-8"))
elif self.path.endswith("/exit"):
elif self.path == "/exit":
self.send_response(200)
self.end_headers()
self.server.server_close()
sys.exit()
def do_POST(self):
content_length = int(self.headers["Content-Length"])
body = self.rfile.read(content_length)
with open(_basedir / "../scrapeflows.conf", "w", encoding="utf-8") as w:
w.write(body.decode("utf-8"))
self.send_response(200)
self.end_headers()
if not self.do_AUTH():
return
filepath = None
if self.path == "/save":
filepath = _basedir / "../scrapeflows.conf"
elif self.path == "/auth":
filepath = _basedir / "authorization"
if filepath is not None:
content_length = int(self.headers["Content-Length"])
body = self.rfile.read(content_length)
with open(filepath, "w", encoding="utf-8") as w:
w.write(body.decode("utf-8"))
self.send_response(200)
self.end_headers()
if __name__ == "__main__":

View File

@ -19,6 +19,12 @@
<ion-icon name="open-outline"></ion-icon>
</a>
</li>
<li>
<a href="#auth" class="modal-trigger">
Auth
<ion-icon name="person-circle-outline"></ion-icon>
</a>
</li>
<li>
<a href="#exit" class="modal-trigger">
Exit
@ -68,6 +74,26 @@
</div>
</div>
<div id="auth" class="modal">
<div class="modal-content">
<h6>Enable basic authentication?</h6>
<div class="row">
<div class="input-field col s6">
<input id="username" type="text" class="validate">
<label for="username">Username</label>
</div>
<div class="input-field col s6">
<input id="password" type="password" class="validate">
<label for="password">Password</label>
</div>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect btn-flat">Cancel</a>
<a id="auth-btn" class="modal-close waves-effect waves-green btn-flat">Confirm</a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
@ -99,10 +125,45 @@
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
body: JSON.stringify(data, null, 2)
}).then(response => {
if (response.ok) {
M.toast({ html: 'Save successful!', classes: 'center-align green lighten-2' })
M.toast({
html: 'Save successful!',
classes: 'center-align green lighten-2',
displayLength: 1000
})
} else {
throw new Error(response.statusText);
}
}).catch(error => {
M.toast({ html: error, classes: 'center-align red lighten-2' })
});
});
var authBtn = document.getElementById('auth-btn');
authBtn.addEventListener('click', event => {
event.preventDefault();
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
if (username == '' && password == '') {
return;
}
fetch('/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: btoa(username + ':' + password)
}).then(response => {
if (response.ok) {
M.toast({
html: 'Authentication enabled!',
classes: 'center-align green lighten-2',
displayLength: 1000
})
} else {
throw new Error(response.statusText);
}