added login cookie, added change pwd screen

This commit is contained in:
2026-01-18 08:22:41 +01:00
parent 2d7297e3b7
commit 23631c6d24
10 changed files with 480 additions and 22 deletions
+99
View File
@@ -0,0 +1,99 @@
from flask import Blueprint, render_template, redirect, url_for, request
from flask_login import login_required, current_user
from models import db
from models import User, Item
import json
from sqlalchemy import desc, text
api_bp = Blueprint("api", __name__, url_prefix="/api")
@api_bp.route("/validate_key", methods=["POST"])
def validate_key():
result={}
result['status']=0
result['data'] = ''
if "key" in request.form:
key = request.form["key"]
print(key)
if key == 'NogNietNodigHier':
result['status'] = 1
else:
result['message'] = 'Unknown Application-Key'
return json.dumps(result), 200
@api_bp.route("/login", methods=["POST"])
def login():
result={}
result['status']=0
result['data'] = ''
if "user" in request.form and "password" in request.form:
user = request.form["user"]
password = request.form["password"]
print(user)
user = User.query.filter_by(name=user).first()
if user and user.check_password(password):
if not user.is_approved:
result['message'] = 'User is pending approval.'
else:
result['status'] = 1
result['data'] = user.as_dict()
else:
result['message'] = 'User or password unknown'
return json.dumps(result), 200
@api_bp.route("/register", methods=["POST"])
def register():
result={}
result['status']=0
result['data'] = ''
if "user" in request.form and "password" in request.form:
username = request.form["user"]
password = request.form["password"]
print(username)
user = User.query.filter_by(name=username).first()
if user:
result['message'] = 'Username is not unique. Pick something else please.'
else:
result['status'] = 2
result['message'] = 'Registration Ok. Awaiting approval.'
user = User(name=username)
user.set_password(password)
db.session.add(user)
db.session.commit()
return json.dumps(result), 200
@api_bp.route("/load_lists", methods=["POST"])
def load_lists():
print("Trying to load lists")
result={}
result['status']=1 # from this call the result is always 1, empty or not
result['data'] = ''
if "userid" in request.form:
userid = int(request.form["userid"])
print(userid)
user = User.query.filter_by(id=userid).first()
if user:
result['data'] = user.all_my_lists_as_dict()
return json.dumps(result), 200
@api_bp.route("/load_items", methods=["POST"])
def load_items():
print("Trying to load items")
result={}
result['status']=1 # from this call the result is always 1, empty or not
result['data'] = ''
if "listid" in request.form:
listid = int(request.form["listid"])
print(listid)
items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all()
print(items)
if items:
result['data'] = [ i.as_dict() for i in items]
print(result)
return json.dumps(result), 200
+98 -3
View File
@@ -1,14 +1,82 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response
from flask_login import login_user, logout_user, login_required, current_user from flask_login import login_user, logout_user, login_required, current_user
from models import db from models import db
from models import User from models import User
from log import Log from log import Log
import pickle
from cryptography.fernet import Fernet
import datetime
auth_bp = Blueprint("auth", __name__) auth_bp = Blueprint("auth", __name__)
# =====================================================
# SECRET KEY (only your program has this)
# =====================================================
# Generate once using: Fernet.generate_key()
_SECRET_KEY = b'YFK7QCyTzhyLO4vqrnRxvDAI5uu8mXEYrInEjbRoQgs='
fernet = Fernet(_SECRET_KEY)
# =====================================================
# ENCRYPT
# =====================================================
def encrypt_object(obj) -> bytes:
"""
Encrypt any Python object and return encrypted bytes.
"""
serialized = pickle.dumps(obj)
encrypted = fernet.encrypt(serialized)
return encrypted
# =====================================================
# DECRYPT
# =====================================================
def decrypt_object(encrypted_data: bytes):
"""
Decrypt bytes back into the original Python object.
"""
decrypted = fernet.decrypt(encrypted_data)
obj = pickle.loads(decrypted)
return obj
def create_cookie(userid):
cookie_data = {
"user": userid,
"datetime": datetime.datetime.now()
}
return encrypt_object(cookie_data).decode('utf-8')
def is_cookie_ok_to_autologin(cookie_content):
user = 0
autologin = False
try:
cookie_data = decrypt_object(cookie_content)
user = cookie_data["user"]
dt = cookie_data["datetime"]
lastweek = datetime.datetime.now() - datetime.timedelta(days=7)
autologin = dt > lastweek
except:
Log.info("Cookie error")
if autologin:
return user
return 0
#. @auth_bp.route("/", methods=["GET", "POST"]) #. @auth_bp.route("/", methods=["GET", "POST"])
@auth_bp.route("/login", methods=["GET", "POST"]) @auth_bp.route("/login", methods=["GET", "POST"])
def login(): def login():
username = request.cookies.get('username')
userid = is_cookie_ok_to_autologin(username)
if userid>0:
user = User.query.filter_by(id=userid).first()
if user and user.is_approved:
login_user(user)
return redirect(url_for("lists.home"))
if request.method == "POST": if request.method == "POST":
user = User.query.filter_by(name=request.form["name"]).first() user = User.query.filter_by(name=request.form["name"]).first()
if user and user.check_password(request.form["password"]): if user and user.check_password(request.form["password"]):
@@ -18,7 +86,12 @@ def login():
return redirect(url_for("lists.home")) return redirect(url_for("lists.home"))
flash("Invalid credentials") flash("Invalid credentials")
Log.info(f"Authorization failure for user '{request.form["name"]}' ") Log.info(f"Authorization failure for user '{request.form["name"]}' ")
return render_template("login.html")
resp = make_response(render_template("login.html"))
resp.set_cookie('username', '')
return resp
@auth_bp.route("/register", methods=["GET", "POST"]) @auth_bp.route("/register", methods=["GET", "POST"])
def register(): def register():
@@ -34,5 +107,27 @@ def register():
@login_required @login_required
def logout(): def logout():
logout_user() logout_user()
return redirect(url_for("auth.login")) resp = make_response(redirect(url_for("auth.login")))
resp.set_cookie('username', '')
return resp
@auth_bp.route("/change_pwd", methods=["GET", "POST"])
def change_pwd():
if request.method == "POST":
pwd_old = request.form["password_old"]
pwd_new1 = request.form["password_new1"]
pwd_new2 = request.form["password_new2"]
print(f" >>{pwd_old} {pwd_new1} {pwd_new2}")
print(f"cp: {current_user.name}")
if current_user.check_password(pwd_old):
if pwd_new1 == pwd_new2:
current_user.set_password(pwd_new2)
db.session.commit()
return redirect(url_for("lists.home"))
flash("New Passwords are not identical")
flash("Invalid credentials")
Log.info(f"Authorization failure for user '{request.form["name"]}' ")
return render_template(url_for("lists.home"))
return render_template("change_pwd.html")
+20 -9
View File
@@ -1,10 +1,11 @@
from flask import Blueprint, render_template, redirect, url_for, request from flask import Blueprint, render_template, redirect, url_for, request, make_response
from flask_login import login_required, current_user from flask_login import login_required, current_user
from models import db from models import db
from models import Item from models import Item
from sqlalchemy import desc, text from sqlalchemy import desc, text
import os import os
from log import Log from log import Log
from auth import create_cookie
items_bp = Blueprint("items", __name__) items_bp = Blueprint("items", __name__)
@@ -14,12 +15,14 @@ ICONPATH = "static/categories/" #without starting / and with ending /
@items_bp.route("/items/<listid>") @items_bp.route("/items/<listid>")
@login_required @login_required
def items(listid): def items(listid):
return render_template("items_show.html", user=current_user, resp = make_response(render_template("items_show.html", user=current_user,
items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all(), items=Item.query.filter_by(listofitems_id=listid, is_suggestion=False).order_by(text("is_checked, category, label")).all(),
button_top_url1 = url_for("items.items_append", listid=listid), button_top_url1 = url_for("items.items_append", listid=listid),
button_top_txt1 = "add", button_top_txt1 = "add",
button_top_url2 = url_for("items.items_clean", listid=listid), button_top_url2 = url_for("items.items_clean", listid=listid),
button_top_txt2 = "clean") button_top_txt2 = "clean"))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
# webservice for updating checked state of one item # webservice for updating checked state of one item
@items_bp.route("/item_update/<itemid>/<itemchecked>") @items_bp.route("/item_update/<itemid>/<itemchecked>")
@@ -124,12 +127,14 @@ def items_append(listid):
if not i.startswith('.'): if not i.startswith('.'):
icon_names.append(os.path.splitext(i)[0]) icon_names.append(os.path.splitext(i)[0])
return render_template("items_append.html", user=current_user, resp = make_response(render_template("items_append.html", user=current_user,
listid=listid, listid=listid,
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(), items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
icons = icon_names, icons = icon_names,
iconpath = '/'+ICONPATH, iconpath = '/'+ICONPATH,
logo_url=url_for("items.items", listid=listid)) logo_url=url_for("items.items", listid=listid)))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
# user adds items to list # user adds items to list
@items_bp.route("/items_multiappend/<listid>", methods=["GET", "POST"]) @items_bp.route("/items_multiappend/<listid>", methods=["GET", "POST"])
@@ -188,9 +193,12 @@ def item_addone(itemid):
if not i.unit: if not i.unit:
i.unit = 'x' i.unit = 'x'
db.session.commit() db.session.commit()
return render_template("items_append.html", user=current_user,
resp = make_response(render_template("items_append.html", user=current_user,
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(), items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
logo_url=url_for("items.items", listid=listid)) logo_url=url_for("items.items", listid=listid)))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
# webservice for adding one item with a quantity/unit # webservice for adding one item with a quantity/unit
@items_bp.route("/item_addquantity/<itemid>/<quantity>") @items_bp.route("/item_addquantity/<itemid>/<quantity>")
@@ -206,9 +214,12 @@ def item_addquantity(itemid, quantity):
else: else:
i.quantity += int(quantity) i.quantity += int(quantity)
db.session.commit() db.session.commit()
return render_template("items_append.html", user=current_user,
resp = make_response(render_template("items_append.html", user=current_user,
items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(), items=Item.query.filter_by(listofitems_id=listid).order_by(Item.label).all(),
logo_url=url_for("items.items", listid=listid)) logo_url=url_for("items.items", listid=listid)))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
# webservice for updating category of one item # webservice for updating category of one item
@items_bp.route("/item_update_category/<itemid>/<category>") @items_bp.route("/item_update_category/<itemid>/<category>")
+3 -1
View File
@@ -6,6 +6,7 @@ from auth import auth_bp
from admin import admin_bp from admin import admin_bp
from lists import lists_bp from lists import lists_bp
from items import items_bp from items import items_bp
from api import api_bp
login_manager = LoginManager() login_manager = LoginManager()
@@ -22,6 +23,7 @@ def create_app():
app.register_blueprint(admin_bp) app.register_blueprint(admin_bp)
app.register_blueprint(lists_bp) app.register_blueprint(lists_bp)
app.register_blueprint(items_bp) app.register_blueprint(items_bp)
app.register_blueprint(api_bp)
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
@@ -36,4 +38,4 @@ def create_app():
if __name__ == "__main__": if __name__ == "__main__":
my_app = create_app() my_app = create_app()
# if needed - initialize data # if needed - initialize data
my_app.run(debug=True, host='0.0.0.0', port=5001) my_app.run(debug=True, host='127.0.0.1', port=5001)
+11 -4
View File
@@ -1,8 +1,9 @@
from flask import Blueprint, render_template, redirect, url_for, request from flask import Blueprint, render_template, redirect, url_for, request, make_response
from flask_login import login_required, current_user from flask_login import login_required, current_user
from models import db from models import db
from models import User, ListOfItems, Item, Shared from models import User, ListOfItems, Item, Shared
from log import Log from log import Log
from auth import create_cookie
lists_bp = Blueprint("lists", __name__) lists_bp = Blueprint("lists", __name__)
@@ -17,7 +18,11 @@ def not_current_user(current_user_id):
@lists_bp.route("/") @lists_bp.route("/")
@login_required @login_required
def home(): def home():
return render_template("lists_show.html", user=current_user, ilists=current_user.all_my_lists()) resp = make_response(render_template("lists_show.html", user=current_user, ilists=current_user.all_my_lists(),
button_top_url1 = url_for("auth.change_pwd"),
button_top_txt1 = "add",))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
@lists_bp.route("/lists_edit", methods=["GET", "POST"]) @lists_bp.route("/lists_edit", methods=["GET", "POST"])
@login_required @login_required
@@ -77,7 +82,9 @@ def edit():
return home() return home()
return render_template("lists_edit.html", user=current_user, resp = make_response(render_template("lists_edit.html", user=current_user,
ilists=current_user.my_owned_lists(with_inactive=True), ilists=current_user.my_owned_lists(with_inactive=True),
users=not_current_user(current_user.id), users=not_current_user(current_user.id),
newlist=newlist) newlist=newlist))
resp.set_cookie('username', create_cookie(current_user.id))
return resp
+29
View File
@@ -15,7 +15,14 @@ class User(UserMixin, db.Model):
is_private = db.Column(db.Boolean, default=False) is_private = db.Column(db.Boolean, default=False)
is_guest = db.Column(db.Boolean, default=False) is_guest = db.Column(db.Boolean, default=False)
def as_dict(self):
result = {}
for p in ['id','name','is_admin','is_approved','is_private','is_guest']:
result[p] = getattr(self, p)
return result
def set_password(self, password): def set_password(self, password):
print(f"u:{self.name} > {password}")
self.password_hash = generate_password_hash(password) self.password_hash = generate_password_hash(password)
def check_password(self, password): def check_password(self, password):
@@ -34,11 +41,18 @@ class User(UserMixin, db.Model):
result.append(l) result.append(l)
return result return result
def all_my_lists_as_dict(self):
result = []
for l in self.all_my_lists():
result.append(l.as_dict())
return result
def shares_in_list(self, listofitem_id): def shares_in_list(self, listofitem_id):
shared = Shared.query.filter_by(user_id=self.id, listofitem_id=listofitem_id).first() shared = Shared.query.filter_by(user_id=self.id, listofitem_id=listofitem_id).first()
return True if shared else False return True if shared else False
class ListOfItems(db.Model): class ListOfItems(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
updated_at = db.Column(db.TIMESTAMP, default=datetime.now(), onupdate=datetime.now(), nullable=False) updated_at = db.Column(db.TIMESTAMP, default=datetime.now(), onupdate=datetime.now(), nullable=False)
@@ -46,6 +60,15 @@ class ListOfItems(db.Model):
name = db.Column(db.String(40), unique=True, nullable=False) name = db.Column(db.String(40), unique=True, nullable=False)
is_active = db.Column(db.Boolean, default=False) is_active = db.Column(db.Boolean, default=False)
def as_dict(self):
result = {}
for p in ['id','name','owner_user_id','is_active']:
result[p] = getattr(self, p)
result["pending"] = self.items_left()
result["total"] = self.items_total()
print(result)
return result
def items_left(self): def items_left(self):
return len(Item.query.filter_by(listofitems_id=self.id, is_checked=False, is_suggestion=False).all()) return len(Item.query.filter_by(listofitems_id=self.id, is_checked=False, is_suggestion=False).all())
@@ -65,6 +88,12 @@ class Item(db.Model):
is_suggestion = db.Column(db.Boolean, default=False) is_suggestion = db.Column(db.Boolean, default=False)
category = db.Column(db.String(20)) category = db.Column(db.String(20))
def as_dict(self):
result = {}
for p in ['id','listofitems_id','label','quantity','unit','is_checked', 'is_suggestion', 'category']:
result[p] = getattr(self, p)
return result
class Shared(db.Model): class Shared(db.Model):
user_id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, primary_key=True)
listofitem_id = db.Column(db.Integer, primary_key=True) listofitem_id = db.Column(db.Integer, primary_key=True)
+121
View File
@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16.482828mm"
height="14.68959mm"
viewBox="0 0 16.482828 14.68959"
version="1.1"
id="svg8634"
inkscape:version="0.92.1 r15371"
sodipodi:docname="password reset.svg">
<defs
id="defs8628" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6568543"
inkscape:cx="-3.8835724"
inkscape:cy="1.3449005"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata8631">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-3.0978698,-4.6612284)">
<g
transform="matrix(1.2809155,0,0,1.2374188,103.99112,-1219.3652)"
id="g4667">
<path
inkscape:connector-curvature="0"
id="path4640"
d="m -72.695687,1000.9938 c -1.972631,-0.2257 -3.790181,-1.49518 -4.687846,-3.27432 -0.175341,-0.34752 -0.468403,-1.12381 -0.468403,-1.24073 0,-0.0266 0.375731,-0.048 0.834956,-0.0476 l 0.834965,7.2e-4 0.163557,0.4041 c 0.519646,1.28389 1.615359,2.24396 2.994703,2.62398 0.49589,0.13662 1.63139,0.13421 2.144474,-0.005 0.522244,-0.14124 1.250991,-0.50448 1.653619,-0.82424 0.687488,-0.546 1.31115,-1.49718 1.539,-2.34722 0.146128,-0.54518 0.177544,-1.43846 0.07041,-2.00269 -0.178809,-0.94189 -0.54809,-1.63735 -1.241568,-2.33827 -0.844669,-0.85372 -1.728666,-1.24944 -2.900391,-1.29837 -0.550547,-0.023 -0.767325,-0.006 -1.18485,0.0941 -1.415428,0.33867 -2.632012,1.39775 -3.122259,2.71804 l -0.103341,0.27832 h -0.842765 -0.842766 l 0.03834,-0.16113 c 0.08466,-0.35604 0.444253,-1.16597 0.699534,-1.57555 0.354919,-0.56944 1.084444,-1.33426 1.637166,-1.71635 3.344531,-2.31207 7.975171,-0.83571 9.268415,2.95499 1.2168,3.56666 -1.256634,7.33132 -5.098284,7.75962 -0.62535,0.07 -0.754641,0.069 -1.386656,0 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.05859375;stroke-opacity:1" />
<g
id="g4592-5-0"
transform="matrix(0.77578717,0,0,0.61178065,-35.477186,373.83106)">
<rect
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.04824853"
id="rect3717-9-8-2"
width="4.4458065"
height="1.1666708"
x="-55.705212"
y="1017.3687" />
<path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.93749994"
id="path3783-0-6"
sodipodi:sides="3"
sodipodi:cx="-109.71203"
sodipodi:cy="1025.7628"
sodipodi:r1="8.7855453"
sodipodi:r2="6.0797644"
sodipodi:arg1="0.43833656"
sodipodi:arg2="1.4855341"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -101.75708,1029.4917 -15.16173,1.2959 6.45861,-13.7784 z"
inkscape:transform-center-x="-0.034756175"
inkscape:transform-center-y="0.58213244"
transform="matrix(-0.30321012,-0.03553367,0.03028113,-0.35580483,-117.82847,1375.3296)" />
</g>
</g>
<g
id="g73"
transform="matrix(0.83530458,0,0,1,39.090178,0.46999031)">
<path
inkscape:connector-curvature="0"
id="path68"
d="m -34.487464,10.849979 0.0036,-0.730218 0.02775,-0.1337396 c 0.196159,-0.9452109 0.892942,-1.6292248 1.80383,-1.7707713 0.149269,-0.023195 0.448719,-0.023232 0.597434,-8.14e-5 0.850246,0.1323855 1.532973,0.7494915 1.763181,1.5937089 0.07187,0.2635504 0.07468,0.3040374 0.07468,1.0731994 l 3e-6,0.687422 h -0.324033 -0.324031 v -0.678215 c 0,-0.574261 -0.0024,-0.691927 -0.01605,-0.767667 -0.115574,-0.6441436 -0.601574,-1.1513119 -1.21426,-1.2671477 -0.133279,-0.025199 -0.38006,-0.025995 -0.5063,-0.00165 -0.211637,0.040837 -0.407858,0.1240287 -0.588513,0.2495169 -0.118197,0.082103 -0.323993,0.2988632 -0.400284,0.4216091 -0.103814,0.1670294 -0.170352,0.3247404 -0.218926,0.5189097 l -0.02677,0.106992 -0.0035,0.714169 -0.0035,0.714171 h -0.324083 -0.324083 z"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.01040864" />
<rect
ry="0.44648439"
y="10.52671"
x="-34.942802"
height="3.3545389"
width="5.1773005"
id="rect12"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.25095639" />
<path
inkscape:connector-curvature="0"
id="path39"
d="m -32.915205,12.470548 c -6.38e-4,-0.291161 -3.71e-4,-0.547149 6.25e-4,-0.568863 0.0065,-0.140261 0.0869,-0.294293 0.198787,-0.380573 0.09138,-0.07046 0.187004,-0.106399 0.298919,-0.112341 0.248028,-0.01317 0.470974,0.149576 0.540122,0.394269 0.01125,0.03982 0.01296,0.108928 0.0153,0.620851 l 0.0026,0.576041 h -0.527593 -0.527593 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.00735304" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

