MM24459: Implement ldap picture sync (#14540)
* implement ldap picture sync * add testing timeout, and no change * Revert "add testing timeout, and no change" This reverts commit 765621a7290074e5664c4ca2a2843c84e01f4cf1. * update app-layer * updates from code review * update app-layers * remove debug statements Co-authored-by: mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
d7cb890f34
Коммит
e8081b7a0f
@@ -267,6 +267,8 @@ type AppIface interface {
|
||||
// This function deviates from other authorization checks in returning an error instead of just
|
||||
// a boolean, allowing the permission failure to be exposed with more granularity.
|
||||
SessionHasPermissionToManageBot(session model.Session, botUserId string) *model.AppError
|
||||
// SessionIsRegistered determines if a specific session has been registered
|
||||
SessionIsRegistered(session model.Session) bool
|
||||
// SetBotIconImage sets LHS icon for a bot.
|
||||
SetBotIconImage(botUserId string, file io.ReadSeeker) *model.AppError
|
||||
// SetBotIconImageFromMultiPartFile sets LHS icon for a bot.
|
||||
@@ -350,6 +352,7 @@ type AppIface interface {
|
||||
AddUserToTeamByInviteId(inviteId string, userId string) (*model.Team, *model.AppError)
|
||||
AddUserToTeamByTeamId(teamId string, user *model.User) *model.AppError
|
||||
AddUserToTeamByToken(userId string, tokenId string) (*model.Team, *model.AppError)
|
||||
AdjustImage(file io.Reader) (*bytes.Buffer, *model.AppError)
|
||||
AllowOAuthAppAccessToUser(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
||||
AsymmetricSigningKey() *ecdsa.PrivateKey
|
||||
AttachDeviceId(sessionId string, deviceId string, expiresAt int64) *model.AppError
|
||||
|
||||
@@ -165,6 +165,12 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
|
||||
a.SetSession(session)
|
||||
|
||||
if user.AuthService == model.USER_AUTH_SERVICE_LDAP && a.Ldap() != nil {
|
||||
a.Srv().Go(func() {
|
||||
a.Ldap().UpdateProfilePictureIfNecessary(user, session)
|
||||
})
|
||||
}
|
||||
|
||||
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := a.PluginContext()
|
||||
|
||||
@@ -541,6 +541,28 @@ func (a *OpenTracingAppLayer) AddUserToTeamByToken(userId string, tokenId string
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AdjustImage(file io.Reader) (*bytes.Buffer, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AdjustImage")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.AdjustImage(file)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AllowOAuthAppAccessToUser(userId string, authRequest *model.AuthorizeRequest) (string, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AllowOAuthAppAccessToUser")
|
||||
@@ -12768,6 +12790,23 @@ func (a *OpenTracingAppLayer) SessionHasPermissionToUserOrBot(session model.Sess
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SessionIsRegistered(session model.Session) bool {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionIsRegistered")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store.SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.SessionIsRegistered(session)
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) SetActiveChannel(userId string, channelId string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SetActiveChannel")
|
||||
|
||||
15
app/user.go
15
app/user.go
@@ -855,12 +855,11 @@ func (a *App) SetProfileImageFromMultiPartFile(userId string, file multipart.Fil
|
||||
return a.SetProfileImageFromFile(userId, file)
|
||||
}
|
||||
|
||||
func (a *App) SetProfileImageFromFile(userId string, file io.Reader) *model.AppError {
|
||||
|
||||
func (a *App) AdjustImage(file io.Reader) (*bytes.Buffer, *model.AppError) {
|
||||
// Decode image into Image object
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.decode.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("SetProfileImage", "api.user.upload_profile_user.decode.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
orientation, _ := getImageOrientation(file)
|
||||
@@ -873,9 +872,17 @@ func (a *App) SetProfileImageFromFile(userId string, file io.Reader) *model.AppE
|
||||
buf := new(bytes.Buffer)
|
||||
err = png.Encode(buf, img)
|
||||
if err != nil {
|
||||
return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.encode.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("SetProfileImage", "api.user.upload_profile_user.encode.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (a *App) SetProfileImageFromFile(userId string, file io.Reader) *model.AppError {
|
||||
|
||||
buf, err := a.AdjustImage(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := "users/" + userId + "/profile.png"
|
||||
|
||||
if _, err := a.WriteFile(buf, path); err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
oauthgitlab "github.com/mattermost/mattermost-server/v5/model/gitlab"
|
||||
"github.com/mattermost/mattermost-server/v5/utils/testutils"
|
||||
)
|
||||
|
||||
func TestIsUsernameTaken(t *testing.T) {
|
||||
@@ -121,6 +122,31 @@ func TestSetDefaultProfileImage(t *testing.T) {
|
||||
assert.Equal(t, int64(0), user.LastPictureUpdate)
|
||||
}
|
||||
|
||||
func TestAdjustProfileImage(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
_, err := th.App.AdjustImage(bytes.NewReader([]byte{}))
|
||||
require.Error(t, err)
|
||||
|
||||
// test image isn't the correct dimensions
|
||||
// it should be adjusted
|
||||
testjpg, error := testutils.ReadTestFile("testjpg.jpg")
|
||||
require.Nil(t, error)
|
||||
adjusted, err := th.App.AdjustImage(bytes.NewReader(testjpg))
|
||||
require.Nil(t, err)
|
||||
assert.True(t, adjusted.Len() > 0)
|
||||
assert.NotEqual(t, testjpg, adjusted)
|
||||
|
||||
// default image should require adjustement
|
||||
user := th.BasicUser
|
||||
image, err := th.App.GetDefaultProfileImage(user)
|
||||
require.Nil(t, err)
|
||||
image2, err := th.App.AdjustImage(bytes.NewReader(image))
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, image, image2.Bytes())
|
||||
}
|
||||
|
||||
func TestUpdateUserToRestrictedDomain(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -29,6 +29,12 @@ type webConnDirectMessage struct {
|
||||
msg model.WebSocketMessage
|
||||
}
|
||||
|
||||
type webConnSessionMessage struct {
|
||||
userId string
|
||||
sessionToken string
|
||||
isRegistered chan bool
|
||||
}
|
||||
|
||||
// Hub is the central place to manage all websocket connections in the server.
|
||||
// It handles different websocket events and sending messages to individual
|
||||
// user connections.
|
||||
@@ -47,20 +53,22 @@ type Hub struct {
|
||||
activity chan *webConnActivityMessage
|
||||
directMsg chan *webConnDirectMessage
|
||||
explicitStop bool
|
||||
checkRegistered chan *webConnSessionMessage
|
||||
}
|
||||
|
||||
// NewWebHub creates a new Hub.
|
||||
func (a *App) NewWebHub() *Hub {
|
||||
return &Hub{
|
||||
app: a,
|
||||
register: make(chan *WebConn),
|
||||
unregister: make(chan *WebConn),
|
||||
broadcast: make(chan *model.WebSocketEvent, broadcastQueueSize),
|
||||
stop: make(chan struct{}),
|
||||
didStop: make(chan struct{}),
|
||||
invalidateUser: make(chan string),
|
||||
activity: make(chan *webConnActivityMessage),
|
||||
directMsg: make(chan *webConnDirectMessage),
|
||||
app: a,
|
||||
register: make(chan *WebConn),
|
||||
unregister: make(chan *WebConn),
|
||||
broadcast: make(chan *model.WebSocketEvent, broadcastQueueSize),
|
||||
stop: make(chan struct{}),
|
||||
didStop: make(chan struct{}),
|
||||
invalidateUser: make(chan string),
|
||||
activity: make(chan *webConnActivityMessage),
|
||||
directMsg: make(chan *webConnDirectMessage),
|
||||
checkRegistered: make(chan *webConnSessionMessage),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +297,15 @@ func (a *App) UpdateWebConnUserActivity(session model.Session, activityAt int64)
|
||||
}
|
||||
}
|
||||
|
||||
// SessionIsRegistered determines if a specific session has been registered
|
||||
func (a *App) SessionIsRegistered(session model.Session) bool {
|
||||
hub := a.GetHubForUserId(session.UserId)
|
||||
if hub != nil {
|
||||
return hub.IsRegistered(session.UserId, session.Token)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Register registers a connection to the hub.
|
||||
func (h *Hub) Register(webConn *WebConn) {
|
||||
select {
|
||||
@@ -305,6 +322,21 @@ func (h *Hub) Unregister(webConn *WebConn) {
|
||||
}
|
||||
}
|
||||
|
||||
// Determines if a user's session is registered a connection from the hub.
|
||||
func (h *Hub) IsRegistered(userId, sessionToken string) bool {
|
||||
ws := &webConnSessionMessage{
|
||||
userId: userId,
|
||||
sessionToken: sessionToken,
|
||||
isRegistered: make(chan bool),
|
||||
}
|
||||
select {
|
||||
case h.checkRegistered <- ws:
|
||||
return <-ws.isRegistered
|
||||
case <-h.stop:
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Broadcast broadcasts the message to all connections in the hub.
|
||||
func (h *Hub) Broadcast(message *model.WebSocketEvent) {
|
||||
// XXX: The hub nil check is because of the way we setup our tests. We call `app.NewServer()`
|
||||
@@ -375,6 +407,15 @@ func (h *Hub) Start() {
|
||||
|
||||
for {
|
||||
select {
|
||||
case webSessionMessage := <-h.checkRegistered:
|
||||
conns := connIndex.ForUser(webSessionMessage.userId)
|
||||
var isRegistered bool
|
||||
for _, item := range conns {
|
||||
if item.sessionToken.Load().(string) == webSessionMessage.sessionToken {
|
||||
isRegistered = true
|
||||
}
|
||||
}
|
||||
webSessionMessage.isRegistered <- isRegistered
|
||||
case webConn := <-h.register:
|
||||
connIndex.Add(webConn)
|
||||
atomic.StoreInt64(&h.connectionCount, int64(len(connIndex.All())))
|
||||
|
||||
@@ -12,13 +12,16 @@ import (
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
goi18n "github.com/mattermost/go-i18n/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
)
|
||||
|
||||
func dummyWebsocketHandler(t *testing.T) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
mlog.Debug("dummyWebsocketHandler")
|
||||
upgrader := &websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
@@ -106,3 +109,31 @@ func TestHubStopRaceCondition(t *testing.T) {
|
||||
require.FailNow(t, "hub call did not return within 15 seconds after stop")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHubIsRegistered(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
s := httptest.NewServer(dummyWebsocketHandler(t))
|
||||
defer s.Close()
|
||||
|
||||
th.App.HubStart()
|
||||
wc1 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||
wc2 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||
wc3 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||
defer wc1.Close()
|
||||
defer wc2.Close()
|
||||
defer wc3.Close()
|
||||
|
||||
session1 := wc1.session.Load().(*model.Session)
|
||||
|
||||
assert.True(t, th.App.SessionIsRegistered(*session1))
|
||||
assert.True(t, th.App.SessionIsRegistered(*wc2.session.Load().(*model.Session)))
|
||||
assert.True(t, th.App.SessionIsRegistered(*wc3.session.Load().(*model.Session)))
|
||||
|
||||
session4, appErr := th.App.CreateSession(&model.Session{
|
||||
UserId: th.BasicUser2.Id,
|
||||
})
|
||||
require.Nil(t, appErr)
|
||||
assert.False(t, th.App.SessionIsRegistered(*session4))
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user