PLT-3383: image proxy support (#7991)
* image proxy support * go vet fix, remove mistakenly added coverage file * fix test compile error * add validation to config settings and documentation to model functions * add message_source field to post
Этот коммит содержится в:
@@ -270,7 +270,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POSTED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", post.ToJson())
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
message.Add("channel_type", channel.Type)
|
||||
message.Add("channel_display_name", channelName)
|
||||
message.Add("channel_name", channel.Name)
|
||||
|
||||
132
app/post.go
132
app/post.go
@@ -4,6 +4,11 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -309,7 +314,7 @@ func (a *App) SendEphemeralPost(userId string, post *model.Post) *model.Post {
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, "", post.ChannelId, userId, nil)
|
||||
message.Add("post", post.ToJson())
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
@@ -419,7 +424,7 @@ func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *mo
|
||||
|
||||
func (a *App) sendUpdatedPostEvent(post *model.Post) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", post.ToJson())
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
@@ -562,7 +567,7 @@ func (a *App) DeletePost(postId string) (*model.Post, *model.AppError) {
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", post.ToJson())
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
@@ -823,3 +828,124 @@ func (a *App) DoPostAction(postId string, actionId string, userId string) *model
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) PostListWithProxyAddedToImageURLs(list *model.PostList) *model.PostList {
|
||||
if f := a.ImageProxyAdder(); f != nil {
|
||||
return list.WithRewrittenImageURLs(f)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (a *App) PostWithProxyAddedToImageURLs(post *model.Post) *model.Post {
|
||||
if f := a.ImageProxyAdder(); f != nil {
|
||||
return post.WithRewrittenImageURLs(f)
|
||||
}
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) PostWithProxyRemovedFromImageURLs(post *model.Post) *model.Post {
|
||||
if f := a.ImageProxyRemover(); f != nil {
|
||||
return post.WithRewrittenImageURLs(f)
|
||||
}
|
||||
return post
|
||||
}
|
||||
|
||||
func (a *App) PostPatchWithProxyRemovedFromImageURLs(patch *model.PostPatch) *model.PostPatch {
|
||||
if f := a.ImageProxyRemover(); f != nil {
|
||||
return patch.WithRewrittenImageURLs(f)
|
||||
}
|
||||
return patch
|
||||
}
|
||||
|
||||
func (a *App) imageProxyConfig() (proxyType, proxyURL, options, siteURL string) {
|
||||
cfg := a.Config()
|
||||
|
||||
if cfg.ServiceSettings.ImageProxyURL == nil || cfg.ServiceSettings.ImageProxyType == nil || cfg.ServiceSettings.SiteURL == nil {
|
||||
return
|
||||
}
|
||||
|
||||
proxyURL = *cfg.ServiceSettings.ImageProxyURL
|
||||
proxyType = *cfg.ServiceSettings.ImageProxyType
|
||||
siteURL = *cfg.ServiceSettings.SiteURL
|
||||
|
||||
if proxyURL == "" || proxyType == "" {
|
||||
return "", "", "", ""
|
||||
}
|
||||
|
||||
if proxyURL[len(proxyURL)-1] != '/' {
|
||||
proxyURL += "/"
|
||||
}
|
||||
|
||||
if cfg.ServiceSettings.ImageProxyOptions != nil {
|
||||
options = *cfg.ServiceSettings.ImageProxyOptions
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (a *App) ImageProxyAdder() func(string) string {
|
||||
proxyType, proxyURL, options, siteURL := a.imageProxyConfig()
|
||||
if proxyType == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(url string) string {
|
||||
if strings.HasPrefix(url, proxyURL) {
|
||||
return url
|
||||
}
|
||||
|
||||
if url[0] == '/' {
|
||||
url = siteURL + url
|
||||
}
|
||||
|
||||
switch proxyType {
|
||||
case "atmos/camo":
|
||||
mac := hmac.New(sha1.New, []byte(options))
|
||||
mac.Write([]byte(url))
|
||||
digest := hex.EncodeToString(mac.Sum(nil))
|
||||
return proxyURL + digest + "/" + hex.EncodeToString([]byte(url))
|
||||
case "willnorris/imageproxy":
|
||||
options := strings.Split(options, "|")
|
||||
if len(options) > 1 {
|
||||
mac := hmac.New(sha256.New, []byte(options[1]))
|
||||
mac.Write([]byte(url))
|
||||
digest := base64.URLEncoding.EncodeToString(mac.Sum(nil))
|
||||
if options[0] == "" {
|
||||
return proxyURL + "s" + digest + "/" + url
|
||||
}
|
||||
return proxyURL + options[0] + ",s" + digest + "/" + url
|
||||
}
|
||||
return proxyURL + options[0] + "/" + url
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) ImageProxyRemover() (f func(string) string) {
|
||||
proxyType, proxyURL, _, _ := a.imageProxyConfig()
|
||||
if proxyType == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(url string) string {
|
||||
switch proxyType {
|
||||
case "atmos/camo":
|
||||
if strings.HasPrefix(url, proxyURL) {
|
||||
if slash := strings.IndexByte(url[len(proxyURL):], '/'); slash >= 0 {
|
||||
if decoded, err := hex.DecodeString(url[len(proxyURL)+slash+1:]); err == nil {
|
||||
return string(decoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
case "willnorris/imageproxy":
|
||||
if strings.HasPrefix(url, proxyURL) {
|
||||
if slash := strings.IndexByte(url[len(proxyURL):], '/'); slash >= 0 {
|
||||
return url[len(proxyURL)+slash+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,3 +185,84 @@ func TestPostChannelMentions(t *testing.T) {
|
||||
},
|
||||
}, result.Props["channel_mentions"])
|
||||
}
|
||||
|
||||
func TestImageProxy(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
for name, tc := range map[string]struct {
|
||||
ProxyType string
|
||||
ProxyURL string
|
||||
ProxyOptions string
|
||||
ImageURL string
|
||||
ProxiedImageURL string
|
||||
}{
|
||||
"atmos/camo": {
|
||||
ProxyType: "atmos/camo",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "foo",
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/f8dace906d23689e8d5b12c3cefbedbf7b9b72f5/687474703a2f2f6d79646f6d61696e2e636f6d2f6d79696d616765",
|
||||
},
|
||||
"willnorris/imageproxy": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "x1000",
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/x1000/http://mydomain.com/myimage",
|
||||
},
|
||||
"willnorris/imageproxy_WithSigning": {
|
||||
ProxyType: "willnorris/imageproxy",
|
||||
ProxyURL: "https://127.0.0.1",
|
||||
ProxyOptions: "x1000|foo",
|
||||
ImageURL: "http://mydomain.com/myimage",
|
||||
ProxiedImageURL: "https://127.0.0.1/x1000,sbhHVoG5d60UvnNtGh6Iy6x4PaMmnsh8JfZ7JfErKjGU=/http://mydomain.com/myimage",
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.ServiceSettings.ImageProxyType = model.NewString(tc.ProxyType)
|
||||
cfg.ServiceSettings.ImageProxyOptions = model.NewString(tc.ProxyOptions)
|
||||
cfg.ServiceSettings.ImageProxyURL = model.NewString(tc.ProxyURL)
|
||||
})
|
||||
|
||||
post := &model.Post{
|
||||
Id: model.NewId(),
|
||||
Message: "",
|
||||
}
|
||||
|
||||
list := model.NewPostList()
|
||||
list.Posts[post.Id] = post
|
||||
|
||||
assert.Equal(t, "", th.App.PostListWithProxyAddedToImageURLs(list).Posts[post.Id].Message)
|
||||
assert.Equal(t, "", th.App.PostWithProxyAddedToImageURLs(post).Message)
|
||||
|
||||
assert.Equal(t, "", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
|
||||
post.Message = ""
|
||||
assert.Equal(t, "", th.App.PostWithProxyRemovedFromImageURLs(post).Message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var imageProxyBenchmarkSink *model.Post
|
||||
|
||||
func BenchmarkPostWithProxyRemovedFromImageURLs(b *testing.B) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.ServiceSettings.ImageProxyType = model.NewString("willnorris/imageproxy")
|
||||
cfg.ServiceSettings.ImageProxyOptions = model.NewString("x1000|foo")
|
||||
cfg.ServiceSettings.ImageProxyURL = model.NewString("https://127.0.0.1")
|
||||
})
|
||||
|
||||
post := &model.Post{
|
||||
Message: "",
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
imageProxyBenchmarkSink = th.App.PostWithProxyAddedToImageURLs(post)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,6 @@ func (a *App) sendReactionEvent(event string, reaction *model.Reaction, post *mo
|
||||
post.HasReactions = true
|
||||
post.UpdateAt = model.GetMillis()
|
||||
umessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, "", nil)
|
||||
umessage.Add("post", post.ToJson())
|
||||
umessage.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
a.Publish(umessage)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user