+23 -5
View File
@@ -26,6 +26,15 @@
height: 56px; height: 56px;
} }
</style> </style>
<script>
function toggleButtons() {
if ($('#toprightbuttons').hasClass("d-none")){
$('#toprightbuttons').removeClass("d-none")
} else {
$('#toprightbuttons').addClass("d-none")
}
}
</script>
</head> </head>
<body class="bg-light"> <body class="bg-light">
@@ -45,13 +54,22 @@
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
<span class="text-muted small d-none d-sm-inline">
{{ current_user.name }}
</span>
<a href="{{ url_for('auth.logout') }}" <div class="text-muted small d-none d-sm-inline">
> <a class="btn" onclick="toggleButtons()">{{current_user.name }}</a>
</div>
<div id="toprightbuttons" class="d-none">
<a href="{{ url_for('auth.logout') }}">
<img style="width: 2em; height: 2em" src="/static/logout.svg"> <img style="width: 2em; height: 2em" src="/static/logout.svg">
</a> </a>
<a href="{{ url_for('auth.change_pwd') }}">
<img style="width: 2em; height: 2em" src="/static/password.svg">
</a>
</div>
{% else %} {% else %}
<a href="{{ url_for('auth.login') }}" <a href="{{ url_for('auth.login') }}"
> >
+17
View File
@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
<h3 class="text-center mb-4"><img style="width: 3em; height: 3em" src="/static/login.svg"></h3>
<form method="post">
<input class="form-control mb-3" name="password_old" type="password" placeholder="Old Password" required>
<input class="form-control mb-3" name="password_new1" type="password" placeholder="New Password" required>
<input class="form-control mb-3" name="password_new2" type="password" placeholder="Repeat" required>
<input
type="image"
src="/static/save.svg"
alt="Save"
style="width: 2em; height: 2em;"
>
</form>
{% endblock %}
+59
View File
@@ -0,0 +1,59 @@
import pickle
from cryptography.fernet import Fernet
# =====================================================
# SECRET KEY (only your program has this)
# =====================================================
# Generate once using: Fernet.generate_key()
_SECRET_KEY = b'YFK7QCyTzhyLO4vqrnRxvDAI5uu8mXEYrInEjbRoQgs='
fernet = Fernet(_SECRET_KEY)
# =====================================================
# ENCRYPT
# =====================================================
def encrypt_object(obj) -> bytes:
"""
Encrypt any Python object and return encrypted bytes.
"""
serialized = pickle.dumps(obj)
encrypted = fernet.encrypt(serialized)
return encrypted
# =====================================================
# DECRYPT
# =====================================================
def decrypt_object(encrypted_data: bytes):
"""
Decrypt bytes back into the original Python object.
"""
decrypted = fernet.decrypt(encrypted_data)
obj = pickle.loads(decrypted)
return obj
# =====================================================
# EXAMPLE USAGE
# =====================================================
if __name__ == "__main__":
original_object = {
"user": "alice",
"permissions": ["read", "write"],
"balance": 123.45
}
encrypted = encrypt_object(original_object)
print("Encrypted:", str(encrypted))
decrypted = decrypt_object(encrypted)
print("Decrypted:", decrypted)
bytes_data = b'I am a bytes string'
string_data = bytes_data.decode('utf-8')
print("Type Before :- ",type(bytes_data))
print(string_data)
print("Type After :- ",type(string_data))