189 lines
5.7 KiB
Go
189 lines
5.7 KiB
Go
package resource
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
|
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
|
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
|
"github.com/aarondl/opt/omit"
|
|
"github.com/aarondl/opt/omitnull"
|
|
"github.com/google/uuid"
|
|
"github.com/gorilla/mux"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type user struct {
|
|
Avatar omitnull.Val[string] `json:"avatar"`
|
|
DisplayName omit.Val[string] `json:"display_name"`
|
|
ID omit.Val[int] `json:"id"`
|
|
Initials omit.Val[string] `json:"initials"`
|
|
IsActive omit.Val[bool] `json:"is_active"`
|
|
PasswordHash omit.Val[string] `json:"-"`
|
|
PasswordHashType omit.Val[string] `json:"-"`
|
|
Role omit.Val[string] `json:"role"`
|
|
Tags omit.Val[[]string] `json:"tags"`
|
|
URI omit.Val[string] `json:"uri"`
|
|
Username omit.Val[string] `json:"username"`
|
|
}
|
|
|
|
func User(r *router) *userR {
|
|
return &userR{
|
|
router: r,
|
|
}
|
|
}
|
|
func (res *userR) response(u *platform.User) (*user, error) {
|
|
if u == nil {
|
|
return nil, fmt.Errorf("nil user")
|
|
}
|
|
avatar, err := res.router.UUIDToURI("avatar.ByUUIDGet", u.Avatar)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("id to uri: %w", err)
|
|
}
|
|
uri, err := res.router.IDToURI("user.ByIDGet", u.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("id to uri: %w", err)
|
|
}
|
|
tags := make([]string, 0)
|
|
if u.IsDronePilot {
|
|
tags = append(tags, "drone pilot")
|
|
}
|
|
if u.IsWarrant {
|
|
tags = append(tags, "warrant")
|
|
}
|
|
return &user{
|
|
Avatar: omitnull.FromPtr(avatar),
|
|
DisplayName: omit.From(u.DisplayName),
|
|
ID: omit.From(int(u.ID)),
|
|
Initials: omit.From(u.Initials),
|
|
IsActive: omit.From(u.Active),
|
|
Role: omit.From(u.Role),
|
|
Tags: omit.From(tags),
|
|
URI: omit.From(uri),
|
|
Username: omit.From(u.Username),
|
|
}, nil
|
|
}
|
|
|
|
type userR struct {
|
|
router *router
|
|
}
|
|
type responseListUser struct {
|
|
Users []*platform.User `json:"users"`
|
|
}
|
|
|
|
func (res *userR) ByIDGet(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*platform.User, *nhttp.ErrorWithStatus) {
|
|
vars := mux.Vars(r)
|
|
user_id_str := vars["id"]
|
|
user_id, err := strconv.Atoi(user_id_str)
|
|
u, err := platform.UserByID(ctx, int32(user_id))
|
|
if err != nil {
|
|
return nil, nhttp.NewError("get user: %w", err)
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (res *userR) ByIDPut(ctx context.Context, r *http.Request, user platform.User, updates user) (string, *nhttp.ErrorWithStatus) {
|
|
vars := mux.Vars(r)
|
|
user_id_str := vars["id"]
|
|
user_id, err := strconv.Atoi(user_id_str)
|
|
if err != nil {
|
|
return "", nhttp.NewErrorStatus(http.StatusBadRequest, "user id conversion: %w", err)
|
|
}
|
|
user_changes := &models.UserSetter{}
|
|
if !(user.HasRoot() || user.IsAccountOwner() || user.ID == user_id) {
|
|
return "", nhttp.NewForbidden("Only account owners can change other users")
|
|
}
|
|
if updates.Avatar.IsValue() {
|
|
avatar_uuid, err := res.router.UUIDFromURI("avatar.ByUUIDGet", updates.Avatar.MustGet())
|
|
if err != nil {
|
|
return "", nhttp.NewBadRequest("parse avatar uri: %w", err)
|
|
}
|
|
user_changes.Avatar = omitnull.FromPtr(avatar_uuid)
|
|
} else if updates.Avatar.IsNull() {
|
|
user_changes.Avatar = omitnull.FromPtr[uuid.UUID](nil)
|
|
}
|
|
if updates.DisplayName.IsValue() {
|
|
user_changes.DisplayName = updates.DisplayName
|
|
}
|
|
if updates.Role.IsValue() {
|
|
// Don't allow privilege escalation
|
|
if user.HasRoot() || user.IsAccountOwner() {
|
|
var role enums.Userrole
|
|
v := updates.Role.MustGet()
|
|
err := role.Scan(v)
|
|
if err != nil {
|
|
return "", nhttp.NewBadRequest("invalid role %s: %w", v, err)
|
|
}
|
|
user_changes.Role = omit.From(role)
|
|
} else {
|
|
return "", nhttp.NewBadRequest("you aren't allowed to change roles")
|
|
}
|
|
}
|
|
if updates.Tags.IsValue() {
|
|
for i, v := range updates.Tags.MustGet() {
|
|
user_changes.IsDronePilot = omit.From(false)
|
|
user_changes.IsWarrant = omit.From(false)
|
|
switch v {
|
|
case "drone pilot":
|
|
user_changes.IsDronePilot = omit.From(true)
|
|
case "warrant":
|
|
user_changes.IsWarrant = omit.From(true)
|
|
default:
|
|
return "", nhttp.NewBadRequest("'%s' (item %d) is not a valid tag", v, i)
|
|
}
|
|
}
|
|
}
|
|
|
|
err = platform.UserUpdate(ctx, user, user_id, user_changes)
|
|
if err != nil {
|
|
return "", nhttp.NewError("user update: %w", err)
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func (res *userR) SelfGet(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*user, *nhttp.ErrorWithStatus) {
|
|
resp, err := res.response(&user)
|
|
if err != nil {
|
|
return nil, nhttp.NewError("create response: %w", err)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (res *userR) List(ctx context.Context, r *http.Request, u platform.User, query QueryParams) ([]*user, *nhttp.ErrorWithStatus) {
|
|
users, err := platform.UserList(ctx, u)
|
|
if err != nil {
|
|
return nil, nhttp.NewError("list users: %w", err)
|
|
}
|
|
results := make([]*user, len(users))
|
|
log.Debug().Int("len", len(users)).Msg("building response")
|
|
for i, v := range users {
|
|
log.Debug().Int("i", i).Msg("making results")
|
|
resp, err := res.response(v)
|
|
if err != nil {
|
|
return nil, nhttp.NewError("create response: %w", err)
|
|
}
|
|
results[i] = resp
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
type responseListUserSuggestion struct {
|
|
Users []*platform.User `json:"users"`
|
|
}
|
|
|
|
func (res *userR) SuggestionGet(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*responseListUserSuggestion, *nhttp.ErrorWithStatus) {
|
|
if query.Query == nil {
|
|
return nil, nhttp.NewErrorStatus(http.StatusBadRequest, "you need to include a query")
|
|
}
|
|
users, err := platform.UserSuggestion(ctx, user, *query.Query)
|
|
if err != nil {
|
|
return nil, nhttp.NewError("query suggestions: %w", err)
|
|
}
|
|
return &responseListUserSuggestion{
|
|
Users: users,
|
|
}, nil
|
|
}
|