274 lines
6.2 KiB
Go
274 lines
6.2 KiB
Go
package users
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/jmoiron/sqlx/types"
|
|
"github.com/odwrtw/papi"
|
|
|
|
"gitlab.quimbo.fr/odwrtw/canape/backend/config"
|
|
"gitlab.quimbo.fr/odwrtw/canape/backend/random"
|
|
"gitlab.quimbo.fr/odwrtw/canape/backend/sqly"
|
|
)
|
|
|
|
const (
|
|
addUserQuery = `INSERT INTO users (name, hash, admin, rawconfig) VALUES ($1, $2, $3, $4) RETURNING id;`
|
|
getUserQuery = `SELECT * FROM users WHERE name=$1;`
|
|
getUserByIDQuery = `SELECT * FROM users WHERE id=$1;`
|
|
updateUserQuery = `UPDATE users SET name=:name, hash=:hash, admin=:admin, activated=:activated, rawconfig=:rawconfig WHERE id=:id RETURNING *;`
|
|
deleteUseQuery = `DELETE FROM users WHERE id=:id;`
|
|
|
|
addTokenQuery = `INSERT INTO tokens (value, user_id) VALUES ($1, $2) RETURNING id;`
|
|
getTokensQuery = `SELECT id, value FROM tokens WHERE user_id=$1;`
|
|
checkTokenQuery = `SELECT count(*) FROM tokens WHERE user_id=$1 AND value=$2;`
|
|
deleteTokenQuery = `DELETE FROM tokens WHERE user_id=$1 AND value=$2;`
|
|
|
|
getAllUsersQuery = `SELECT * FROM users order by created_at;`
|
|
)
|
|
|
|
const (
|
|
// UserRole represents the user's role
|
|
UserRole = "user"
|
|
// AdminRole represents the admin's role
|
|
AdminRole = "admin"
|
|
)
|
|
|
|
// ErrUnknownUser returned web a user does'nt exist
|
|
var ErrUnknownUser = fmt.Errorf("users: user does'nt exist")
|
|
|
|
// User represents an user
|
|
type User struct {
|
|
sqly.BaseModel
|
|
Name string
|
|
Hash string
|
|
Admin bool
|
|
Activated bool
|
|
RawConfig types.JSONText
|
|
}
|
|
|
|
// GetConfig unmarshal json from specified config key into v
|
|
func (u *User) GetConfig(key string, v interface{}) error {
|
|
var configMap map[string]*json.RawMessage
|
|
err := u.RawConfig.Unmarshal(&configMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if raw, ok := configMap[key]; ok {
|
|
return json.Unmarshal(*raw, v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetConfig marshal v into json for specified config key
|
|
func (u *User) SetConfig(key string, v interface{}) error {
|
|
var configMap map[string]*json.RawMessage
|
|
if err := u.RawConfig.Unmarshal(&configMap); err != nil {
|
|
return err
|
|
}
|
|
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if configMap == nil {
|
|
configMap = map[string]*json.RawMessage{}
|
|
}
|
|
|
|
r := json.RawMessage(b)
|
|
configMap[key] = &r
|
|
b, err = json.Marshal(configMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return u.RawConfig.UnmarshalJSON(b)
|
|
}
|
|
|
|
// NewConfig creates a new empty config
|
|
func (u *User) NewConfig() error {
|
|
configMap := make(map[string]*json.RawMessage)
|
|
b, err := json.Marshal(configMap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return u.RawConfig.UnmarshalJSON(b)
|
|
}
|
|
|
|
// NewPapiClient creates a new papi client for the given user
|
|
func (u *User) NewPapiClient() (*papi.Client, error) {
|
|
var polochonConfig config.UserPolochon
|
|
err := u.GetConfig("polochon", &polochonConfig)
|
|
if err != nil {
|
|
return nil, errors.New("missing polochon config")
|
|
}
|
|
|
|
client, err := papi.New(polochonConfig.URL)
|
|
if err != nil {
|
|
return nil, errors.New("error getting papi client")
|
|
}
|
|
|
|
if polochonConfig.Token != "" {
|
|
client.SetToken(polochonConfig.Token)
|
|
}
|
|
return client, nil
|
|
}
|
|
|
|
// Token represents a token
|
|
type Token struct {
|
|
sqly.BaseModel
|
|
Value string
|
|
}
|
|
|
|
// Get returns user with specified name
|
|
func Get(q sqlx.Queryer, name string) (*User, error) {
|
|
u := &User{}
|
|
err := q.QueryRowx(getUserQuery, name).StructScan(u)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, ErrUnknownUser
|
|
}
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// GetByID returns user using its id
|
|
func GetByID(q sqlx.Queryer, id string) (*User, error) {
|
|
u := &User{}
|
|
err := q.QueryRowx(getUserByIDQuery, id).StructScan(u)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, ErrUnknownUser
|
|
}
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// GetAll returns all the users
|
|
func GetAll(db *sqlx.DB) ([]*User, error) {
|
|
users := []*User{}
|
|
err := db.Select(&users, getAllUsersQuery)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
// Add user to database or raises an error
|
|
func (u *User) Add(q sqlx.Queryer) error {
|
|
var id string
|
|
err := q.QueryRowx(addUserQuery, u.Name, u.Hash, u.Admin, u.RawConfig).Scan(&id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
u.ID = id
|
|
return nil
|
|
}
|
|
|
|
// Update user on database or raise an error
|
|
func (u *User) Update(ex *sqlx.DB) error {
|
|
rows, err := ex.NamedQuery(updateUserQuery, u)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for rows.Next() {
|
|
rows.StructScan(u)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Delete user from database or raise an error
|
|
func (u *User) Delete(ex *sqlx.DB) error {
|
|
_, err := ex.NamedExec(deleteUseQuery, u)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetTokens returns all tokens owned by the user
|
|
func (u *User) GetTokens(ex *sqlx.DB) ([]*Token, error) {
|
|
tokens := []*Token{}
|
|
err := ex.Select(&tokens, getTokensQuery, u.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tokens, nil
|
|
}
|
|
|
|
// NewToken generates a new token for the user
|
|
func (u *User) NewToken(ex *sqlx.DB) (*Token, error) {
|
|
t := &Token{
|
|
Value: random.String(50),
|
|
}
|
|
|
|
var id string
|
|
err := ex.QueryRowx(addTokenQuery, t.Value, u.ID).Scan(&id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
t.ID = id
|
|
return t, nil
|
|
}
|
|
|
|
// CheckToken checks if specified value exists in token's values for the user
|
|
func (u *User) CheckToken(ex *sqlx.DB, value string) (bool, error) {
|
|
var count int
|
|
err := ex.QueryRowx(checkTokenQuery, u.ID, value).Scan(&count)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if count != 1 {
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// DeleteToken delete token by value
|
|
func (u *User) DeleteToken(ex *sqlx.DB, value string) error {
|
|
r, err := ex.Exec(deleteTokenQuery, u.ID, value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, _ := r.RowsAffected()
|
|
if count != 1 {
|
|
return fmt.Errorf("Unexpected number of row deleted: %d", count)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetHash implements auth.User interface
|
|
func (u *User) GetHash() string {
|
|
return u.Hash
|
|
}
|
|
|
|
// GetName implements auth.User interface
|
|
func (u *User) GetName() string {
|
|
return u.Name
|
|
}
|
|
|
|
// HasRole checks if a user as a role
|
|
func (u *User) HasRole(role string) bool {
|
|
if role == AdminRole && !u.Admin {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IsAdmin checks if a user is admin
|
|
func (u *User) IsAdmin() bool {
|
|
return u.HasRole(AdminRole)
|
|
}
|
|
|
|
// IsActivated checks if a user is activated
|
|
func (u *User) IsActivated() bool {
|
|
return u.Activated
|
|
}
|