Basic email verification

This commit is contained in:
BlenderDumbass 2024-12-03 00:30:02 +02:00
parent 1f6dd90118
commit 84d8eb4451
6 changed files with 318 additions and 7 deletions

View file

@ -82,7 +82,7 @@ def RandString(n=50):
return s return s
IDColors = {} IDColors = {" ANONYMOUS ":clr["norm"]}
def consoleForm(obj): def consoleForm(obj):

163
modules/Email.py Normal file
View file

@ -0,0 +1,163 @@
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import json
from modules import Set
from modules.Common import *
from modules import Render
verifying = {}
def Send(reciever, subject, html):
config = Set.Load()
Email = config.get("email")
if not Email:
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" No email credentials!")
try: port = int(Email.get("port", ""))
except: port = 587
message = MIMEMultipart()
message["From"] = Email["visible"]
message["To"] = reciever
message["Subject"] = subject
message.attach(MIMEText(html, "html"))
# Send the email
with smtplib.SMTP(Email["server"], port) as server:
server.starttls()
server.login(Email["sender"],
Email["password"])
server.sendmail(Email["sender"],
reciever,
message.as_string())
print('Email "'+subject+'" was sent to', reciever)
def VerifyPage(server):
user = Render.validate(server.cookie)
# Authorization check
if not user:
Render.AccessDenied(server)
return
config = Set.Load()
# Generating <head>
html = Render.head(title = "Verify Email",
description = "Verify Email",
config = config
)
html = html + Render.Button(config.get("title", "My Website"), "/", image=config.get("favicon", "/icon/internet"))
code = server.parsed.get("code", [""])[0]
if not code:
html = html + """
<div class="middle_section_article">
<div class="dark_box">
<form action="email_verify">
<center>
<br>
You have recieved an email with a code.<br><br>
<small>It could be in spam.</small>
<br><br>
<input class="button" name="code" required placeholder="Your code...">
<br>
<button class="button" type="submit">
<img alt="[icon ok]" style="vertical-align: middle" src="/icon/ok">
Verify
</button>
</center>
</form>
</div>
</div>
"""
code = RandString(10)
verifying[code] = user.get("email")
text = """
<center>
Your Verification Code
<br>
<div class="toot">"""+code+"""
</div>
</center>
"""
text = Format(text)
Send(user.get("email", ""), "Verification Code", text)
Render.send(server, html, 200)
else:
# If we recieved the code.
if code not in verifying:
print("code not in verifying")
Render.AccessDenied(server)
return
if verifying[code] != user.get("email"):
print("email wrong")
Render.AccessDenied(server)
return
user["email_verified"] = True
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Render.Redirect(server, "/settings#email")
def Format(text):
config = Set.Load()
favicon = config.get("favicon", "")
if favicon.startswith("/"):
favicon = "https://"+config.get("domain", "")+favicon
css = open(config.get("css", "default.css"), "r")
html = '<style>'+css.read()+'</style>'
html = html + '<div class="dark_box"><center>'
if favicon:
html = html + '<img alt="[favicon]" style="height:40px;vertical-align: middle" src="'+favicon+'">'
html = html + " "+config.get("title", "My Website")
html = html + "</center><br></div>"
html = html + '<div class="dark_box">'
html = html + text
html = html + '</div>'
return html

View file

@ -33,6 +33,7 @@ def Set():
print(clr["tdyl"]+"--tagline"+clr["norm"]+" - Set tagline of the website.") print(clr["tdyl"]+"--tagline"+clr["norm"]+" - Set tagline of the website.")
print(clr["tdyl"]+"--domain"+clr["norm"]+" - Let the server know the clearnet domain.") print(clr["tdyl"]+"--domain"+clr["norm"]+" - Let the server know the clearnet domain.")
print(clr["tdyl"]+"--tor"+clr["norm"]+" - Let the server know the tor domain.") print(clr["tdyl"]+"--tor"+clr["norm"]+" - Let the server know the tor domain.")
print(clr["tdyl"]+"--email"+clr["norm"]+" - Set Email SMTP account for internal automatic messages.")
print(clr["tdyl"]+"--port"+clr["norm"]+" - Set port where to run the website.") print(clr["tdyl"]+"--port"+clr["norm"]+" - Set port where to run the website.")
print(clr["tdyl"]+"--css"+clr["norm"]+" - Set a CSS file.") print(clr["tdyl"]+"--css"+clr["norm"]+" - Set a CSS file.")
print(clr["tdyl"]+"--css_edit"+clr["norm"]+" - edit a CSS file.") print(clr["tdyl"]+"--css_edit"+clr["norm"]+" - edit a CSS file.")

