package users import ( "encoding/json" "fmt" "github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx/types" "gitlab.quimbo.fr/odwrtw/canape-sql/random" "gitlab.quimbo.fr/odwrtw/canape-sql/sqly" ) const ( addUserQuery = `INSERT INTO users (name, hash, admin, rawconfig) VALUES ($1, $2, $3, $4) RETURNING id;` getUserQuery = `SELECT * FROM users WHERE name=$1;` updateUserQuery = `UPDATE users SET name=:name, hash=:hash, admin=:admin, 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;` ) const ( UserRole = "user" 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 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 err := u.RawConfig.Unmarshal(&configMap) if err != nil { return err } b, err := json.Marshal(v) if err != nil { return err } r := json.RawMessage(b) configMap[key] = &r b, err = json.Marshal(configMap) if err != nil { return err } return u.RawConfig.UnmarshalJSON(b) } // 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.Error() == "sql: no rows in result set" { return nil, ErrUnknownUser } return nil, err } return u, 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 } func (u *User) HasRole(role string) bool { if role == AdminRole && !u.Admin { return false } return true }