128 lines
4.6 KiB
Python
128 lines
4.6 KiB
Python
import streamlit as st
|
|
import bcrypt
|
|
import json
|
|
import os
|
|
import re
|
|
|
|
CONFIG_PATH = "config.json"
|
|
|
|
# Muat file konfigurasi
|
|
def load_config():
|
|
if not os.path.exists(CONFIG_PATH):
|
|
return {"username": "admin", "password": "", "security_question": "", "security_answer": ""}
|
|
with open(CONFIG_PATH, "r") as f:
|
|
return json.load(f)
|
|
|
|
# Simpan konfigurasi
|
|
def save_config(config):
|
|
with open(CONFIG_PATH, "w") as f:
|
|
json.dump(config, f, indent=4)
|
|
|
|
# Validasi kekuatan password
|
|
def is_strong_password(password):
|
|
if len(password) < 8:
|
|
return False
|
|
if not re.search(r"[a-z]", password):
|
|
return False
|
|
if not re.search(r"[A-Z]", password):
|
|
return False
|
|
if not re.search(r"\d", password):
|
|
return False
|
|
if not re.search(r"[^\w\s]", password):
|
|
return False
|
|
return True
|
|
|
|
# Login form
|
|
def login():
|
|
config = load_config()
|
|
|
|
if 'authenticated' not in st.session_state:
|
|
st.session_state.authenticated = False
|
|
if 'show_reset' not in st.session_state:
|
|
st.session_state.show_reset = False
|
|
if 'login_failed' not in st.session_state:
|
|
st.session_state.login_failed = False
|
|
|
|
if not st.session_state.authenticated:
|
|
st.subheader("🔐 Login Admin")
|
|
|
|
username = st.text_input("Username")
|
|
password = st.text_input("Password", type="password")
|
|
|
|
if st.button("Login"):
|
|
if username == config.get("username") and bcrypt.checkpw(password.encode(), config.get("password").encode()):
|
|
st.session_state.authenticated = True
|
|
st.session_state.login_failed = False
|
|
st.rerun()
|
|
else:
|
|
st.session_state.login_failed = True
|
|
st.error("❌ Username atau password salah.")
|
|
|
|
# Hanya muncul jika login gagal
|
|
if st.session_state.login_failed and not st.session_state.show_reset:
|
|
if st.button("Lupa Password"):
|
|
st.session_state.show_reset = True
|
|
|
|
if st.session_state.show_reset:
|
|
st.info("Jawab pertanyaan rahasia untuk reset password.")
|
|
question = config.get("security_question")
|
|
answer = st.text_input("Pertanyaan:", placeholder=question)
|
|
new_pass = st.text_input("Password Baru", type="password")
|
|
if st.button("Ganti Password"):
|
|
if answer.lower() == config.get("security_answer").lower():
|
|
new_hash = bcrypt.hashpw(new_pass.encode(), bcrypt.gensalt()).decode()
|
|
config["password"] = new_hash
|
|
with open("config.json", "w") as f:
|
|
json.dump(config, f)
|
|
st.success("✅ Password berhasil diganti!")
|
|
st.session_state.show_reset = False
|
|
st.session_state.login_failed = False
|
|
else:
|
|
st.error("❌ Jawaban salah.")
|
|
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
# Form reset password via pertanyaan rahasia
|
|
def show_password_reset_form():
|
|
config = load_config()
|
|
st.subheader("🔐 Reset Password")
|
|
|
|
pertanyaan = config.get("security_question", "Pertanyaan tidak tersedia")
|
|
jawaban_input = st.text_input(pertanyaan)
|
|
new_pass = st.text_input("Password Baru", type="password")
|
|
|
|
if st.button("Ganti Password"):
|
|
if jawaban_input.strip().lower() == config.get("security_answer", "").strip().lower():
|
|
if not is_strong_password(new_pass):
|
|
st.warning("Password harus minimal 8 karakter, mengandung huruf besar, huruf kecil, angka, dan simbol.")
|
|
return
|
|
config["password"] = bcrypt.hashpw(new_pass.encode(), bcrypt.gensalt()).decode()
|
|
save_config(config)
|
|
st.success("Password berhasil diubah! Silakan login kembali.")
|
|
# Reset semua flag
|
|
st.session_state.show_reset = False
|
|
st.session_state.show_reset_button = False
|
|
else:
|
|
st.error("Jawaban pertanyaan salah.")
|
|
|
|
# Logout
|
|
def logout():
|
|
with st.sidebar:
|
|
if st.button("🚪 Logout"):
|
|
st.session_state.confirm_logout = True
|
|
|
|
# Tampilkan dialog konfirmasi jika diklik
|
|
if st.session_state.get("confirm_logout", False):
|
|
with st.sidebar:
|
|
st.warning("Apakah Anda yakin ingin logout?")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
if st.button("✅ Ya, Logout"):
|
|
st.session_state.clear()
|
|
st.rerun()
|
|
with col2:
|
|
if st.button("❌ Batal"):
|
|
st.session_state.confirm_logout = False |