Compare commits

..

No commits in common. "master" and "prime" have entirely different histories.

10 changed files with 192 additions and 219 deletions

13
app/.gitignore vendored
View File

@ -1,11 +1,8 @@
.ssh .ssh/
.venv
.beaker/data/*
!.beaker/data/.noremove
**/__pycache__ **/__pycache__
*.pyc *.pyc
venv/
.DS_Store env/
.beaker/data/*
!.beaker/data/.noremove
nohup.out nohup.out

View File

@ -119,21 +119,18 @@ menu = {
], ],
"sidebar": [ "sidebar": [
{ {
"icon":"fa-solid fa-gauge",
"name":"Dashboard", "name":"Dashboard",
"target":"_self", "target":"_self",
"href":"/dashboard", "href":"/dashboard",
"roles":[1,2] "roles":[1,2]
}, },
{ {
"icon":"fa-solid fa-user-tag",
"name":"Roles", "name":"Roles",
"target":"_self", "target":"_self",
"href":"/dashboard/roles", "href":"/dashboard/roles",
"roles":[1] "roles":[1]
}, },
{ {
"icon":"fa-solid fa-address-card",
"name":"Users", "name":"Users",
"target":"_self", "target":"_self",
"href":"/dashboard/users", "href":"/dashboard/users",

View File

@ -230,32 +230,27 @@ def index():
print(str(e)) print(str(e))
return json.dumps({}, indent = 2).encode() return json.dumps({}, indent = 2).encode()
@app.route('/api/auth/password/forgot', method='POST') @app.route('/api/auth/password/forgot/<type>', method='POST')
def index(): def index(type):
try: try:
params = request.json params = request.json
params["mako"] = { params["type"] = type
"email" : template_email.main(directory.page["email"], "reset") if type == "send":
} params["mako"] = {
"email" : template_email.main(directory.page["email"], "reset")
}
elif type == "change":
params["mako"] = {
"email" : template_email.main(directory.page["email"], "message")
}
else:
pass
response.content_type = 'application/json' response.content_type = 'application/json'
return json.dumps(api_auth.auth().forgot(params), indent = 2).encode() return json.dumps(api_auth.auth().forgot(params), indent = 2).encode()
except Exception as e: except Exception as e:
print(str(e)) print(str(e))
return json.dumps({}, indent = 2).encode() return json.dumps({}, indent = 2).encode()
@app.route('/api/auth/password/reset', method='POST')
def index():
try:
params = request.json
params["mako"] = {
"email" : template_email.main(directory.page["email"], "message")
}
response.content_type = 'application/json'
return json.dumps(api_auth.auth().reset(params), indent = 2).encode()
except Exception as e:
print(str(e))
return json.dumps({}, indent = 2).encode()
@app.route('/api/dashboard/roles/list', method='POST') @app.route('/api/dashboard/roles/list', method='POST')
def index(): def index():
try: try:

View File

@ -1,13 +1,10 @@
python3 -m venv .venv # Create .venv sudo apt-get install -y python3-pip
pip install --upgrade pip
pip install bottle # Micro Framework
pip install gunicorn # WSGI Server Backend
pip install beaker # Session & caching library
pip install mako # Template library
pip install mysql-connector # Database
.venv/bin/pip3 install --upgrade pip # Upgrade pip pip install bcrypt
pip install pyjwt[crypto]
.venv/bin/pip3 install bottle # Micro Framework
.venv/bin/pip3 install gunicorn # WSGI Server Backend
.venv/bin/pip3 install beaker # Session & caching library
.venv/bin/pip3 install mako # Template library
.venv/bin/pip3 install mysql-connector # Database connector
.venv/bin/pip3 install bcrypt # Password hash
.venv/bin/pip3 install pyjwt[crypto] # JWT
.venv/bin/pip3 install requests # For HTTP Request (Recaptcha need a POST HTTP requests)

View File

@ -465,109 +465,101 @@ class auth:
return response return response
def forgot(self, params): def forgot(self, params):
APIADDR = "/api/auth/password/forgot" APIADDR = "/api/auth/password/forgot/:type"
loggorilla.prcss(APIADDR, "Define parameters")
response = {} response = {}
type = params["type" ] # POST: send / change
self.cursor.execute("BEGIN;") self.cursor.execute("BEGIN;")
try: try:
loggorilla.prcss(APIADDR, "Define parameters") loggorilla.fyinf(APIADDR, f"type: {type}")
email = params["email"].lower() if type == "send":
loggorilla.prcss(APIADDR, "Get dependency data") loggorilla.prcss(APIADDR, "Define parameters inside decision")
self.cursor.execute(f"SELECT COUNT(*) AS `count`, auth_profile.token, auth_profile.email FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.email = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 1 ; ", (email,) ) email = params["email"].lower()
result_verified = self.cursor.fetchone() loggorilla.prcss(APIADDR, "Get dependency data")
if result_verified["count"] >= 1: self.cursor.execute(f"SELECT COUNT(*) AS `count`, auth_profile.token, auth_profile.email FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.email = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 1 ; ", (email,) )
loggorilla.prcss(APIADDR, "Get token") result_verified = self.cursor.fetchone()
token = result_verified["token"].decode() if result_verified["count"] >= 1:
loggorilla.prcss(APIADDR, "Generate URL") loggorilla.prcss(APIADDR, "Get token")
# TODO: set expired time token = result_verified["token"].decode()
expired = datetime.datetime.now() + datetime.timedelta(minutes=30) # Can be hours or minutes loggorilla.prcss(APIADDR, "Generate URL")
expired_isoformat = expired.isoformat() # TODO: set expired time
payload = { expired = datetime.datetime.now() + datetime.timedelta(minutes=30) # Can be hours or minutes
"token" : token, expired_isoformat = expired.isoformat()
"expired": expired_isoformat payload = {
} "token" : token,
# TODO: Config SSH key for tokenguard and set forgot URL "expired": expired_isoformat
token_encrypt = tokenguard.encode(payload, globalvar.ssh['key']['private'], globalvar.ssh['passphrase']) }
change_forgot_url = globalvar.change_forgot_url(token_encrypt) # TODO: Config SSH key for tokenguard and set forgot URL
loggorilla.prcss(APIADDR, "Sending email") token_encrypt = tokenguard.encode(payload, globalvar.ssh['key']['private'], globalvar.ssh['passphrase'])
self.smtpconfig['subject' ] = f"{globalvar.title} forgot password" change_forgot_url = globalvar.change_forgot_url(token_encrypt)
self.smtpconfig['to' ] = email loggorilla.prcss(APIADDR, "Sending email")
self.smtpconfig['text' ] = f"Please visit this link to reset password: {change_forgot_url}. Avoid the link if you are not request this." self.smtpconfig['subject' ] = f"{globalvar.title} forgot password"
self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render( self.smtpconfig['to' ] = email
title = globalvar.title, self.smtpconfig['text' ] = f"Please visit this link to reset password: {change_forgot_url}. Avoid the link if you are not request this."
header = globalvar.title, self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render(
copyright = globalvar.copyright, title = globalvar.title,
container = Template(params["mako"]["email"]['container']).render( header = globalvar.title,
reset = change_forgot_url copyright = globalvar.copyright,
container = Template(params["mako"]["email"]['container']).render(
reset = change_forgot_url
)
) )
) sendwave.smtp(self.smtpconfig)
sendwave.smtp(self.smtpconfig) loggorilla.prcss(APIADDR, "Giving response")
loggorilla.prcss(APIADDR, "Giving response") response["status" ] = "success"
response["status" ] = "success" response["desc" ] = "Check email for password change."
response["desc" ] = "Check email for password change." else:
response["status" ] = "failed"
response["desc" ] = "The parameters seems suspicious and you are not authorized for that"
elif type == "change":
loggorilla.prcss(APIADDR, "Define parameters inside decision")
token_encrypt = params["token" ]
password = params["password" ]
loggorilla.prcss(APIADDR, "Decrypt token")
payload = tokenguard.decode(token_encrypt, globalvar.ssh['key']['public'])
token = payload['token']
expired = datetime.datetime.fromisoformat(payload['expired'])
loggorilla.prcss(APIADDR, "Process parameters")
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
loggorilla.prcss(APIADDR, "Get dependency data")
self.cursor.execute(f"SELECT COUNT(*) AS `count`, auth_profile.email FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.token = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 1 ; ", (token,) )
result_verified = self.cursor.fetchone()
email = result_verified['email']
loggorilla.prcss(APIADDR, "Validation")
if datetime.datetime.now() > expired:
response["status" ] = "failed"
response["desc" ] = "Expired"
elif len(password) < 6:
response["status" ] = "failed"
response["desc" ] = "password too short"
elif result_verified["count"] == 0:
response["status" ] = "failed"
response["desc" ] = "Forbidden: No active account for this"
response["data" ] = {
"message": "Please contact us if you still had a problem"
}
else:
loggorilla.prcss(APIADDR, "Updating")
self.cursor.execute("UPDATE `auth` SET `password` = %s, `when_update` = NOW() WHERE `token` = %s", (hashed, token) )
loggorilla.prcss(APIADDR, "Sending email")
self.smtpconfig['subject' ] = f"{globalvar.title} password change success"
self.smtpconfig['to' ] = email
self.smtpconfig['text' ] = f"You had change your password."
self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render(
title = globalvar.title,
header = globalvar.title,
copyright = globalvar.copyright,
container = Template(params["mako"]["email"]['container']).render(
message = f"You had change your password."
)
)
sendwave.smtp(self.smtpconfig)
loggorilla.prcss(APIADDR, "Giving response")
response["status" ] = "success"
response["desc" ] = "password change success"
else: else:
response["status" ] = "failed" response["status" ] = "failed"
response["desc" ] = "The parameters seems suspicious and you are not authorized for that" response["desc" ] = "forbidden"
except Exception as e:
self.cursor.execute("ROLLBACK;")
loggorilla.error(APIADDR, str(e) )
response["status" ] = "failed"
response["desc" ] = "Internal Server Error. Please contact us if you still have an error. for detail"
finally:
self.cursor.execute("COMMIT;")
self.cursor.close()
self.db_main.close()
return response
def reset(self, params):
APIADDR = "/api/auth/password/reset"
response = {}
self.cursor.execute("BEGIN;")
try:
loggorilla.prcss(APIADDR, "Define parameters")
token_encrypt = params["token" ]
password = params["password" ]
loggorilla.prcss(APIADDR, "Decrypt token")
payload = tokenguard.decode(token_encrypt, globalvar.ssh['key']['public'])
token = payload['token']
expired = datetime.datetime.fromisoformat(payload['expired'])
loggorilla.prcss(APIADDR, "Process parameters")
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
loggorilla.prcss(APIADDR, "Get dependency data")
self.cursor.execute(f"SELECT COUNT(*) AS `count`, auth_profile.email FROM auth_profile_verification INNER JOIN auth_profile ON auth_profile.id = auth_profile_verification.auth_profile WHERE auth_profile.token = %s AND auth_profile_verification.type = 'email' AND auth_profile_verification.verified = 1 ; ", (token,) )
result_verified = self.cursor.fetchone()
email = result_verified['email']
loggorilla.prcss(APIADDR, "Validation")
if datetime.datetime.now() > expired:
response["status" ] = "failed"
response["desc" ] = "Expired"
elif len(password) < 6:
response["status" ] = "failed"
response["desc" ] = "password too short"
elif result_verified["count"] == 0:
response["status" ] = "failed"
response["desc" ] = "Forbidden: No active account for this"
response["data" ] = {
"message": "Please contact us if you still had a problem"
}
else:
loggorilla.prcss(APIADDR, "Updating")
self.cursor.execute("UPDATE `auth` SET `password` = %s, `when_update` = NOW() WHERE `token` = %s", (hashed, token) )
loggorilla.prcss(APIADDR, "Sending email")
self.smtpconfig['subject' ] = f"{globalvar.title} password change success"
self.smtpconfig['to' ] = email
self.smtpconfig['text' ] = f"You had change your password."
self.smtpconfig['html' ] = Template(params["mako"]["email"]['index']).render(
title = globalvar.title,
header = globalvar.title,
copyright = globalvar.copyright,
container = Template(params["mako"]["email"]['container']).render(
message = f"You had change your password."
)
)
sendwave.smtp(self.smtpconfig)
loggorilla.prcss(APIADDR, "Giving response")
response["status" ] = "success"
response["desc" ] = "password change success"
except Exception as e: except Exception as e:
self.cursor.execute("ROLLBACK;") self.cursor.execute("ROLLBACK;")
loggorilla.error(APIADDR, str(e) ) loggorilla.error(APIADDR, str(e) )

View File

@ -1,8 +1,9 @@
<div class="container mb-5"> <h1>Here is Dashboard!</h1>
<h1>Here is Dashboard!</h1>
% if 4 in user['profile']['roles']: % if 4 in user['profile']['roles']:
<!-- Debug Section -->
<!-- Debug Section -->
<div class="container mb-5">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="card"> <div class="card">
@ -19,6 +20,6 @@
</div> </div>
</div> </div>
</div> </div>
% endif
</div> </div>
% endif

View File

@ -1,45 +1,42 @@
<div class="container mb-5"> <div class="row">
<div class="row"> <div class="col">
<div class="col"> <h1 class="h3">Roles</h1>
<h1 class="h3">Roles</h1> <input type="hidden" id="form-token" value="${token}">
<input type="hidden" id="form-token" value="${token}"> <div class="table-responsive">
<div class="table-responsive"> <table class="table table-sm table-bordered table-striped" id="table-roles" width="100%" cellspacing="0">
<table class="table table-sm table-bordered table-striped" id="table-roles" width="100%" cellspacing="0"> <thead class="table-primary">
<thead class="table-primary"> <tr>
<tr> <th>ID</th>
<th>ID</th> <th>Name</th>
<th>Name</th> <th>Users</th>
<th>Users</th> <th>Action</th>
<th>Action</th> </tr>
</tr> <tr>
<tr> <th>
<th> <input class="form-control form-control-sm" placeholder="ID" id="form-add-id">
<input class="form-control form-control-sm" placeholder="ID" id="form-add-id"> </th>
</th> <th>
<th> <input class="form-control form-control-sm" placeholder="Name" id="form-add-name">
<input class="form-control form-control-sm" placeholder="Name" id="form-add-name"> </th>
</th> <th></th>
<th></th> <th>
<th> <button class="btn btn-primary btn-sm" type="button" onclick="submitAdd()">
<button class="btn btn-primary btn-sm" type="button" onclick="submitAdd()"> <span class="fa fa-plus"></span> Add
<span class="fa fa-plus"></span> Add </button>
</button> </th>
</th> </tr>
</tr> </thead>
</thead> <tbody></tbody>
<tbody></tbody> </table>
</table>
</div>
<!-- End table-responsive -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" src="/js/carrack.js"></script>
<script type="text/javascript" src="/js/dashboard/roles.js"></script>
</div> </div>
<!-- End col --> <!-- End table-responsive -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" src="/js/carrack.js"></script>
<script type="text/javascript" src="/js/dashboard/roles.js"></script>
</div> </div>
<!-- End row --> <!-- End col -->
</div> </div>
<!-- End container --> <!-- End row -->

View File

@ -1,34 +1,31 @@
<div class="container mb-5"> <div class="row">
<div class="row"> <div class="col">
<div class="col"> <h1 class="h3">Users</h1>
<h1 class="h3">Users</h1> <input type="hidden" id="form-token" value="${token}">
<input type="hidden" id="form-token" value="${token}"> <div class="table-responsive">
<div class="table-responsive"> <table class="table table-sm table-bordered table-striped" id="table-users" width="100%" cellspacing="0">
<table class="table table-sm table-bordered table-striped" id="table-users" width="100%" cellspacing="0"> <thead class="table-primary">
<thead class="table-primary"> <tr>
<tr> <th>ID</th>
<th>ID</th> <th>Username</th>
<th>Username</th> <th>Email</th>
<th>Email</th> <th>Phone</th>
<th>Phone</th> <th>Roles</th>
<th>Roles</th> <th>Verification</th>
<th>Verification</th> <th>Action</th>
<th>Action</th> </tr>
</tr> </thead>
</thead> <tbody></tbody>
<tbody></tbody> </table>
</table>
</div>
<!-- End table-responsive -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" src="/js/carrack.js"></script>
<script type="text/javascript" src="/js/dashboard/users.js"></script>
</div> </div>
<!-- End col --> <!-- End table-responsive -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" src="/js/carrack.js"></script>
<script type="text/javascript" src="/js/dashboard/users.js"></script>
</div> </div>
<!-- End row --> <!-- End col -->
</div> </div>
<!-- End container --> <!-- End row -->

View File

@ -26,7 +26,7 @@ function responseAlert(response) {
function onSubmit() { function onSubmit() {
loadingResponse(); loadingResponse();
var email = document.getElementById("form-email" ).value; var email = document.getElementById("form-email" ).value;
var url = "/api/auth/password/forgot"; var url = "/api/auth/password/forgot/send";
var payload = { var payload = {
"email" : email "email" : email
}; };

View File

@ -32,7 +32,7 @@ function onSubmit() {
const urlParams = new URLSearchParams(queryString); const urlParams = new URLSearchParams(queryString);
const token = urlParams.get('token') const token = urlParams.get('token')
var password = document.getElementById("form-password").value; var password = document.getElementById("form-password").value;
var url = "/api/auth/password/reset"; var url = "/api/auth/password/forgot/change";
var payload = { var payload = {
"token" : token, "token" : token,
"password" : password "password" : password