1
Fork 0

Allow proxyURL to contain a path.

We now support or reasonable values for proxyURL, such as "http:"
or "/galene".
This commit is contained in:
Juliusz Chroboczek 2024-02-24 12:09:10 +01:00
parent 6756e7f7cc
commit 29e006037c
6 changed files with 66 additions and 47 deletions

2
README
View File

@ -78,7 +78,7 @@ The fields are as follows:
allowed. This is safe if the server is on the public Internet, but not allowed. This is safe if the server is on the public Internet, but not
necessarily so if it is on a private network. necessarily so if it is on a private network.
- `proxyURL`: if running behind a reverse proxy, this specifies the - `proxyURL`: if running behind a reverse proxy, this specifies the
address of the proxy. root URL that will be visible outside the proxy.
- `canonicalHost`: the canonical name of the host running the server; this - `canonicalHost`: the canonical name of the host running the server; this
will cause clients to be redirected if they use a different hostname to will cause clients to be redirected if they use a different hostname to
access the server. access the server.

View File

@ -917,7 +917,6 @@ func GetConfiguration() (*Configuration, error) {
return configuration.configuration, nil return configuration.configuration, nil
} }
// called locked // called locked
func (g *Group) getPasswordPermission(creds ClientCredentials) ([]string, error) { func (g *Group) getPasswordPermission(creds ClientCredentials) ([]string, error) {
desc := g.description desc := g.description
@ -1051,7 +1050,7 @@ type Status struct {
// Status returns a group's status. // Status returns a group's status.
// Base is the base URL for groups; if omitted, then both the Location and // Base is the base URL for groups; if omitted, then both the Location and
// Endpoint members are omitted from the result. // Endpoint members are omitted from the result.
func (g *Group) Status(authentified bool, base string) Status { func (g *Group) Status(authentified bool, base *url.URL) Status {
desc := g.Description() desc := g.Description()
if desc.Redirect != "" { if desc.Redirect != "" {
@ -1064,28 +1063,25 @@ func (g *Group) Status(authentified bool, base string) Status {
} }
var location, endpoint string var location, endpoint string
if base != "" { if base != nil {
burl, err := url.Parse(base) wss := "wss"
if err == nil { if base.Scheme == "http" {
wss := "wss" wss = "ws"
if burl.Scheme == "http" {
wss = "ws"
}
l := url.URL{
Scheme: burl.Scheme,
Host: burl.Host,
Path: path.Join(burl.Path, g.name) + "/",
}
location = l.String()
e := url.URL{
Scheme: wss,
Host: burl.Host,
Path: "/ws",
}
endpoint = e.String()
} else {
log.Printf("Couldn't parse base URL %v", base)
} }
l := url.URL{
Scheme: base.Scheme,
Host: base.Host,
Path: path.Join(
path.Join(base.Path, "/group/"),
g.Name()) + "/",
}
location = l.String()
e := url.URL{
Scheme: wss,
Host: base.Host,
Path: path.Join(base.Path, "/ws"),
}
endpoint = e.String()
} }
d := Status{ d := Status{
@ -1108,7 +1104,7 @@ func (g *Group) Status(authentified bool, base string) Status {
return d return d
} }
func GetPublic(base string) []Status { func GetPublic(base *url.URL) []Status {
gs := make([]Status, 0) gs := make([]Status, 0)
Range(func(g *Group) bool { Range(func(g *Group) bool {
if g.Description().Public { if g.Description().Public {

View File

@ -42,7 +42,7 @@ func TestGroup(t *testing.T) {
t.Errorf("Expected [], got %v", subs) t.Errorf("Expected [], got %v", subs)
} }
if public := GetPublic(""); len(public) != 1 || public[0].Name != "group/subgroup" { if public := GetPublic(nil); len(public) != 1 || public[0].Name != "group/subgroup" {
t.Errorf("Expected group/subgroup, got %v", public) t.Errorf("Expected group/subgroup, got %v", public)
} }
} }

View File

@ -1161,7 +1161,7 @@ func handleAction(c *webClient, a any) error {
if a.group != "" { if a.group != "" {
g = group.Get(a.group) g = group.Get(a.group)
if g != nil { if g != nil {
s := g.Status(true, "") s := g.Status(true, nil)
status = &s status = &s
data = g.Data() data = g.Data()
} }
@ -1208,7 +1208,7 @@ func handleAction(c *webClient, a any) error {
return errors.New("Permissions changed in no group") return errors.New("Permissions changed in no group")
} }
perms := append([]string(nil), c.permissions...) perms := append([]string(nil), c.permissions...)
status := g.Status(true, "") status := g.Status(true, nil)
username := c.username username := c.username
c.write(clientMessage{ c.write(clientMessage{
Type: "joined", Type: "joined",

View File

@ -361,29 +361,44 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
status := g.Status(false, "") status := g.Status(false, nil)
cspHeader(w, status.AuthServer) cspHeader(w, status.AuthServer)
serveFile(w, r, filepath.Join(StaticRoot, "galene.html")) serveFile(w, r, filepath.Join(StaticRoot, "galene.html"))
} }
func groupBase(r *http.Request) (string, error) { func baseURL(r *http.Request) (*url.URL, error) {
conf, err := group.GetConfiguration() conf, err := group.GetConfiguration()
if err != nil { if err != nil {
return "", err return nil, err
} }
var pu *url.URL
if conf.ProxyURL != "" { if conf.ProxyURL != "" {
return url.JoinPath(conf.ProxyURL, "/group/") pu, err = url.Parse(conf.ProxyURL)
if err != nil {
return nil, err
}
} }
scheme := "https" scheme := "https"
if r.TLS == nil { if r.TLS == nil {
scheme = "http" scheme = "http"
} }
host := r.Host
path := ""
if pu != nil {
if pu.Scheme != "" {
scheme = pu.Scheme
}
if pu.Host != "" {
host = pu.Host
}
path = pu.Path
}
base := url.URL{ base := url.URL{
Scheme: scheme, Scheme: scheme,
Host: r.Host, Host: host,
Path: "/group/", Path: path,
} }
return base.String(), nil return &base, nil
} }
func groupStatusHandler(w http.ResponseWriter, r *http.Request) { func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
@ -404,9 +419,11 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
base, err := groupBase(r) base, err := baseURL(r)
if err != nil { if err != nil {
httpError(w, err) log.Printf("Parse ProxyURL: %v", err)
http.Error(w, "Internal server error",
http.StatusInternalServerError)
return return
} }
d := g.Status(false, base) d := g.Status(false, base)
@ -422,7 +439,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
} }
func publicHandler(w http.ResponseWriter, r *http.Request) { func publicHandler(w http.ResponseWriter, r *http.Request) {
base, err := groupBase(r) base, err := baseURL(r)
if err != nil { if err != nil {
log.Printf("couldn't determine group base: %v", err) log.Printf("couldn't determine group base: %v", err)
httpError(w, err) httpError(w, err)

View File

@ -42,12 +42,18 @@ func TestBase(t *testing.T) {
t bool t bool
h, res string h, res string
}{ }{
{"", true, "a.org", "https://a.org/group/"}, {"", true, "a.org", "https://a.org"},
{"", false, "a.org", "http://a.org/group/"}, {"", false, "a.org", "http://a.org"},
{"https://b.org", true, "a.org", "https://b.org/group/"}, {"/base", true, "a.org", "https://a.org/base"},
{"https://b.org", false, "a.org", "https://b.org/group/"}, {"/base", false, "a.org", "http://a.org/base"},
{"http://b.org", true, "a.org", "http://b.org/group/"}, {"http:", true, "a.org", "http://a.org"},
{"http://b.org", false, "a.org", "http://b.org/group/"}, {"https:", false, "a.org", "https://a.org"},
{"http:/base", true, "a.org", "http://a.org/base"},
{"https:/base", false, "a.org", "https://a.org/base"},
{"https://b.org", true, "a.org", "https://b.org"},
{"https://b.org", false, "a.org", "https://b.org"},
{"http://b.org", true, "a.org", "http://b.org"},
{"http://b.org", false, "a.org", "http://b.org"},
} }
dir := t.TempDir() dir := t.TempDir()
@ -75,13 +81,13 @@ func TestBase(t *testing.T) {
if v.t { if v.t {
tcs = &tls.ConnectionState{} tcs = &tls.ConnectionState{}
} }
base, err := groupBase(&http.Request{ base, err := baseURL(&http.Request{
TLS: tcs, TLS: tcs,
Host: v.h, Host: v.h,
}) })
if err != nil || base != v.res { if err != nil || base.String() != v.res {
t.Errorf("Expected %v, got %v (%v)", t.Errorf("Expected %v, got %v (%v)",
v.res, base, err, v.res, base.String(), err,
) )
} }
} }