View file

@ -747,6 +747,8 @@ def AccountPage(server, account):
html = html + '<center>' html = html + '<center>'
html = html + '<img style="vertical-align: middle" src="/icon/frase">' html = html + '<img style="vertical-align: middle" src="/icon/frase">'
html = html + '<a href="mailto:'+email+'"> '+email+'</a>' html = html + '<a href="mailto:'+email+'"> '+email+'</a>'
if Accounts.get(account, {}).get("email_verified"):
html = html + '<img title="Email Verified!" style="vertical-align: middle" src="/icon/ok">'
html = html + '</center>' html = html + '</center>'
# Mastodon # Mastodon
@ -921,6 +923,13 @@ def LoginPage(server):
<br> <br>
<img style="vertical-align: middle" src="/icon/lock"> <img style="vertical-align: middle" src="/icon/lock">
<input class="button" style="width:90%" maxlength="500" id="password" type="password" required="" name="password" placeholder="Password..."></input><br> <input class="button" style="width:90%" maxlength="500" id="password" type="password" required="" name="password" placeholder="Password..."></input><br>
<div class="button">
<input type="checkbox" name="logout">
<label>Log Out Other Sessions</label>
</div>
<br><br>
""" """
if redirect: if redirect:
@ -1110,9 +1119,44 @@ def SettingsPage(server):
</form> </form>
</div> </div>
""" """
if user.get("email"):
if not user.get("email_verified"):
html = html + """
<div class="dark_box" id="email">
<center><h2>Email Settings</h2>
<br>
Email """+Safe(user.get("email"))+""" is not verified.
<br><br>
"""+Button("Verify", "/email_verify", "ok")+"""
</center>
</div>
"""
else:
html = html + """
<div class="dark_box" id="email">
<form action="email_update">
<center><h2>Email Settings</h2>
<br>
Email """+Safe(user.get("email"))+""" is verified!
<br><br>
</center>
</form>
</div>
"""
# Current Logged in Sessions # Current Logged in Sessions
sessions = user.get("sessions", {}) sessions = user.get("sessions", {})
@ -1173,6 +1217,26 @@ def SettingsPage(server):
</form> </form>
</div> </div>
<div class="dark_box">
<center><h2>Change Password</h2></center>
<form action="change_password" method="post">
<img style="vertical-align: middle" src="/icon/unlock">
<input class="button" style="width:90%" maxlength="200" required type="password" name="password" title="Old Password" placeholder="Old Password">
<img style="vertical-align: middle" src="/icon/lock">
<input class="button" style="width:90%" maxlength="200" required type="password" name="new_password" title="New Password" placeholder="New Password">
<button class="button" type="submit">
<img style="vertical-align: middle" src="/icon/ok">
Change
</button>
</div>
</div> </div>
""" """
@ -1809,6 +1873,7 @@ def Login(server):
hashed = hashlib.sha512(password.encode("utf-8")).hexdigest() hashed = hashlib.sha512(password.encode("utf-8")).hexdigest()
redirect = server.parsed.get("redirect" , [""])[0] redirect = server.parsed.get("redirect" , [""])[0]
logout = server.parsed.get("logout" , [""])[0]
Accounts = accounts() Accounts = accounts()
@ -1825,11 +1890,10 @@ def Login(server):
account["sessions"] = {} account["sessions"] = {}
account["sessions"][server.cookie] = server.headers.get("User-Agent") account["sessions"][server.cookie] = server.headers.get("User-Agent")
f = Set.Folder() f = Set.Folder()
folder = f+"/accounts" folder = f+"/accounts"
with open(folder+"/"+username+".json", "w") as save:
json.dump(account, save, indent=4)
# Move the cookie arround # Move the cookie arround
# When a login happens, we want to make the server know # When a login happens, we want to make the server know
# which articles the person already read and stuff. So # which articles the person already read and stuff. So
@ -1859,8 +1923,17 @@ def Login(server):
with open(f+"/tabs"+article.get("url")+"/metadata.json", "w") as save: with open(f+"/tabs"+article.get("url")+"/metadata.json", "w") as save:
json.dump(article, save, indent=4) json.dump(article, save, indent=4)
if logout:
for cookie in list(account["sessions"].keys()):
if cookie != server.cookie:
del account["sessions"][cookie]
with open(folder+"/"+username+".json", "w") as save:
json.dump(account, save, indent=4)
if not redirect: if not redirect:
Redirect(server, "/settings") Redirect(server, "/settings")
else: else:
@ -1992,6 +2065,11 @@ def UpdateAccount(server):
for key in keys: for key in keys:
data = server.parsed.get(key, [""])[0] data = server.parsed.get(key, [""])[0]
# Making sure to reverify email.
if key == "email" and user[key] != data:
user["email_verified"] = False
user[key] = Safe(data) user[key] = Safe(data)
f = Set.Folder() f = Set.Folder()
@ -2001,6 +2079,31 @@ def UpdateAccount(server):
Redirect(server, "/settings") Redirect(server, "/settings")
def UpdatePassword(server):
user = validate(server.cookie)
old_password = server.parsed.get("password", [""])[0]
new_password = server.parsed.get("new_password", [""])[0]
old_hashed = hashlib.sha512(old_password.encode("utf-8")).hexdigest()
new_hashed = hashlib.sha512(new_password.encode("utf-8")).hexdigest()
# Validating the user's password
if user.get("password", "") == old_hashed:
user["password"] = new_hashed
f = Set.Folder()
folder = f+"/accounts"
with open(folder+"/"+user.get("username", "")+".json", "w") as save:
json.dump(user, save, indent=4)
Redirect(server, "/settings")
AccessDenied(server)
def UpdatePublicationRights(server): def UpdatePublicationRights(server):
user = validate(server.cookie) user = validate(server.cookie)

