fix: force file urls to https
This commit is contained in:
+24
-1
@@ -17,6 +17,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime"
|
"mime"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -369,6 +370,7 @@ func largeFileReceiveHTTP(wrt http.ResponseWriter, req *http.Request) {
|
|||||||
logs.Warn.Printf("media upload: failed to enqueue file lifecycle event file=%s: %v", fdef.Id, err)
|
logs.Warn.Printf("media upload: failed to enqueue file lifecycle event file=%s: %v", fdef.Id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url = normalizeUploadURL(url)
|
||||||
params := map[string]any{"url": url, "file_id": fdef.Uid().String()}
|
params := map[string]any{"url": url, "file_id": fdef.Uid().String()}
|
||||||
if globals.mediaGcPeriod > 0 {
|
if globals.mediaGcPeriod > 0 {
|
||||||
// How long this file is guaranteed to exist without being attached to a message or a topic.
|
// How long this file is guaranteed to exist without being attached to a message or a topic.
|
||||||
@@ -700,7 +702,7 @@ func (*grpcNodeServer) LargeFileReceive(stream pbx.Node_LargeFileReceiveServer)
|
|||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
Text: http.StatusText(http.StatusOK),
|
Text: http.StatusText(http.StatusOK),
|
||||||
Meta: &pbx.FileMeta{
|
Meta: &pbx.FileMeta{
|
||||||
Name: url,
|
Name: normalizeUploadURL(url),
|
||||||
MimeType: mimeType,
|
MimeType: mimeType,
|
||||||
Etag: fdef.ETag,
|
Etag: fdef.ETag,
|
||||||
Size: size,
|
Size: size,
|
||||||
@@ -719,6 +721,27 @@ func (*grpcNodeServer) LargeFileReceive(stream pbx.Node_LargeFileReceiveServer)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeUploadURL(rawURL string) string {
|
||||||
|
parsed, err := url.Parse(strings.TrimSpace(rawURL))
|
||||||
|
if err != nil || parsed.Scheme != "http" || parsed.Host == "" || isLocalUploadHost(parsed.Hostname()) {
|
||||||
|
return rawURL
|
||||||
|
}
|
||||||
|
parsed.Scheme = "https"
|
||||||
|
return parsed.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLocalUploadHost(host string) bool {
|
||||||
|
host = strings.ToLower(strings.TrimSpace(host))
|
||||||
|
if host == "" || host == "localhost" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast()
|
||||||
|
}
|
||||||
|
|
||||||
// largeFileRunGarbageCollection runs every 'period' and deletes up to 'blockSize' unused files.
|
// largeFileRunGarbageCollection runs every 'period' and deletes up to 'blockSize' unused files.
|
||||||
// Returns channel which can be used to stop the process.
|
// Returns channel which can be used to stop the process.
|
||||||
func largeFileRunGarbageCollection(period time.Duration, blockSize int) chan<- bool {
|
func largeFileRunGarbageCollection(period time.Duration, blockSize int) chan<- bool {
|
||||||
|
|||||||
@@ -74,6 +74,51 @@ func TestAuthFileRequestFromSessionRequiresTenant(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNormalizeUploadURL(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
in string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "public http becomes https",
|
||||||
|
in: "http://api.118im.com/v0/file/s/abc.jpg",
|
||||||
|
want: "https://api.118im.com/v0/file/s/abc.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "query preserved",
|
||||||
|
in: "http://cdn.example.com/a/b.png?x=1",
|
||||||
|
want: "https://cdn.example.com/a/b.png?x=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative unchanged",
|
||||||
|
in: "/v0/file/s/abc.jpg",
|
||||||
|
want: "/v0/file/s/abc.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "https unchanged",
|
||||||
|
in: "https://api.118im.com/v0/file/s/abc.jpg",
|
||||||
|
want: "https://api.118im.com/v0/file/s/abc.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "localhost unchanged",
|
||||||
|
in: "http://localhost:6060/v0/file/s/abc.jpg",
|
||||||
|
want: "http://localhost:6060/v0/file/s/abc.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "private ip unchanged",
|
||||||
|
in: "http://10.42.0.1/v0/file/s/abc.jpg",
|
||||||
|
want: "http://10.42.0.1/v0/file/s/abc.jpg",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if got := normalizeUploadURL(tc.in); got != tc.want {
|
||||||
|
t.Fatalf("normalizeUploadURL(%q)=%q, want %q", tc.in, got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthFileRequestFromTokenReturnsPrincipal(t *testing.T) {
|
func TestAuthFileRequestFromTokenReturnsPrincipal(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|||||||
+10
-1
@@ -254,7 +254,7 @@ func (oh *ossHandler) Headers(tenantID types.TenantID, method string, url *url.U
|
|||||||
|
|
||||||
if redirURL != "" {
|
if redirURL != "" {
|
||||||
return http.Header{
|
return http.Header{
|
||||||
"Location": {redirURL},
|
"Location": {normalizeSignedRedirectURL(redirURL)},
|
||||||
"ETag": {`"` + fdef.ETag + `"`},
|
"ETag": {`"` + fdef.ETag + `"`},
|
||||||
"Content-Type": {"application/json; charset=utf-8"},
|
"Content-Type": {"application/json; charset=utf-8"},
|
||||||
"Cache-Control": {oh.conf.CacheControl},
|
"Cache-Control": {oh.conf.CacheControl},
|
||||||
@@ -263,6 +263,15 @@ func (oh *ossHandler) Headers(tenantID types.TenantID, method string, url *url.U
|
|||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeSignedRedirectURL(rawURL string) string {
|
||||||
|
parsed, err := url.Parse(strings.TrimSpace(rawURL))
|
||||||
|
if err != nil || parsed.Scheme != "http" || parsed.Host == "" {
|
||||||
|
return rawURL
|
||||||
|
}
|
||||||
|
parsed.Scheme = "https"
|
||||||
|
return parsed.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Upload processes request for a file upload.
|
// Upload processes request for a file upload.
|
||||||
func (oh *ossHandler) Upload(fdef *types.FileDef, file io.Reader) (string, int64, error) {
|
func (oh *ossHandler) Upload(fdef *types.FileDef, file io.Reader) (string, int64, error) {
|
||||||
key, err := tenantObjectKey(fdef.TenantID, fdef.Uid().String32(), fdef.CreatedAt)
|
key, err := tenantObjectKey(fdef.TenantID, fdef.Uid().String32(), fdef.CreatedAt)
|
||||||
|
|||||||
@@ -58,6 +58,17 @@ func TestIsOSSObjectNotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNormalizeSignedRedirectURL(t *testing.T) {
|
||||||
|
rawURL := "http://im-oss-t1.oss-cn-shenzhen.aliyuncs.com/tenants/1/avatar.jpg?Expires=1&OSSAccessKeyId=ak&Signature=sig"
|
||||||
|
want := "https://im-oss-t1.oss-cn-shenzhen.aliyuncs.com/tenants/1/avatar.jpg?Expires=1&OSSAccessKeyId=ak&Signature=sig"
|
||||||
|
if got := normalizeSignedRedirectURL(rawURL); got != want {
|
||||||
|
t.Fatalf("normalizeSignedRedirectURL()=%q, want %q", got, want)
|
||||||
|
}
|
||||||
|
if got := normalizeSignedRedirectURL("/v0/file/s/avatar.jpg"); got != "/v0/file/s/avatar.jpg" {
|
||||||
|
t.Fatalf("relative URL must be unchanged, got %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadSysConfig(t *testing.T) {
|
func TestLoadSysConfig(t *testing.T) {
|
||||||
original := store.ThirdPartyConfig
|
original := store.ThirdPartyConfig
|
||||||
store.ThirdPartyConfig = fakeThirdPartyConfig{
|
store.ThirdPartyConfig = fakeThirdPartyConfig{
|
||||||
|
|||||||
Reference in New Issue
Block a user