add(admin): add user
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Evan Reichard 2024-03-11 22:20:41 -07:00
parent f1414e3e4e
commit 2d206826d6
4 changed files with 113 additions and 2 deletions

View File

@ -157,6 +157,7 @@ func (api *API) registerWebAppRoutes(router *gin.Engine) {
router.GET("/admin/import", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdminImport)
router.POST("/admin/import", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appPerformAdminImport)
router.GET("/admin/users", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdminUsers)
router.POST("/admin/users", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appUpdateAdminUsers)
router.GET("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appGetAdmin)
router.POST("/admin", api.authWebAppMiddleware, api.authAdminWebAppMiddleware, api.appPerformAdminAction)
router.POST("/login", api.appAuthLogin)

View File

@ -54,6 +54,20 @@ type requestAdminImport struct {
Type importType `form:"type"`
}
type operationType string
const (
opUpdate operationType = "UPDATE"
opCreate operationType = "CREATE"
)
type requestAdminUpdateUser struct {
User string `form:"user"`
Password string `form:"password"`
isAdmin bool `form:"is_admin"`
Operation operationType `form:"operation"`
}
type requestAdminLogs struct {
Filter string `form:"filter"`
}
@ -225,6 +239,45 @@ func (api *API) appGetAdminUsers(c *gin.Context) {
c.HTML(http.StatusOK, "page/admin-users", templateVars)
}
func (api *API) appUpdateAdminUsers(c *gin.Context) {
templateVars, _ := api.getBaseTemplateVars("admin-users", c)
var rAdminUserUpdate requestAdminUpdateUser
if err := c.ShouldBind(&rAdminUserUpdate); err != nil {
log.Error("Invalid URI Bind")
appErrorPage(c, http.StatusNotFound, "Invalid user update")
return
}
var err error
switch rAdminUserUpdate.Operation {
case opCreate:
err = api.createUser(rAdminUserUpdate.User, rAdminUserUpdate.Password)
case opUpdate:
err = fmt.Errorf("unimplemented")
default:
appErrorPage(c, http.StatusNotFound, "Unknown user operation")
return
}
if err != nil {
appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("Unable to create user: %v", err))
return
}
users, err := api.db.Queries.GetUsers(api.db.Ctx)
if err != nil {
log.Error("GetUsers DB Error: ", err)
appErrorPage(c, http.StatusInternalServerError, fmt.Sprintf("GetUsers DB Error: %v", err))
return
}
templateVars["Data"] = users
c.HTML(http.StatusOK, "page/admin-users", templateVars)
}
func (api *API) appGetAdminImport(c *gin.Context) {
templateVars, _ := api.getBaseTemplateVars("admin-import", c)

View File

@ -460,3 +460,55 @@ func (api *API) rotateAllAuthHashes() error {
return nil
}
func (api *API) createUser(username string, rawPassword string) error {
password := fmt.Sprintf("%x", md5.Sum([]byte(rawPassword)))
if username == "" {
return fmt.Errorf("username can't be empty")
}
if rawPassword == "" {
return fmt.Errorf("password can't be empty")
}
hashedPassword, err := argon2.CreateHash(password, argon2.DefaultParams)
if err != nil {
return fmt.Errorf("unable to create hashed password")
}
// Generate auth hash
rawAuthHash, err := utils.GenerateToken(64)
if err != nil {
return fmt.Errorf("unable to create token for user")
}
// Get current users
currentUsers, err := api.db.Queries.GetUsers(api.db.Ctx)
if err != nil {
return fmt.Errorf("unable to get current users")
}
// Determine if we should be admin
isAdmin := false
if len(currentUsers) == 0 {
isAdmin = true
}
// Create user in DB
authHash := fmt.Sprintf("%x", rawAuthHash)
if rows, err := api.db.Queries.CreateUser(api.db.Ctx, database.CreateUserParams{
ID: username,
Pass: &hashedPassword,
AuthHash: &authHash,
Admin: isAdmin,
}); err != nil {
log.Error("CreateUser DB Error:", err)
return fmt.Errorf("unable to create user")
} else if rows == 0 {
log.Warn("User Already Exists:", username)
return fmt.Errorf("user already exists")
}
return nil
}

View File

@ -9,8 +9,13 @@
action="./users"
class="flex flex-col gap-2 text-black dark:text-white text-sm">
<input type="text"
id="username"
name="username"
id="operation"
name="operation"
value="CREATE"
class="hidden" />
<input type="text"
id="user"
name="user"
placeholder="User"
class="p-2 bg-gray-300 text-black dark:bg-gray-700 dark:text-white" />
<input type="password"