From 6515a6fd2b2d29a8f363c906148f3abf7cf982e9 Mon Sep 17 00:00:00 2001 From: haturatu Date: Sun, 12 Oct 2025 22:57:23 +0900 Subject: fix(api): admin api --- cmd/server/main.go | 14 +++++++++++--- handlers/admin.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 89d17bc..196164e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -112,13 +112,21 @@ func main() { mux.HandleFunc("/api/auth/token", handlers.TokenHandler) // --- Protected API Endpoints (for Admin UI, protected by SessionAuth) --- - adminUsersAPI := http.HandlerFunc(handlers.GetUsersHandler) + adminCreateUserAPI := http.HandlerFunc(handlers.CreateUserHandler) + adminUsersAPI := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + adminCreateUserAPI.ServeHTTP(w, r) + } else { + getUsersHandler.ServeHTTP(w, r) + } + }) + adminUpdateRoleAPI := http.HandlerFunc(handlers.UpdateUserRoleHandler) adminDeleteUserAPI := http.HandlerFunc(handlers.DeleteUserHandler) adminSetStatusAPI := http.HandlerFunc(handlers.SetUserActiveStatusHandler) - mux.Handle(config.Paths.AdminUsersAPI, middleware.SessionAuth(middleware.AdminMiddleware(adminUsersAPI))) - mux.Handle(config.Paths.AdminUsersAPI+"/", middleware.SessionAuth(middleware.AdminMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mux.Handle(config.Paths.AdminUsersAPI, middleware.BearerAuth(middleware.AdminMiddleware(adminUsersAPI))) + mux.Handle(config.Paths.AdminUsersAPI+"/", middleware.BearerAuth(middleware.AdminMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path switch { case strings.HasSuffix(path, "/role") && r.Method == http.MethodPost: diff --git a/handlers/admin.go b/handlers/admin.go index 61519fa..8f302c0 100644 --- a/handlers/admin.go +++ b/handlers/admin.go @@ -205,3 +205,54 @@ func SetUserActiveStatusHandler(w http.ResponseWriter, r *http.Request) { logging.SecurityLog.Info("ADMIN ACTION", "action", "set_active_status", "performed_by", adminUsername, "target_user_id", id, "new_status", req.IsActive, "ip", ip) w.WriteHeader(http.StatusOK) } + +// CreateUserHandler handles creating a new user by an admin. +func CreateUserHandler(w http.ResponseWriter, r *http.Request) { + ip := logging.GetClientIP(r) + + var reqBody struct { + Username string `json:"username"` + Password string `json:"password"` + Role string `json:"role"` + } + + if err := json.NewDecoder(r.Body).Decode(&reqBody); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + // Default role to "user" if not provided or invalid + if reqBody.Role != "admin" && reqBody.Role != "user" { + reqBody.Role = "user" + } + + // Validate password policy + if ok, message := utils.ValidatePassword(reqBody.Password); !ok { + http.Error(w, message, http.StatusBadRequest) + return + } + + existingUser, err := models.GetUserByUsername(reqBody.Username) + if err != nil { + logging.AppLog.Error("Error checking username during admin user creation", "error", err, "ip", ip) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if existingUser != nil { + http.Error(w, "Username is already taken", http.StatusConflict) + return + } + + user, err := models.CreateUser(reqBody.Username, reqBody.Password, reqBody.Role) + if err != nil { + logging.AppLog.Error("Failed to create user via admin API", "error", err, "username", reqBody.Username, "ip", ip) + http.Error(w, "Failed to create user", http.StatusInternalServerError) + return + } + + logging.SecurityLog.Info("USER CREATED BY ADMIN", "username", user.Username, "role", user.Role, "ip", ip) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + json.NewEncoder(w).Encode(user) +} -- cgit v1.2.3