Skip to content

Commit 52925e9

Browse files
authored
Fix duplicate sub-path for avatars (#31365) (#31368)
Backport #31365, only backport necessary changes.
1 parent 188e515 commit 52925e9

File tree

6 files changed

+86
-16
lines changed

6 files changed

+86
-16
lines changed

models/repo/avatar_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package repo
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
"code.gitea.io/gitea/modules/setting"
11+
"code.gitea.io/gitea/modules/test"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRepoAvatarLink(t *testing.T) {
17+
defer test.MockVariableValue(&setting.AppURL, "https://localhost/")()
18+
defer test.MockVariableValue(&setting.AppSubURL, "")()
19+
20+
repo := &Repository{ID: 1, Avatar: "avatar.png"}
21+
link := repo.AvatarLink(db.DefaultContext)
22+
assert.Equal(t, "https://localhost/repo-avatars/avatar.png", link)
23+
24+
setting.AppURL = "https://localhost/sub-path/"
25+
setting.AppSubURL = "/sub-path"
26+
link = repo.AvatarLink(db.DefaultContext)
27+
assert.Equal(t, "https://localhost/sub-path/repo-avatars/avatar.png", link)
28+
}

models/user/avatar.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
8989
return avatars.GenerateEmailAvatarFastLink(ctx, u.AvatarEmail, size)
9090
}
9191

92-
// AvatarLink returns the full avatar url with http host. TODO: refactor it to a relative URL, but it is still used in API response at the moment
92+
// AvatarLink returns the full avatar url with http host.
93+
// TODO: refactor it to a relative URL, but it is still used in API response at the moment
9394
func (u *User) AvatarLink(ctx context.Context) string {
94-
return httplib.MakeAbsoluteURL(ctx, u.AvatarLinkWithSize(ctx, 0))
95+
relLink := u.AvatarLinkWithSize(ctx, 0) // it can't be empty
96+
return httplib.MakeAbsoluteURL(ctx, relLink)
9597
}
9698

9799
// IsUploadAvatarChanged returns true if the current user's avatar would be changed with the provided data

models/user/avatar_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package user
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/db"
10+
"code.gitea.io/gitea/modules/setting"
11+
"code.gitea.io/gitea/modules/test"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestUserAvatarLink(t *testing.T) {
17+
defer test.MockVariableValue(&setting.AppURL, "https://localhost/")()
18+
defer test.MockVariableValue(&setting.AppSubURL, "")()
19+
20+
u := &User{ID: 1, Avatar: "avatar.png"}
21+
link := u.AvatarLink(db.DefaultContext)
22+
assert.Equal(t, "https://localhost/avatars/avatar.png", link)
23+
24+
setting.AppURL = "https://localhost/sub-path/"
25+
setting.AppSubURL = "/sub-path"
26+
link = u.AvatarLink(db.DefaultContext)
27+
assert.Equal(t, "https://localhost/sub-path/avatars/avatar.png", link)
28+
}

modules/httplib/url.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,16 @@ func getForwardedHost(req *http.Request) string {
5757
return req.Header.Get("X-Forwarded-Host")
5858
}
5959