View file

@ -69,7 +69,11 @@ class handler(BaseHTTPRequestHandler):
# Failing early to make sure that nobody will # Failing early to make sure that nobody will
# try attacking this part of the server. # try attacking this part of the server.
commands = ["do_edit", "do_login", "do_register"] commands = ["do_edit",
"do_login",
"do_register",
"change_password"]
found = False found = False
for i in commands: for i in commands:
if i in self.path: if i in self.path:
@ -106,6 +110,9 @@ class handler(BaseHTTPRequestHandler):
elif self.path[1:].startswith("do_login"): elif self.path[1:].startswith("do_login"):
Render.Login(self) Render.Login(self)
elif self.path[1:].startswith("change_password"):
Render.UpdatePassword(self)
elif self.path[1:].startswith("do_register"): elif self.path[1:].startswith("do_register"):
Render.Register(self) Render.Register(self)
@ -164,6 +171,11 @@ class handler(BaseHTTPRequestHandler):
elif self.path[1:].startswith("editor"): elif self.path[1:].startswith("editor"):
Render.EditorPage(self) Render.EditorPage(self)
elif self.path[1:].startswith("email_verify"):
from modules import Email
Email.VerifyPage(self)
elif self.path[1:].startswith("register"): elif self.path[1:].startswith("register"):
Render.RegisterPage(self) Render.RegisterPage(self)

View file

@ -106,6 +106,10 @@ def Set():
print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the editor!") print(clr["bold"]+clr["tdrd"]+"Error:"+clr["norm"]+" Didn't specify the editor!")
print('Use: $ python3 run.py --set --editor emacs') print('Use: $ python3 run.py --set --editor emacs')
if "--email" in sys.argv:
Email()
if "--port" in sys.argv: if "--port" in sys.argv:
try: try:
@ -281,3 +285,31 @@ def SetFavicon(filename):
print("New favicon is set at "+clr["bold"]+Folder()+"/pictures/favicon.png"+clr["norm"]) print("New favicon is set at "+clr["bold"]+Folder()+"/pictures/favicon.png"+clr["norm"])
def Email():
print("Please make sure your email provider supports")
print("SMTP protocol.")
server = input("SMTP server : ")
port = input("SMTP port : ")
username = input("Username : ")
password = input("Password : ")
sender = input("Sender Address : ")
visible = input("Visible Address: ")
config = Load()
email = {
"server":server,
"port":port,
"username":username,
"password":password,
"sender":sender,
"visible":visible
}
config["email"] = email
Save(config)
print("Email credentials saved!")