134422df4d
* check guest access to other members * lint fix
433 lines
10 KiB
Go
433 lines
10 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/mattermost/focalboard/server/services/audit"
|
|
"github.com/mattermost/focalboard/server/utils"
|
|
)
|
|
|
|
func (a *API) registerUsersRoutes(r *mux.Router) {
|
|
// Users APIs
|
|
r.HandleFunc("/users", a.sessionRequired(a.handleGetUsersList)).Methods("POST")
|
|
r.HandleFunc("/users/me", a.sessionRequired(a.handleGetMe)).Methods("GET")
|
|
r.HandleFunc("/users/me/memberships", a.sessionRequired(a.handleGetMyMemberships)).Methods("GET")
|
|
r.HandleFunc("/users/{userID}", a.sessionRequired(a.handleGetUser)).Methods("GET")
|
|
r.HandleFunc("/users/{userID}/config", a.sessionRequired(a.handleUpdateUserConfig)).Methods(http.MethodPut)
|
|
r.HandleFunc("/users/me/config", a.sessionRequired(a.handleGetUserPreferences)).Methods(http.MethodGet)
|
|
}
|
|
|
|
func (a *API) handleGetUsersList(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation POST /users getUsersList
|
|
//
|
|
// Returns a user[]
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: userID
|
|
// in: path
|
|
// description: User ID
|
|
// required: true
|
|
// type: string
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// schema:
|
|
// "$ref": "#/definitions/User"
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
|
|
requestBody, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
var userIDs []string
|
|
if err = json.Unmarshal(requestBody, &userIDs); err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
auditRec := a.makeAuditRecord(r, "getUsersList", audit.Fail)
|
|
defer a.audit.LogRecord(audit.LevelAuth, auditRec)
|
|
|
|
var users []*model.User
|
|
var error error
|
|
|
|
if len(userIDs) == 0 {
|
|
a.errorResponse(w, r, model.NewErrBadRequest("User IDs are empty"))
|
|
return
|
|
}
|
|
|
|
if userIDs[0] == model.SingleUser {
|
|
ws, _ := a.app.GetRootTeam()
|
|
now := utils.GetMillis()
|
|
user := &model.User{
|
|
ID: model.SingleUser,
|
|
Username: model.SingleUser,
|
|
Email: model.SingleUser,
|
|
CreateAt: ws.UpdateAt,
|
|
UpdateAt: now,
|
|
}
|
|
users = append(users, user)
|
|
} else {
|
|
users, error = a.app.GetUsersList(userIDs)
|
|
if error != nil {
|
|
a.errorResponse(w, r, error)
|
|
return
|
|
}
|
|
}
|
|
|
|
ctx := r.Context()
|
|
session := ctx.Value(sessionContextKey).(*model.Session)
|
|
isSystemAdmin := a.permissions.HasPermissionTo(session.UserID, model.PermissionManageSystem)
|
|
|
|
sanitizedUsers := make([]*model.User, 0)
|
|
for _, user := range users {
|
|
canSeeUser, err2 := a.app.CanSeeUser(session.UserID, user.ID)
|
|
if err2 != nil {
|
|
a.errorResponse(w, r, err2)
|
|
return
|
|
}
|
|
if !canSeeUser {
|
|
continue
|
|
}
|
|
if user.ID == session.UserID {
|
|
user.Sanitize(map[string]bool{})
|
|
} else {
|
|
a.app.SanitizeProfile(user, isSystemAdmin)
|
|
}
|
|
sanitizedUsers = append(sanitizedUsers, user)
|
|
}
|
|
|
|
usersList, err := json.Marshal(sanitizedUsers)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
jsonStringResponse(w, http.StatusOK, string(usersList))
|
|
auditRec.Success()
|
|
}
|
|
|
|
func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation GET /users/me getMe
|
|
//
|
|
// Returns the currently logged-in user
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: teamID
|
|
// in: path
|
|
// description: Team ID
|
|
// required: false
|
|
// type: string
|
|
// - name: channelID
|
|
// in: path
|
|
// description: Channel ID
|
|
// required: false
|
|
// type: string
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// schema:
|
|
// "$ref": "#/definitions/User"
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
query := r.URL.Query()
|
|
teamID := query.Get("teamID")
|
|
channelID := query.Get("channelID")
|
|
|
|
userID := getUserID(r)
|
|
|
|
var user *model.User
|
|
var err error
|
|
|
|
auditRec := a.makeAuditRecord(r, "getMe", audit.Fail)
|
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
|
|
|
if userID == model.SingleUser {
|
|
ws, _ := a.app.GetRootTeam()
|
|
now := utils.GetMillis()
|
|
user = &model.User{
|
|
ID: model.SingleUser,
|
|
Username: model.SingleUser,
|
|
Email: model.SingleUser,
|
|
CreateAt: ws.UpdateAt,
|
|
UpdateAt: now,
|
|
}
|
|
} else {
|
|
user, err = a.app.GetUser(userID)
|
|
if err != nil {
|
|
// ToDo: wrap with an invalid token error
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
if teamID != "" && a.permissions.HasPermissionToTeam(userID, teamID, model.PermissionManageTeam) {
|
|
user.Permissions = append(user.Permissions, model.PermissionManageTeam.Id)
|
|
}
|
|
if a.permissions.HasPermissionTo(userID, model.PermissionManageSystem) {
|
|
user.Permissions = append(user.Permissions, model.PermissionManageSystem.Id)
|
|
}
|
|
if channelID != "" && a.permissions.HasPermissionToChannel(userID, channelID, model.PermissionCreatePost) {
|
|
user.Permissions = append(user.Permissions, model.PermissionCreatePost.Id)
|
|
}
|
|
|
|
user.Sanitize(map[string]bool{})
|
|
userData, err := json.Marshal(user)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
jsonBytesResponse(w, http.StatusOK, userData)
|
|
|
|
auditRec.AddMeta("userID", user.ID)
|
|
auditRec.Success()
|
|
}
|
|
|
|
func (a *API) handleGetMyMemberships(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation GET /users/me/memberships getMyMemberships
|
|
//
|
|
// Returns the currently users board memberships
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// schema:
|
|
// type: array
|
|
// items:
|
|
// "$ref": "#/definitions/BoardMember"
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
|
|
userID := getUserID(r)
|
|
|
|
auditRec := a.makeAuditRecord(r, "getMyBoardMemberships", audit.Fail)
|
|
auditRec.AddMeta("userID", userID)
|
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
|
|
|
members, err := a.app.GetMembersForUser(userID)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
membersData, err := json.Marshal(members)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
jsonBytesResponse(w, http.StatusOK, membersData)
|
|
|
|
auditRec.Success()
|
|
}
|
|
|
|
func (a *API) handleGetUser(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation GET /users/{userID} getUser
|
|
//
|
|
// Returns a user
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: userID
|
|
// in: path
|
|
// description: User ID
|
|
// required: true
|
|
// type: string
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// schema:
|
|
// "$ref": "#/definitions/User"
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
|
|
vars := mux.Vars(r)
|
|
userID := vars["userID"]
|
|
|
|
auditRec := a.makeAuditRecord(r, "postBlocks", audit.Fail)
|
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
|
auditRec.AddMeta("userID", userID)
|
|
|
|
user, err := a.app.GetUser(userID)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
ctx := r.Context()
|
|
session := ctx.Value(sessionContextKey).(*model.Session)
|
|
|
|
canSeeUser, err := a.app.CanSeeUser(session.UserID, userID)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
if !canSeeUser {
|
|
a.errorResponse(w, r, model.NewErrNotFound("user ID="+userID))
|
|
return
|
|
}
|
|
|
|
if userID == session.UserID {
|
|
user.Sanitize(map[string]bool{})
|
|
} else {
|
|
a.app.SanitizeProfile(user, a.permissions.HasPermissionTo(session.UserID, model.PermissionManageSystem))
|
|
}
|
|
|
|
userData, err := json.Marshal(user)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
jsonBytesResponse(w, http.StatusOK, userData)
|
|
auditRec.Success()
|
|
}
|
|
|
|
func (a *API) handleUpdateUserConfig(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation PATCH /users/{userID}/config updateUserConfig
|
|
//
|
|
// Updates user config
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// parameters:
|
|
// - name: userID
|
|
// in: path
|
|
// description: User ID
|
|
// required: true
|
|
// type: string
|
|
// - name: Body
|
|
// in: body
|
|
// description: User config patch to apply
|
|
// required: true
|
|
// schema:
|
|
// "$ref": "#/definitions/UserPreferencesPatch"
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
|
|
requestBody, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
var patch *model.UserPreferencesPatch
|
|
err = json.Unmarshal(requestBody, &patch)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
vars := mux.Vars(r)
|
|
userID := vars["userID"]
|
|
|
|
ctx := r.Context()
|
|
session := ctx.Value(sessionContextKey).(*model.Session)
|
|
|
|
auditRec := a.makeAuditRecord(r, "updateUserConfig", audit.Fail)
|
|
defer a.audit.LogRecord(audit.LevelModify, auditRec)
|
|
|
|
// a user can update only own config
|
|
if userID != session.UserID {
|
|
a.errorResponse(w, r, model.NewErrForbidden(""))
|
|
return
|
|
}
|
|
|
|
updatedConfig, err := a.app.UpdateUserConfig(userID, *patch)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
data, err := json.Marshal(updatedConfig)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
jsonBytesResponse(w, http.StatusOK, data)
|
|
auditRec.Success()
|
|
}
|
|
|
|
func (a *API) handleGetUserPreferences(w http.ResponseWriter, r *http.Request) {
|
|
// swagger:operation GET /users/me/config getUserConfig
|
|
//
|
|
// Returns an array of user preferences
|
|
//
|
|
// ---
|
|
// produces:
|
|
// - application/json
|
|
// security:
|
|
// - BearerAuth: []
|
|
// responses:
|
|
// '200':
|
|
// description: success
|
|
// schema:
|
|
// "$ref": "#/definitions/Preferences"
|
|
// default:
|
|
// description: internal error
|
|
// schema:
|
|
// "$ref": "#/definitions/ErrorResponse"
|
|
|
|
userID := getUserID(r)
|
|
|
|
auditRec := a.makeAuditRecord(r, "getUserConfig", audit.Fail)
|
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
|
|
|
preferences, err := a.app.GetUserPreferences(userID)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
data, err := json.Marshal(preferences)
|
|
if err != nil {
|
|
a.errorResponse(w, r, err)
|
|
return
|
|
}
|
|
|
|
jsonBytesResponse(w, http.StatusOK, data)
|
|
auditRec.Success()
|
|
}
|