2020-03-04 11:07:12 +00:00
package main
import (
2022-09-09 18:14:38 +00:00
"fmt"
2022-07-09 17:42:00 +00:00
"log"
"net/http"
2023-03-06 11:53:03 +00:00
"regexp"
2020-03-04 11:07:12 +00:00
"strconv"
2022-09-09 18:14:38 +00:00
"strings"
2020-11-20 14:46:52 +00:00
"time"
2020-03-04 11:07:12 +00:00
2022-07-09 17:42:00 +00:00
"github.com/gin-gonic/gin"
2020-03-04 11:07:12 +00:00
)
2021-03-01 16:47:00 +00:00
var currentPromo uint = 0
2022-07-09 17:42:00 +00:00
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 )
2022-09-03 14:16:41 +00:00
declareAPIAuthKeysRoutes ( usersRoutes )
2022-09-09 18:14:38 +00:00
declareAPIAuthWorksRoutes ( usersRoutes )
2022-07-09 17:42:00 +00:00
}
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 )
} )
2022-09-04 11:23:23 +00:00
router . GET ( "/session" , func ( c * gin . Context ) {
c . JSON ( http . StatusOK , c . MustGet ( "Session" ) . ( * Session ) )
} )
2022-07-09 17:42:00 +00:00
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
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
2022-09-09 18:14:38 +00:00
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 )
2022-07-09 17:42:00 +00:00
} )
2023-03-08 04:04:59 +00:00
// 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 )
} )
2022-07-09 17:42:00 +00:00
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 )
2023-03-05 13:20:40 +00:00
declareAPIAdminWorksRoutes ( usersRoutes )
2022-07-09 17:42:00 +00:00
}
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
2020-03-04 11:07:12 +00:00
}
2022-07-09 17:42:00 +00:00
c . Next ( )
2020-03-04 11:07:12 +00:00
}
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" `
2021-03-01 16:47:00 +00:00
Promo uint ` json:"promo" `
2022-08-02 09:41:13 +00:00
Groups string ` json:"groups,omitempty" `
2020-03-04 11:07:12 +00:00
IsAdmin bool ` json:"is_admin" `
}
func getUsers ( ) ( users [ ] User , err error ) {
2021-11-18 11:12:28 +00:00
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 {
2020-03-04 11:07:12 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var u User
2021-09-15 22:26:09 +00:00
if err = rows . Scan ( & u . Id , & u . Login , & u . Email , & u . Firstname , & u . Lastname , & u . Time , & u . Promo , & u . Groups , & u . IsAdmin ) ; err != nil {
2020-03-04 11:07:12 +00:00
return
}
users = append ( users , u )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2023-03-06 11:53:03 +00:00
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
}
}
2021-11-18 11:12:28 +00:00
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
}
}
2022-07-09 17:42:00 +00:00
func getUser ( id int ) ( u * User , err error ) {
u = new ( User )
2021-09-15 22:26:09 +00:00
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 )
2020-03-04 11:07:12 +00:00
return
}
2022-07-09 17:42:00 +00:00
func getUserByLogin ( login string ) ( u * User , err error ) {
u = new ( User )
2021-09-15 22:26:09 +00:00
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 )
2020-03-04 11:07:12 +00:00
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
}
2022-11-11 10:14:43 +00:00
func NewUser ( login string , email string , firstname string , lastname string , promo uint , groups string ) ( * User , error ) {
2020-03-04 11:07:12 +00:00
t := time . Now ( )
2022-11-11 10:14:43 +00:00
if res , err := DBExec ( "INSERT INTO users (login, email, firstname, lastname, time, promo, groups) VALUES (?, ?, ?, ?, ?, ?, ?)" , login , email , firstname , lastname , t , promo , groups ) ; err != nil {
2022-07-09 17:42:00 +00:00
return nil , err
2020-03-04 11:07:12 +00:00
} else if sid , err := res . LastInsertId ( ) ; err != nil {
2022-07-09 17:42:00 +00:00
return nil , err
2020-03-04 11:07:12 +00:00
} else {
2022-11-11 10:14:43 +00:00
return & User { sid , login , email , firstname , lastname , t , promo , groups , false } , nil
2020-03-04 11:07:12 +00:00
}
}
2023-03-08 04:04:59 +00:00
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"
}
2020-03-04 11:07:12 +00:00
func ( u User ) Update ( ) ( int64 , error ) {
2021-09-15 22:26:09 +00:00
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 {
2020-03-04 11:07:12 +00:00
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
}
}
2022-07-09 17:42:00 +00:00
func updateUser ( c * gin . Context ) {
current := c . MustGet ( "user" ) . ( * User )
2020-03-04 11:07:12 +00:00
var new User
2022-07-09 17:42:00 +00:00
if err := c . ShouldBindJSON ( & new ) ; err != nil {
c . AbortWithStatusJSON ( http . StatusBadRequest , gin . H { "errmsg" : err . Error ( ) } )
return
2020-03-04 11:07:12 +00:00
}
current . Login = new . Login
current . Email = new . Email
current . Firstname = new . Firstname
current . Lastname = new . Lastname
current . Time = new . Time
2021-03-01 16:47:00 +00:00
current . Promo = new . Promo
2021-09-15 22:26:09 +00:00
current . Groups = new . Groups
2022-07-09 17:42:00 +00:00
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 )
}
2020-03-04 11:07:12 +00:00
}