353 lines
9.5 KiB
Go
353 lines
9.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
var currentPromo uint = 0
|
|
|
|
func declareAPIAuthUsersRoutes(router *gin.RouterGroup) {
|
|
usersRoutes := router.Group("/users/:uid")
|
|
usersRoutes.Use(userHandler)
|
|
usersRoutes.Use(sameUserMiddleware)
|
|
|
|
usersRoutes.GET("", func(c *gin.Context) {
|
|
u := c.MustGet("user").(*User)
|
|
|
|
c.JSON(http.StatusOK, u)
|
|
})
|
|
|
|
declareAPIAuthSurveysRoutes(usersRoutes)
|
|
declareAPIAuthKeysRoutes(usersRoutes)
|
|
declareAPIAuthWorksRoutes(usersRoutes)
|
|
}
|
|
|
|
func declareAPIAdminUsersRoutes(router *gin.RouterGroup) {
|
|
router.GET("/promos", func(c *gin.Context) {
|
|
promos, err := getPromos()
|
|
if err != nil {
|
|
log.Println("Unable to getPromos:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve promotions. Please try again later."})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, promos)
|
|
})
|
|
|
|
router.GET("/session", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, c.MustGet("Session").(*Session))
|
|
})
|
|
|
|
router.GET("/users", func(c *gin.Context) {
|
|
users, err := getUsers()
|
|
if err != nil {
|
|
log.Println("Unable to getUsers:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve users. Please try again later."})
|
|
return
|
|
}
|
|
|
|
var filterPromo *uint64
|
|
if c.Query("promo") != "" {
|
|
fPromo, err := strconv.ParseUint(c.Query("promo"), 10, 64)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to parse promo: %s", err.Error())})
|
|
return
|
|
}
|
|
filterPromo = &fPromo
|
|
}
|
|
|
|
filterGroup := c.Query("group")
|
|
|
|
if filterPromo == nil && filterGroup == "" {
|
|
c.JSON(http.StatusOK, users)
|
|
return
|
|
}
|
|
|
|
var ret []User
|
|
for _, u := range users {
|
|
if (filterPromo == nil || uint(*filterPromo) == u.Promo) && (filterGroup == "" || strings.Contains(u.Groups, filterGroup)) {
|
|
ret = append(ret, u)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, ret)
|
|
})
|
|
|
|
// Anonymize old accounts
|
|
router.PATCH("/users", func(c *gin.Context) {
|
|
users, err := getUsers()
|
|
if err != nil {
|
|
log.Println("Unable to getUsers:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve users. Please try again later."})
|
|
return
|
|
}
|
|
|
|
var filterPromo *uint64
|
|
if c.Query("promo") != "" {
|
|
fPromo, err := strconv.ParseUint(c.Query("promo"), 10, 64)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": fmt.Sprintf("Unable to parse promo: %s", err.Error())})
|
|
return
|
|
}
|
|
filterPromo = &fPromo
|
|
}
|
|
|
|
for _, u := range users {
|
|
if (filterPromo == nil && u.Promo < currentPromo-1) || (filterPromo != nil && uint(*filterPromo) == u.Promo) {
|
|
u.Anonymize()
|
|
_, err = u.Update()
|
|
if err != nil {
|
|
log.Printf("Unable to anonymize %s: %s", u.Login, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, true)
|
|
})
|
|
|
|
usersRoutes := router.Group("/users/:uid")
|
|
usersRoutes.Use(userHandler)
|
|
|
|
usersRoutes.PUT("", updateUser)
|
|
usersRoutes.DELETE("", func(c *gin.Context) {
|
|
u := c.MustGet("user").(*User)
|
|
|
|
if _, err := u.Delete(); err != nil {
|
|
log.Println("Unable to Delete user:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete the user. Please try again later."})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, nil)
|
|
})
|
|
|
|
declareAPIAdminUserCorrectionsRoutes(usersRoutes)
|
|
declareAPIAdminUserQuestionsRoutes(usersRoutes)
|
|
declareAPIAdminWorksRoutes(usersRoutes)
|
|
}
|
|
|
|
func userHandler(c *gin.Context) {
|
|
var user *User
|
|
|
|
uid, err := strconv.Atoi(string(c.Param("uid")))
|
|
if err != nil {
|
|
user, err = getUserByLogin(c.Param("uid"))
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad user identifier."})
|
|
return
|
|
}
|
|
} else {
|
|
user, err = getUser(uid)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "User not found."})
|
|
return
|
|
}
|
|
}
|
|
|
|
c.Set("user", user)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
func sameUserMiddleware(c *gin.Context) {
|
|
user := c.MustGet("user").(*User)
|
|
loggeduser := c.MustGet("LoggedUser").(*User)
|
|
|
|
if user.Id != loggeduser.Id && !loggeduser.IsAdmin {
|
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied."})
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
|
|
type User struct {
|
|
Id int64 `json:"id"`
|
|
Login string `json:"login"`
|
|
Email string `json:"email"`
|
|
Firstname string `json:"firstname"`
|
|
Lastname string `json:"lastname"`
|
|
Time time.Time `json:"time"`
|
|
Promo uint `json:"promo"`
|
|
Groups string `json:"groups,omitempty"`
|
|
IsAdmin bool `json:"is_admin"`
|
|
}
|
|
|
|
func getUsers() (users []User, err error) {
|
|
if rows, errr := DBQuery("SELECT id_user, login, email, firstname, lastname, time, promo, groups, is_admin FROM users ORDER BY promo DESC, id_user DESC"); errr != nil {
|
|
return nil, errr
|
|
} else {
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var u User
|
|
if err = rows.Scan(&u.Id, &u.Login, &u.Email, &u.Firstname, &u.Lastname, &u.Time, &u.Promo, &u.Groups, &u.IsAdmin); err != nil {
|
|
return
|
|
}
|
|
users = append(users, u)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func getFilteredUsers(promo uint, group string) (users []User, err error) {
|
|
// Avoid SQL injection: check group name doesn't contains harmful content
|
|
var validGroup = regexp.MustCompile(`^[a-z0-9-]*$`)
|
|
if !validGroup.MatchString(group) {
|
|
return nil, fmt.Errorf("%q is not a valid group name", group)
|
|
}
|
|
|
|
if rows, errr := DBQuery("SELECT id_user, login, email, firstname, lastname, time, promo, groups, is_admin FROM users WHERE promo = ? AND groups LIKE '%,"+group+",%' ORDER BY promo DESC, id_user DESC", promo); errr != nil {
|
|
return nil, errr
|
|
} else {
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var u User
|
|
if err = rows.Scan(&u.Id, &u.Login, &u.Email, &u.Firstname, &u.Lastname, &u.Time, &u.Promo, &u.Groups, &u.IsAdmin); err != nil {
|
|
return
|
|
}
|
|
users = append(users, u)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func getPromos() (promos []uint, err error) {
|
|
if rows, errr := DBQuery("SELECT DISTINCT promo FROM users ORDER BY promo DESC"); errr != nil {
|
|
return nil, errr
|
|
} else {
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var p uint
|
|
if err = rows.Scan(&p); err != nil {
|
|
return
|
|
}
|
|
promos = append(promos, p)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
func getUser(id int) (u *User, err error) {
|
|
u = new(User)
|
|
err = DBQueryRow("SELECT id_user, login, email, firstname, lastname, time, promo, groups, is_admin FROM users WHERE id_user=?", id).Scan(&u.Id, &u.Login, &u.Email, &u.Firstname, &u.Lastname, &u.Time, &u.Promo, &u.Groups, &u.IsAdmin)
|
|
return
|
|
}
|
|
|
|
func getUserByLogin(login string) (u *User, err error) {
|
|
u = new(User)
|
|
err = DBQueryRow("SELECT id_user, login, email, firstname, lastname, time, promo, groups, is_admin FROM users WHERE login=?", login).Scan(&u.Id, &u.Login, &u.Email, &u.Firstname, &u.Lastname, &u.Time, &u.Promo, &u.Groups, &u.IsAdmin)
|
|
return
|
|
}
|
|
|
|
func userExists(login string) bool {
|
|
var z int
|
|
err := DBQueryRow("SELECT 1 FROM users WHERE login=?", login).Scan(&z)
|
|
return err == nil && z == 1
|
|
}
|
|
|
|
func NewUser(login string, email string, firstname string, lastname string, promo uint, groups string) (*User, error) {
|
|
t := time.Now()
|
|
if res, err := DBExec("INSERT INTO users (login, email, firstname, lastname, time, promo, groups) VALUES (?, ?, ?, ?, ?, ?, ?)", login, email, firstname, lastname, t, promo, groups); err != nil {
|
|
return nil, err
|
|
} else if sid, err := res.LastInsertId(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return &User{sid, login, email, firstname, lastname, t, promo, groups, false}, nil
|
|
}
|
|
}
|
|
|
|
func (u *User) Anonymize() {
|
|
u.Login = fmt.Sprintf("Anonyme #%d", u.Id)
|
|
u.Email = fmt.Sprintf("anonyme-%d@non-existant.email", u.Id)
|
|
u.Firstname = "Arnaud"
|
|
u.Lastname = "Nimes"
|
|
}
|
|
|
|
func (u User) Update() (int64, error) {
|
|
if res, err := DBExec("UPDATE users SET login = ?, email = ?, firstname = ?, lastname = ?, time = ?, promo = ?, groups = ? WHERE id_user = ?", u.Login, u.Email, u.Firstname, u.Lastname, u.Time, u.Promo, u.Groups, u.Id); err != nil {
|
|
return 0, err
|
|
} else if nb, err := res.RowsAffected(); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return nb, err
|
|
}
|
|
}
|
|
|
|
func (u User) MakeAdmin(value bool) (User, error) {
|
|
if _, err := DBExec("UPDATE users SET is_admin = ? WHERE id_user = ?", value, u.Id); err != nil {
|
|
return u, err
|
|
} else {
|
|
u.IsAdmin = value
|
|
return u, err
|
|
}
|
|
}
|
|
|
|
func (u User) Delete() (int64, error) {
|
|
if res, err := DBExec("DELETE FROM users WHERE id_user = ?", u.Id); err != nil {
|
|
return 0, err
|
|
} else if nb, err := res.RowsAffected(); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return nb, err
|
|
}
|
|
}
|
|
|
|
func ClearUsers() (int64, error) {
|
|
if res, err := DBExec("DELETE FROM users"); err != nil {
|
|
return 0, err
|
|
} else if nb, err := res.RowsAffected(); err != nil {
|
|
return 0, err
|
|
} else {
|
|
return nb, err
|
|
}
|
|
}
|
|
|
|
func updateUser(c *gin.Context) {
|
|
current := c.MustGet("user").(*User)
|
|
|
|
var new User
|
|
if err := c.ShouldBindJSON(&new); err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
current.Login = new.Login
|
|
current.Email = new.Email
|
|
current.Firstname = new.Firstname
|
|
current.Lastname = new.Lastname
|
|
current.Time = new.Time
|
|
current.Promo = new.Promo
|
|
current.Groups = new.Groups
|
|
|
|
if u, err := current.Update(); err != nil {
|
|
log.Println("Unable to Update user:", err)
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to update the given user. Please try again later."})
|
|
return
|
|
} else {
|
|
c.JSON(http.StatusOK, u)
|
|
}
|
|
}
|