60-
// GuessCurrentAppURL tries to guess the current full URL by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
60+
// GuessCurrentAppURL tries to guess the current full app URL (with sub-path) by http headers. It always has a '/' suffix, exactly the same as setting.AppURL
6161
func GuessCurrentAppURL(ctx context.Context) string {
62+
return GuessCurrentHostURL(ctx) + setting.AppSubURL + "/"
63+
}
64+
65+
// GuessCurrentHostURL tries to guess the current full host URL (no sub-path) by http headers, there is no trailing slash.
66+
func GuessCurrentHostURL(ctx context.Context) string {
6267
req, ok := ctx.Value(RequestContextKey).(*http.Request)
6368
if !ok {
64-
return setting.AppURL
69+
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
6570
}
6671
// If no scheme provided by reverse proxy, then do not guess the AppURL, use the configured one.
6772
// At the moment, if site admin doesn't configure the proxy headers correctly, then Gitea would guess wrong.
@@ -74,20 +79,27 @@ func GuessCurrentAppURL(ctx context.Context) string {
7479
// So in the future maybe it should introduce a new config option, to let site admin decide how to guess the AppURL.
7580
reqScheme := getRequestScheme(req)
7681
if reqScheme == "" {
77-
return setting.AppURL
82+
return strings.TrimSuffix(setting.AppURL, setting.AppSubURL+"/")
7883
}
7984
reqHost := getForwardedHost(req)
8085
if reqHost == "" {
8186
reqHost = req.Host
8287
}
83-
return reqScheme + "://" + reqHost + setting.AppSubURL + "/"
88+
return reqScheme + "://" + reqHost
8489
}
8590

86-
func MakeAbsoluteURL(ctx context.Context, s string) string {
87-
if IsRelativeURL(s) {
88-
return GuessCurrentAppURL(ctx) + strings.TrimPrefix(s, "/")
91+
// MakeAbsoluteURL tries to make a link to an absolute URL:
92+
// * If link is empty, it returns the current app URL.
93+
// * If link is absolute, it returns the link.
94+
// * Otherwise, it returns the current host URL + link, the link itself should have correct sub-path (AppSubURL) if needed.
95+
func MakeAbsoluteURL(ctx context.Context, link string) string {
96+
if link == "" {
97+
return GuessCurrentAppURL(ctx)
98+
}
99+
if !IsRelativeURL(link) {
100+
return link
89101
}
90-
return s
102+
return GuessCurrentHostURL(ctx) + "/" + strings.TrimPrefix(link, "/")
91103
}
92104

93105
func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {

modules/httplib/url_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,22 @@ func TestMakeAbsoluteURL(t *testing.T) {
4646

4747
ctx := context.Background()
4848
assert.Equal(t, "http://cfg-host/sub/", MakeAbsoluteURL(ctx, ""))
49-
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "foo"))
50-
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
49+
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "foo"))
50+
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
5151
assert.Equal(t, "http://other/foo", MakeAbsoluteURL(ctx, "http://other/foo"))
5252

5353
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
5454
Host: "user-host",
5555
})
56-
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
56+
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
5757

5858
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
5959
Host: "user-host",
6060
Header: map[string][]string{
6161
"X-Forwarded-Host": {"forwarded-host"},
6262
},
6363
})
64-
assert.Equal(t, "http://cfg-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
64+
assert.Equal(t, "http://cfg-host/foo", MakeAbsoluteURL(ctx, "/foo"))
6565

6666
ctx = context.WithValue(ctx, RequestContextKey, &http.Request{
6767
Host: "user-host",
@@ -70,7 +70,7 @@ func TestMakeAbsoluteURL(t *testing.T) {
7070
"X-Forwarded-Proto": {"https"},
7171
},
7272
})
73-
assert.Equal(t, "https://forwarded-host/sub/foo", MakeAbsoluteURL(ctx, "/foo"))
73+
assert.Equal(t, "https://forwarded-host/foo", MakeAbsoluteURL(ctx, "/foo"))
7474
}
7575

7676
func TestIsCurrentGiteaSiteURL(t *testing.T) {

routers/api/packages/container/container.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
117117

118118
func apiUnauthorizedError(ctx *context.Context) {
119119
// container registry requires that the "/v2" must be in the root, so the sub-path in AppURL should be removed
120-
realmURL := strings.TrimSuffix(httplib.GuessCurrentAppURL(ctx), setting.AppSubURL+"/") + "/v2/token"
120+
realmURL := httplib.GuessCurrentHostURL(ctx) + "/v2/token"
121121
ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+realmURL+`",service="container_registry",scope="*"`)
122122
apiErrorDefined(ctx, errUnauthorized)
123123
}

0 commit comments

Comments
 (0)