200 lines
5.3 KiB
Go
200 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func (a *App) audit(operator uint, action, targetType, targetID, metadata string) {
|
|
_ = a.db.Create(&AuditLog{OperatorID: operator, Action: action, TargetType: targetType, TargetID: targetID, Metadata: metadata}).Error
|
|
}
|
|
|
|
func defaultPermissions(role string) []string {
|
|
if role == RolePrivileged {
|
|
return []string{PermDownload, PermRedeem, PermWebDAV, PermWebDAVConfig}
|
|
}
|
|
if role == RoleUser {
|
|
return []string{PermDownload, PermRedeem}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *App) adminListUsers(c *gin.Context) {
|
|
var users []User
|
|
if err := a.db.Order("id asc").Find(&users).Error; err != nil {
|
|
c.JSON(500, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
for i := range users {
|
|
users[i].PasswordHash = ""
|
|
}
|
|
c.JSON(200, gin.H{"data": users})
|
|
}
|
|
|
|
func (a *App) adminCreateUser(c *gin.Context) {
|
|
var in struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
Role string `json:"role"`
|
|
Credits int64 `json:"credits"`
|
|
}
|
|
if c.BindJSON(&in) != nil || strings.TrimSpace(in.Username) == "" || len(in.Password) < 8 {
|
|
c.JSON(400, gin.H{"error": "username and password(8+) required"})
|
|
return
|
|
}
|
|
if in.Role == "" {
|
|
in.Role = RoleUser
|
|
}
|
|
if in.Role != RoleUser && in.Role != RolePrivileged && in.Role != RoleAdmin {
|
|
c.JSON(400, gin.H{"error": "invalid role"})
|
|
return
|
|
}
|
|
hash, err := hashPassword(in.Password)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
admin := c.MustGet("user").(*User)
|
|
var u User
|
|
err = a.db.Transaction(func(tx *gorm.DB) error {
|
|
u = User{Username: strings.TrimSpace(in.Username), PasswordHash: hash, Role: in.Role, Status: UserActive}
|
|
if err := tx.Create(&u).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Create(&CreditAccount{UserID: u.ID, Balance: in.Credits}).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, p := range defaultPermissions(in.Role) {
|
|
if err := tx.Create(&UserPermission{UserID: u.ID, Permission: p, Enabled: true}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if in.Credits > 0 {
|
|
if err := tx.Create(&CreditTransaction{UserID: u.ID, Type: "grant", Amount: in.Credits, BalanceAfter: in.Credits, ReferenceType: "admin", ReferenceID: strconv.FormatUint(uint64(c.MustGet("user").(*User).ID), 10), Reason: "initial credits"}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
u.PasswordHash = ""
|
|
a.audit(admin.ID, "user.create", "user", strconv.FormatUint(uint64(u.ID), 10), "role="+u.Role)
|
|
c.JSON(201, u)
|
|
}
|
|
|
|
func (a *App) adminAudit(c *gin.Context) {
|
|
var logs []AuditLog
|
|
q := a.db.Order("id desc").Limit(200)
|
|
if action := c.Query("action"); action != "" {
|
|
q = q.Where("action = ?", action)
|
|
}
|
|
if err := q.Find(&logs).Error; err != nil {
|
|
c.JSON(500, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(200, gin.H{"data": logs})
|
|
}
|
|
|
|
func (a *App) adminUpdateUser(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
c.Status(400)
|
|
return
|
|
}
|
|
var in struct {
|
|
Password *string `json:"password"`
|
|
Role *string `json:"role"`
|
|
Status *string `json:"status"`
|
|
}
|
|
if c.BindJSON(&in) != nil {
|
|
c.Status(400)
|
|
return
|
|
}
|
|
var u User
|
|
if a.db.First(&u, id).Error != nil {
|
|
c.Status(404)
|
|
return
|
|
}
|
|
if in.Password != nil {
|
|
if len(*in.Password) < 8 {
|
|
c.JSON(400, gin.H{"error": "password must be 8+"})
|
|
return
|
|
}
|
|
h, e := hashPassword(*in.Password)
|
|
if e != nil {
|
|
c.JSON(500, gin.H{"error": e.Error()})
|
|
return
|
|
}
|
|
u.PasswordHash = h
|
|
}
|
|
if in.Role != nil {
|
|
if *in.Role != RoleUser && *in.Role != RolePrivileged && *in.Role != RoleAdmin {
|
|
c.JSON(400, gin.H{"error": "invalid role"})
|
|
return
|
|
}
|
|
u.Role = *in.Role
|
|
}
|
|
if in.Status != nil {
|
|
if *in.Status != UserActive && *in.Status != UserDisabled {
|
|
c.JSON(400, gin.H{"error": "invalid status"})
|
|
return
|
|
}
|
|
u.Status = *in.Status
|
|
}
|
|
operator := c.MustGet("user").(*User)
|
|
if u.ID == operator.ID && (u.Role != RoleAdmin || u.Status == UserDisabled) {
|
|
var admins int64
|
|
if err := a.db.Model(&User{}).Where("role = ? AND status = ?", RoleAdmin, UserActive).Count(&admins).Error; err != nil {
|
|
c.JSON(500, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if admins <= 1 {
|
|
c.JSON(400, gin.H{"error": "cannot remove the last active admin account"})
|
|
return
|
|
}
|
|
}
|
|
if err := a.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Save(&u).Error; err != nil {
|
|
return err
|
|
}
|
|
if in.Password != nil {
|
|
if err := tx.Where("user_id = ?", u.ID).Delete(&Session{}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if in.Role != nil {
|
|
if err := tx.Where("user_id = ?", u.ID).Delete(&UserPermission{}).Error; err != nil {
|
|
return err
|
|
}
|
|
for _, p := range defaultPermissions(u.Role) {
|
|
if err := tx.Create(&UserPermission{UserID: u.ID, Permission: p, Enabled: true}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
c.JSON(500, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
u.PasswordHash = ""
|
|
changes := []string{}
|
|
if in.Password != nil {
|
|
changes = append(changes, "password")
|
|
}
|
|
if in.Role != nil {
|
|
changes = append(changes, "role="+u.Role)
|
|
}
|
|
if in.Status != nil {
|
|
changes = append(changes, "status="+u.Status)
|
|
}
|
|
a.audit(operator.ID, "user.update", "user", strconv.FormatUint(uint64(u.ID), 10), strings.Join(changes, ","))
|
|
c.JSON(200, u)
|
|
}
|