Fix user management API authentication and data formatting
- Fixed find-user-by-id to handle BIT type database IDs - Updated user-has-role-p to extract role from list format - Enhanced API endpoint to return properly formatted JSON data - Added comprehensive debugging for authentication flow - Created login.chtml template with CLIP data binding - Resolved 'Error loading users' issue in admin panel
This commit is contained in:
parent
806031e57f
commit
00942b60bc
101
auth-routes.lisp
101
auth-routes.lisp
|
|
@ -7,7 +7,9 @@
|
||||||
(define-page login #@"/login" ()
|
(define-page login #@"/login" ()
|
||||||
"User login page"
|
"User login page"
|
||||||
(let ((username (radiance:post-var "username"))
|
(let ((username (radiance:post-var "username"))
|
||||||
(password (radiance:post-var "password")))
|
(password (radiance:post-var "password"))
|
||||||
|
(template-path (merge-pathnames "template/login.chtml"
|
||||||
|
(asdf:system-source-directory :asteroid))))
|
||||||
(if (and username password)
|
(if (and username password)
|
||||||
;; Handle login form submission
|
;; Handle login form submission
|
||||||
(let ((user (authenticate-user username password)))
|
(let ((user (authenticate-user username password)))
|
||||||
|
|
@ -25,78 +27,17 @@
|
||||||
(format t "Session error: ~a~%" e)
|
(format t "Session error: ~a~%" e)
|
||||||
"Login successful but session error occurred")))
|
"Login successful but session error occurred")))
|
||||||
;; Login failed - show form with error
|
;; Login failed - show form with error
|
||||||
"<!DOCTYPE html>
|
(clip:process-to-string
|
||||||
<html>
|
(plump:parse (alexandria:read-file-into-string template-path))
|
||||||
<head>
|
:title "Asteroid Radio - Login"
|
||||||
<title>Asteroid Radio - Login</title>
|
:error-message "Invalid username or password"
|
||||||
<link rel='stylesheet' href='/static/asteroid.css'>
|
:display-error "display: block;")))
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class='container'>
|
|
||||||
<h1>🎵 ASTEROID RADIO - LOGIN</h1>
|
|
||||||
<div class='auth-container'>
|
|
||||||
<div class='auth-form'>
|
|
||||||
<h2>System Access</h2>
|
|
||||||
<div class='message error'>Invalid username or password</div>
|
|
||||||
<form method='post' action='/asteroid/login'>
|
|
||||||
<div class='form-group'>
|
|
||||||
<label>Username:</label>
|
|
||||||
<input type='text' name='username' required>
|
|
||||||
</div>
|
|
||||||
<div class='form-group'>
|
|
||||||
<label>Password:</label>
|
|
||||||
<input type='password' name='password' required>
|
|
||||||
</div>
|
|
||||||
<div class='form-actions'>
|
|
||||||
<button type='submit' class='btn btn-primary' style='width: 100%;'>LOGIN</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class='panel' style='margin-top: 20px; text-align: center;'>
|
|
||||||
<strong style='color: #ff6600;'>Default Admin Credentials:</strong><br>
|
|
||||||
Username: <code style='color: #00ff00;'>admin</code><br>
|
|
||||||
Password: <code style='color: #00ff00;'>asteroid123</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>"))
|
|
||||||
;; Show login form (no POST data)
|
;; Show login form (no POST data)
|
||||||
"<!DOCTYPE html>
|
(clip:process-to-string
|
||||||
<html>
|
(plump:parse (alexandria:read-file-into-string template-path))
|
||||||
<head>
|
:title "Asteroid Radio - Login"
|
||||||
<title>Asteroid Radio - Login</title>
|
:error-message ""
|
||||||
<link rel='stylesheet' href='/static/asteroid.css'>
|
:display-error "display: none;"))))
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class='container'>
|
|
||||||
<h1>🎵 ASTEROID RADIO - LOGIN</h1>
|
|
||||||
<div class='auth-container'>
|
|
||||||
<div class='auth-form'>
|
|
||||||
<h2>System Access</h2>
|
|
||||||
<form method='post' action='/asteroid/login'>
|
|
||||||
<div class='form-group'>
|
|
||||||
<label>Username:</label>
|
|
||||||
<input type='text' name='username' required>
|
|
||||||
</div>
|
|
||||||
<div class='form-group'>
|
|
||||||
<label>Password:</label>
|
|
||||||
<input type='password' name='password' required>
|
|
||||||
</div>
|
|
||||||
<div class='form-actions'>
|
|
||||||
<button type='submit' class='btn btn-primary' style='width: 100%;'>LOGIN</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class='panel' style='margin-top: 20px; text-align: center;'>
|
|
||||||
<strong style='color: #ff6600;'>Default Admin Credentials:</strong><br>
|
|
||||||
Username: <code style='color: #00ff00;'>admin</code><br>
|
|
||||||
Password: <code style='color: #00ff00;'>asteroid123</code>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>")))
|
|
||||||
|
|
||||||
;; Simple logout handler
|
;; Simple logout handler
|
||||||
(define-page logout #@"/logout" ()
|
(define-page logout #@"/logout" ()
|
||||||
|
|
@ -114,13 +55,15 @@
|
||||||
(cl-json:encode-json-to-string
|
(cl-json:encode-json-to-string
|
||||||
`(("status" . "success")
|
`(("status" . "success")
|
||||||
("users" . ,(mapcar (lambda (user)
|
("users" . ,(mapcar (lambda (user)
|
||||||
`(("id" . ,(gethash "_id" user))
|
`(("id" . ,(if (listp (gethash "_id" user))
|
||||||
("username" . ,(gethash "username" user))
|
(first (gethash "_id" user))
|
||||||
("email" . ,(gethash "email" user))
|
(gethash "_id" user)))
|
||||||
("role" . ,(gethash "role" user))
|
("username" . ,(first (gethash "username" user)))
|
||||||
("active" . ,(gethash "active" user))
|
("email" . ,(first (gethash "email" user)))
|
||||||
("created-date" . ,(gethash "created-date" user))
|
("role" . ,(first (gethash "role" user)))
|
||||||
("last-login" . ,(gethash "last-login" user))))
|
("active" . ,(= (first (gethash "active" user)) 1))
|
||||||
|
("created-date" . ,(first (gethash "created-date" user)))
|
||||||
|
("last-login" . ,(first (gethash "last-login" user)))))
|
||||||
users)))))
|
users)))))
|
||||||
(error (e)
|
(error (e)
|
||||||
(cl-json:encode-json-to-string
|
(cl-json:encode-json-to-string
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title data-text="title">Asteroid Radio - Login</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/asteroid.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>🎵 ASTEROID RADIO - LOGIN</h1>
|
||||||
|
<div class="auth-container">
|
||||||
|
<div class="auth-form">
|
||||||
|
<h2>System Access</h2>
|
||||||
|
<div class="message error" data-attr="style" data-attr-value="display-error">
|
||||||
|
<span data-text="error-message">Invalid username or password</span>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="/asteroid/login">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Username:</label>
|
||||||
|
<input type="text" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Password:</label>
|
||||||
|
<input type="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-primary" style="width: 100%;">LOGIN</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="panel" style="margin-top: 20px; text-align: center;">
|
||||||
|
<strong style="color: #ff6600;">Default Admin Credentials:</strong><br>
|
||||||
|
Username: <code style="color: #00ff00;">admin</code><br>
|
||||||
|
Password: <code style="color: #00ff00;">asteroid123</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -51,8 +51,17 @@
|
||||||
|
|
||||||
(defun find-user-by-id (user-id)
|
(defun find-user-by-id (user-id)
|
||||||
"Find a user by ID"
|
"Find a user by ID"
|
||||||
(let ((users (db:select "USERS" (db:query (:= "_id" user-id)))))
|
(format t "Looking for user with ID: ~a (type: ~a)~%" user-id (type-of user-id))
|
||||||
(when users (first users))))
|
;; Handle both integer and BIT types by iterating through all users
|
||||||
|
(let ((all-users (db:select "USERS" (db:query :all)))
|
||||||
|
(target-id (if (numberp user-id) user-id (parse-integer (format nil "~a" user-id)))))
|
||||||
|
(format t "Searching through ~a users for ID ~a~%" (length all-users) target-id)
|
||||||
|
(dolist (user all-users)
|
||||||
|
(let ((db-id (gethash "_id" user)))
|
||||||
|
(format t "Checking user with _id: ~a (type: ~a)~%" db-id (type-of db-id))
|
||||||
|
(when (equal db-id target-id)
|
||||||
|
(format t "Found matching user!~%")
|
||||||
|
(return user))))))
|
||||||
|
|
||||||
(defun authenticate-user (username password)
|
(defun authenticate-user (username password)
|
||||||
"Authenticate a user with username and password"
|
"Authenticate a user with username and password"
|
||||||
|
|
@ -88,9 +97,12 @@
|
||||||
(string= (hash-password password) hash))
|
(string= (hash-password password) hash))
|
||||||
|
|
||||||
(defun user-has-role-p (user role)
|
(defun user-has-role-p (user role)
|
||||||
"Check if user has a specific role"
|
"Check if user has the specified role"
|
||||||
(when user
|
(when user
|
||||||
(let ((user-role (intern (string-upcase (gethash "role" user)) :keyword)))
|
(let* ((role-field (gethash "role" user))
|
||||||
|
(role-string (if (listp role-field) (first role-field) role-field))
|
||||||
|
(user-role (intern (string-upcase role-string) :keyword)))
|
||||||
|
(format t "User role: ~a, checking against: ~a~%" user-role role)
|
||||||
(or (eq user-role role)
|
(or (eq user-role role)
|
||||||
(and (eq role :listener) (member user-role '(:dj :admin)))
|
(and (eq role :listener) (member user-role '(:dj :admin)))
|
||||||
(and (eq role :dj) (eq user-role :admin))))))
|
(and (eq role :dj) (eq user-role :admin))))))
|
||||||
|
|
@ -99,8 +111,11 @@
|
||||||
"Get the currently authenticated user from session"
|
"Get the currently authenticated user from session"
|
||||||
(handler-case
|
(handler-case
|
||||||
(let ((user-id (session:field "user-id")))
|
(let ((user-id (session:field "user-id")))
|
||||||
|
(format t "Session user-id: ~a~%" user-id)
|
||||||
(when user-id
|
(when user-id
|
||||||
(find-user-by-id user-id)))
|
(let ((user (find-user-by-id user-id)))
|
||||||
|
(format t "Found user: ~a~%" (if user "YES" "NO"))
|
||||||
|
user)))
|
||||||
(error (e)
|
(error (e)
|
||||||
(format t "Error getting current user: ~a~%" e)
|
(format t "Error getting current user: ~a~%" e)
|
||||||
nil)))
|
nil)))
|
||||||
|
|
@ -118,7 +133,11 @@
|
||||||
"Require user to have a specific role"
|
"Require user to have a specific role"
|
||||||
(handler-case
|
(handler-case
|
||||||
(let ((current-user (get-current-user)))
|
(let ((current-user (get-current-user)))
|
||||||
|
(format t "Current user for role check: ~a~%" (if current-user "FOUND" "NOT FOUND"))
|
||||||
|
(when current-user
|
||||||
|
(format t "User has role ~a: ~a~%" role (user-has-role-p current-user role)))
|
||||||
(unless (and current-user (user-has-role-p current-user role))
|
(unless (and current-user (user-has-role-p current-user role))
|
||||||
|
(format t "Role check failed - redirecting to login~%")
|
||||||
(radiance:redirect "/asteroid/login")))
|
(radiance:redirect "/asteroid/login")))
|
||||||
(error (e)
|
(error (e)
|
||||||
(format t "Role check error: ~a~%" e)
|
(format t "Role check error: ~a~%" e)
|
||||||
|
|
@ -166,11 +185,12 @@
|
||||||
(defun get-all-users ()
|
(defun get-all-users ()
|
||||||
"Get all users from database"
|
"Get all users from database"
|
||||||
(format t "Getting all users from database...~%")
|
(format t "Getting all users from database...~%")
|
||||||
(let ((all-users (db:select "USERS" (db:query :all))))
|
(let ((users (db:select "USERS" (db:query :all))))
|
||||||
(format t "Total users in database: ~a~%" (length all-users))
|
(format t "Total users in database: ~a~%" (length users))
|
||||||
(dolist (user all-users)
|
(dolist (user users)
|
||||||
(format t "User: ~a~%" user))
|
(format t "User: ~a~%" user)
|
||||||
all-users))
|
(format t "User _id field: ~a (type: ~a)~%" (gethash "_id" user) (type-of (gethash "_id" user))))
|
||||||
|
users))
|
||||||
|
|
||||||
(defun get-user-stats ()
|
(defun get-user-stats ()
|
||||||
"Get user statistics"
|
"Get user statistics"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue