Files
mattermost/server/channels/api4/resolver_user.go
Agniva De Sarker efaa6264cc MM-53032: Fix module path after repo rename (#23689)
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
```
2023-06-11 10:54:35 +05:30

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
}