mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
It was a good decision in hindsight to keep the public module as 0.x because this would have been a breaking change again. https://mattermost.atlassian.net/browse/MM-53032 ```release-note Changed the Go module path from github.com/mattermost/mattermost-server/server/v8 to github.com/mattermost/mattermost/server/v8. For the public facing module, it's path is also changed from github.com/mattermost/mattermost-server/server/public to github.com/mattermost/mattermost/server/public ```
223 lines
5.1 KiB
Go
223 lines
5.1 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package api4
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/graph-gophers/dataloader/v6"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/v8/channels/web"
|
|
)
|
|
|
|
// user is an internal graphQL wrapper struct to add resolver methods.
|
|
type user struct {
|
|
model.User
|
|
}
|
|
|
|
// match with api4.getUser
|
|
func getGraphQLUser(ctx context.Context, id string) (*user, error) {
|
|
c, err := getCtx(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if id == model.Me {
|
|
id = c.AppContext.Session().UserId
|
|
}
|
|
|
|
if !model.IsValidId(id) {
|
|
return nil, web.NewInvalidParamError("user_id")
|
|
}
|
|
|
|
loader, err := getUsersLoader(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
thunk := loader.Load(ctx, dataloader.StringKey(id))
|
|
result, err := thunk()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
usr := result.(*model.User)
|
|
|
|
if c.IsSystemAdmin() || c.AppContext.Session().UserId == usr.Id {
|
|
userTermsOfService, appErr := c.App.GetUserTermsOfService(usr.Id)
|
|
if appErr != nil && appErr.StatusCode != http.StatusNotFound {
|
|
return nil, appErr
|
|
}
|
|
|
|
if userTermsOfService != nil {
|
|
usr.TermsOfServiceId = userTermsOfService.TermsOfServiceId
|
|
usr.TermsOfServiceCreateAt = userTermsOfService.CreateAt
|
|
}
|
|
}
|
|
|
|
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
|
|
|
return &user{*usr}, nil
|
|
}
|
|
|
|
// match with api4.getRolesByNames
|
|
func (u *user) Roles(ctx context.Context) ([]*model.Role, error) {
|
|
roleNames := u.GetRoles()
|
|
if len(roleNames) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
loader, err := getRolesLoader(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
thunk := loader.LoadMany(ctx, dataloader.NewKeysFromStrings(roleNames))
|
|
results, errs := thunk()
|
|
// All errors are the same. We just return the first one.
|
|
if len(errs) > 0 && errs[0] != nil {
|
|
return nil, err
|
|
}
|
|
|
|
roles := make([]*model.Role, len(results))
|
|
for i, res := range results {
|
|
roles[i] = res.(*model.Role)
|
|
}
|
|
|
|
return roles, nil
|
|
}
|
|
|
|
// match with api4.getPreferences
|
|
func (u *user) Preferences(ctx context.Context) ([]model.Preference, error) {
|
|
c, err := getCtx(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), u.Id) {
|
|
c.SetPermissionError(model.PermissionEditOtherUsers)
|
|
return nil, c.Err
|
|
}
|
|
|
|
preferences, appErr := c.App.GetPreferencesForUser(u.Id)
|
|
if appErr != nil {
|
|
return nil, appErr
|
|
}
|
|
return preferences, nil
|
|
}
|
|
|
|
// match with api4.getUserStatus
|
|
func (u *user) Status(ctx context.Context) (*model.Status, error) {
|
|
c, err := getCtx(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
statuses, appErr := c.App.GetUserStatusesByIds([]string{u.Id})
|
|
if appErr != nil {
|
|
return nil, appErr
|
|
}
|
|
|
|
if len(statuses) == 0 {
|
|
return nil, model.NewAppError("UserStatus", "api.status.user_not_found.app_error", nil, "", http.StatusNotFound)
|
|
}
|
|
|
|
return statuses[0], nil
|
|
}
|
|
|
|
// match with api4.getSessions
|
|
func (u *user) Sessions(ctx context.Context) ([]*model.Session, error) {
|
|
c, err := getCtx(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), u.Id) {
|
|
c.SetPermissionError(model.PermissionEditOtherUsers)
|
|
return nil, c.Err
|
|
}
|
|
|
|
sessions, appErr := c.App.GetSessions(u.Id)
|
|
if appErr != nil {
|
|
return nil, appErr
|
|
}
|
|
|
|
for _, session := range sessions {
|
|
session.Sanitize()
|
|
}
|
|
|
|
return sessions, nil
|
|
}
|
|
|
|
func graphQLUsersLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
|
|
stringKeys := keys.Keys()
|
|
result := make([]*dataloader.Result, len(stringKeys))
|
|
|
|
c, err := getCtx(ctx)
|
|
if err != nil {
|
|
for i := range result {
|
|
result[i] = &dataloader.Result{Error: err}
|
|
}
|
|
return result
|
|
}
|
|
|
|
users, err := getGraphQLUsers(c, stringKeys)
|
|
if err != nil {
|
|
for i := range result {
|
|
result[i] = &dataloader.Result{Error: err}
|
|
}
|
|
return result
|
|
}
|
|
|
|
for i, user := range users {
|
|
result[i] = &dataloader.Result{Data: user}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func getGraphQLUsers(c *web.Context, userIDs []string) ([]*model.User, error) {
|
|
// Usually this will be called only for one user
|
|
// and cached for the rest of the query. So it's not an issue
|
|
// to run this in a loop.
|
|
for _, id := range userIDs {
|
|
canSee, appErr := c.App.UserCanSeeOtherUser(c.AppContext.Session().UserId, id)
|
|
if appErr != nil || !canSee {
|
|
c.SetPermissionError(model.PermissionViewMembers)
|
|
return nil, c.Err
|
|
}
|
|
}
|
|
|
|
users, appErr := c.App.GetUsers(userIDs)
|
|
if appErr != nil {
|
|
return nil, appErr
|
|
}
|
|
|
|
// Same as earlier, we want to pre-compute this only once
|
|
// because otherwise the resolvers run in multiple goroutines
|
|
// and *User.Sanitize causes a race, and we want to avoid
|
|
// deep-copying every user in all goroutines.
|
|
for _, user := range users {
|
|
if c.AppContext.Session().UserId == user.Id {
|
|
user.Sanitize(map[string]bool{})
|
|
} else {
|
|
c.App.SanitizeProfile(user, c.IsSystemAdmin())
|
|
}
|
|
}
|
|
|
|
// The users need to be in the exact same order as the input slice.
|
|
tmp := make(map[string]*model.User)
|
|
for _, u := range users {
|
|
tmp[u.Id] = u
|
|
}
|
|
|
|
// We reuse the same slice and just rewrite the roles.
|
|
for i, uID := range userIDs {
|
|
users[i] = tmp[uID]
|
|
}
|
|
|
|
return users, nil
|
|
}
|