Basic email verification
This commit is contained in:
parent
1f6dd90118
commit
84d8eb4451
6 changed files with 318 additions and 7 deletions
|
@ -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
163
modules/Email.py
Normal 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
|
||||||
|
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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
|
||||||
|
@ -922,6 +924,13 @@ def LoginPage(server):
|
||||||
<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:
|
||||||
html = html + '<input type="hidden" name="redirect" value="'+Safe(redirect)+'">'
|
html = html + '<input type="hidden" name="redirect" value="'+Safe(redirect)+'">'
|
||||||
|
@ -1113,6 +1122,41 @@ def SettingsPage(server):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
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,10 +1890,9 @@ 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
|
||||||
|
@ -1860,6 +1924,15 @@ 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")
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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!")
|
||||||
|
|
Loading…
Add table
Reference in a new issue