feat: add optional dev mode and analyzer tool upload role
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ EXPOSE 8000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/', timeout=5)" || exit 1
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8888/', timeout=5)" || exit 1
|
||||
|
||||
# Set entrypoint
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">🏠 Trang chủ</a>
|
||||
<a href="{{ url_for('my_files') }}" class="nav-tab"
|
||||
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">📁 File của tôi</a>
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
{% if user_info and user_info.role in ['admin', 'analyzer'] %}
|
||||
<a href="{{ url_for('admin_tools') }}" class="nav-tab"
|
||||
style="padding: 10px 20px; background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(25, 118, 210, 0.3);">⚙️ Quản trị</a>
|
||||
{% endif %}
|
||||
@@ -403,11 +403,13 @@
|
||||
<div style="display: inline-flex; gap: 6px; align-items: center;">
|
||||
<a href="{{ url_for('download_tool', version_key=tool.version_key) }}"
|
||||
class="btn btn-secondary" style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">⬇️ Tải</a>
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
<form action="tools/delete/{{ tool.version_key }}" method="post" style="display: inline; margin: 0;"
|
||||
onsubmit="return confirm('Bạn có chắc muốn xóa công cụ này?')">
|
||||
<button type="submit" class="btn btn-danger"
|
||||
style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">🗑️ Xóa</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<span style="color: var(--text-muted);">-</span>
|
||||
@@ -425,6 +427,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
<!-- Upload Documentation Form -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
@@ -516,6 +519,9 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
<!-- Email Notification Settings -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
@@ -583,6 +589,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
<!-- User Email Stats -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
@@ -623,15 +632,17 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Drag and drop support
|
||||
const uploadForm = document.getElementById('uploadForm');
|
||||
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
uploadForm.addEventListener(eventName, preventDefaults, false);
|
||||
});
|
||||
if (uploadForm) {
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
uploadForm.addEventListener(eventName, preventDefaults, false);
|
||||
});
|
||||
}
|
||||
|
||||
function preventDefaults(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -980,7 +980,7 @@
|
||||
style="padding: 10px 20px; background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(25, 118, 210, 0.3);">🏠 Trang chủ</a>
|
||||
<a href="{{ url_for('my_files') }}" class="nav-tab"
|
||||
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">📁 File của tôi</a>
|
||||
{% if user_info and user_info.role == 'admin' %}
|
||||
{% if user_info and user_info.role in ['admin', 'analyzer'] %}
|
||||
<a href="{{ url_for('admin_tools') }}" class="nav-tab"
|
||||
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">⚙️ Quản trị</a>
|
||||
{% endif %}
|
||||
|
||||
+47
-6
@@ -53,6 +53,22 @@ def _email_enabled() -> bool:
|
||||
app = FastAPI(title="Audit Hardening Tool", redirect_slashes=False)
|
||||
security = HTTPBasic()
|
||||
|
||||
# Development mode: allows unauthenticated file upload only.
|
||||
# Keep disabled by default; enable explicitly with AUDIT_DEV_MODE=true.
|
||||
DEV_MODE = os.getenv("AUDIT_DEV_MODE", "false").strip().lower() in {"1", "true", "yes", "on"}
|
||||
DEV_USER = {
|
||||
"username": "dev_guest",
|
||||
"full_name": "Development Guest",
|
||||
"role": "dev_guest",
|
||||
"unit_id": "",
|
||||
"unit_name": "Development",
|
||||
"auth_method": "dev_mode",
|
||||
"can_view_outputs": True,
|
||||
"can_download": True,
|
||||
}
|
||||
if DEV_MODE:
|
||||
print(Fore.YELLOW + "[DEV MODE] Unauthenticated file upload is ENABLED; admin routes remain protected" + Fore.RESET)
|
||||
|
||||
# Add middleware to handle X-Forwarded-* headers from reverse proxy (Caddy)
|
||||
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
||||
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
|
||||
@@ -1078,10 +1094,16 @@ async def logout(request: Request):
|
||||
|
||||
@app.get("/", response_class=HTMLResponse, name="index")
|
||||
async def index(request: Request):
|
||||
# Kiểm tra đăng nhập bắt buộc
|
||||
session_id = get_or_create_session(request)
|
||||
user_info = sessions.get(session_id, {}).get('user_info')
|
||||
|
||||
if not user_info and DEV_MODE:
|
||||
user_info = dict(DEV_USER)
|
||||
sessions[session_id]['username'] = DEV_USER['username']
|
||||
sessions[session_id]['user_info'] = user_info
|
||||
sessions[session_id]['unit_id'] = ''
|
||||
sessions[session_id]['unit_name'] = 'Development'
|
||||
|
||||
if not user_info:
|
||||
# Chưa đăng nhập → redirect về trang login
|
||||
response = RedirectResponse(url=str(request.url_for('login')), status_code=303)
|
||||
@@ -1639,9 +1661,15 @@ async def api_upload_async(
|
||||
os_type: str = Form(...),
|
||||
encrypted_files: List[UploadFile] = File(...)
|
||||
):
|
||||
# Bắt buộc đăng nhập để xác định unit_id người upload
|
||||
# Authenticated users and explicit dev mode may upload; admin routes stay protected.
|
||||
session_id = get_or_create_session(request)
|
||||
user_info = sessions.get(session_id, {}).get('user_info')
|
||||
if not user_info and DEV_MODE:
|
||||
user_info = dict(DEV_USER)
|
||||
sessions[session_id]['username'] = DEV_USER['username']
|
||||
sessions[session_id]['user_info'] = user_info
|
||||
sessions[session_id]['unit_id'] = ''
|
||||
sessions[session_id]['unit_name'] = 'Development'
|
||||
if not user_info:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -1764,6 +1792,11 @@ async def upload_files(
|
||||
|
||||
# Lấy hoặc tạo session
|
||||
session_id = get_or_create_session(request, response)
|
||||
if DEV_MODE and not sessions.get(session_id, {}).get('user_info'):
|
||||
sessions[session_id]['username'] = DEV_USER['username']
|
||||
sessions[session_id]['user_info'] = dict(DEV_USER)
|
||||
sessions[session_id]['unit_id'] = ''
|
||||
sessions[session_id]['unit_name'] = 'Development'
|
||||
|
||||
# Bắt đầu đo thời gian xử lý
|
||||
start_time = time.time()
|
||||
@@ -2530,16 +2563,24 @@ async def download_doc(doc_key: str):
|
||||
# ============== ADMIN ROUTES ==============
|
||||
|
||||
def require_admin_session(request: Request):
|
||||
"""Check session auth and return admin user or None"""
|
||||
"""Check session auth and return admin user or None."""
|
||||
user = get_session_user(request)
|
||||
if user and user.get('role') == 'admin':
|
||||
return user
|
||||
return None
|
||||
|
||||
|
||||
def require_tool_manager_session(request: Request):
|
||||
"""Allow admin or analyzer to access the tool upload page."""
|
||||
user = get_session_user(request)
|
||||
if user and user.get('role') in {'admin', 'analyzer'}:
|
||||
return user
|
||||
return None
|
||||
|
||||
@app.get("/admin/tools", response_class=HTMLResponse, name="admin_tools")
|
||||
async def admin_tools_page(request: Request, msg: str = None, msg_type: str = "success"):
|
||||
"""Admin page for managing tools - requires session authentication"""
|
||||
user = require_admin_session(request)
|
||||
user = require_tool_manager_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url=request.url_for('login'), status_code=303)
|
||||
|
||||
@@ -2789,8 +2830,8 @@ async def admin_upload_tool(
|
||||
tool_name: str = Form(...),
|
||||
tool_file: UploadFile = File(...)
|
||||
):
|
||||
"""Upload a new tool - requires admin session authentication"""
|
||||
user = require_admin_session(request)
|
||||
"""Upload a new tool - requires admin or analyzer session."""
|
||||
user = require_tool_manager_session(request)
|
||||
if not user:
|
||||
return RedirectResponse(url=request.url_for('login'), status_code=303)
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ services:
|
||||
environment:
|
||||
- TZ=Asia/Ho_Chi_Minh
|
||||
- PYTHONUNBUFFERED=1
|
||||
# Dev mode: unauthenticated upload; admin routes remain protected.
|
||||
# Set to false for production.
|
||||
- AUDIT_DEV_MODE=${AUDIT_DEV_MODE:-false}
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
Reference in New Issue
Block a user