Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: replace HOST_DOMAIN with BASE_URL #1069

Merged
merged 4 commits into from
May 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions .example.env
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
HOST_DOMAIN=localhost
BASE_URL=http://localhost:3000
GO_ENV=development
DATABASE_URL=postgres://fider:fider_pw@localhost:5555/fider?sslmode=disable
JWT_SECRET=hsjl]W;&ZcHxT&FK;s%bgIQF:#ch=~#Al4:5]N;7V<qPZ3e9lT4'%;go;LIkc%k

CDN_HOST=dev.fider.io:3000

LOG_LEVEL=DEBUG
LOG_CONSOLE=true
LOG_SQL=true
Expand Down
1 change: 1 addition & 0 deletions .test.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ LOG_LEVEL=DEBUG
HOST_MODE=multi
DATABASE_URL=postgres://fider_test:fider_test_pw@localhost:5566/fider_test?sslmode=disable
HOST_DOMAIN=test.fider.io
BASE_URL=https://test.fider.io:3000
JWT_SECRET=SOME_RANDOM_TOKEN_JUST_FOR_TESTING

METRICS_ENABLED=true
Expand Down
5 changes: 0 additions & 5 deletions app/cmd/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ func RunPing() int {
return 1
}

// Set Host if HostDomain is set
if env.IsSingleHostMode() && len(env.Config.HostDomain) > 0 {
req.Host = env.Config.HostDomain
}

resp, err := client.Do(req)
if err != nil {
fmt.Printf("Request failed with: %s\n", err)
Expand Down
14 changes: 0 additions & 14 deletions app/middlewares/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"fmt"
"strings"

"github.com/getfider/fider/app/models/dto"
"github.com/getfider/fider/app/pkg/env"
"github.com/getfider/fider/app/pkg/log"
"github.com/getfider/fider/app/pkg/web"
)

Expand All @@ -16,18 +14,6 @@ import (
func Secure() web.MiddlewareFunc {
return func(next web.HandlerFunc) web.HandlerFunc {
return func(c *web.Context) error {

// Host attack is only a problem if Fider is running on single host mode
if env.IsSingleHostMode() {
if c.Request.URL.Hostname() != env.Config.HostDomain {
log.Errorf(c, "Requested hostname '@{URLHostname}' does not match environment HOST_DOMAIN '@{HostDomain}'.", dto.Props{
"URLHostname": c.Request.URL.Hostname(),
"HostDomain": env.Config.HostDomain,
})
return c.NotFound()
}
}

cdnHost := env.Config.CDN.Host
if cdnHost != "" && !env.IsSingleHostMode() {
cdnHost = "*." + cdnHost
Expand Down
14 changes: 0 additions & 14 deletions app/middlewares/security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,6 @@ import (
"github.com/getfider/fider/app/pkg/web"
)

func TestSecure_HostMismatch(t *testing.T) {
RegisterT(t)
env.Config.HostDomain = "yoursite.com"

server := mock.NewSingleTenantServer()
server.Use(middlewares.Secure())

status, _ := server.WithURL("http://someothersite.com").Execute(func(c *web.Context) error {
return c.String(http.StatusOK, c.Tenant().Name)
})

Expect(status).Equals(http.StatusNotFound)
}

func TestSecureWithoutCDN(t *testing.T) {
RegisterT(t)

Expand Down
17 changes: 16 additions & 1 deletion app/pkg/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package env

import (
"fmt"
"net/url"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -43,7 +44,8 @@ type config struct {
}
Port string `env:"PORT,default=3000"`
HostMode string `env:"HOST_MODE,default=single"`
HostDomain string `env:"HOST_DOMAIN,required"`
HostDomain string `env:"HOST_DOMAIN"`
BaseURL string `env:"BASE_URL"`
Locale string `env:"LOCALE,default=en"`
JWTSecret string `env:"JWT_SECRET,required"`
Paddle struct {
Expand Down Expand Up @@ -146,6 +148,19 @@ func Reload() {
panic(errors.Wrap(err, "failed to parse environment variables"))
}

if IsSingleHostMode() {
if Config.HostDomain != "" {
panic("HOST_DOMAIN environment variable has been replaced by BASE_URL. Set it to your site base url, e.g: https://feedback.mysite.com")
}

mustBeSet("BASE_URL")

_, err := url.Parse(Config.BaseURL)
if err != nil {
panic(errors.Wrap(err, "'%s' is not a valid URL", Config.BaseURL))
}
}

// Email Type can be inferred if absense
if Config.Email.Type == "" {
if Config.Email.Mailgun.APIKey != "" {
Expand Down
23 changes: 16 additions & 7 deletions app/pkg/web/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,11 @@ func (c *Context) SetCanonicalURL(rawurl string) {

//TenantBaseURL returns base URL for a given tenant
func TenantBaseURL(ctx context.Context, tenant *entity.Tenant) string {
request := ctx.Value(app.RequestCtxKey).(Request)

if env.IsSingleHostMode() {
return request.BaseURL()
return BaseURL(ctx)
}

request := ctx.Value(app.RequestCtxKey).(Request)
address := request.URL.Scheme + "://"
if tenant.CNAME != "" {
address += tenant.CNAME
Expand All @@ -553,15 +552,21 @@ func TenantBaseURL(ctx context.Context, tenant *entity.Tenant) string {
// AssetsURL return the full URL to a tenant-specific static asset
func AssetsURL(ctx context.Context, path string, a ...interface{}) string {
request := ctx.Value(app.RequestCtxKey).(Request)
tenant, hasTenant := ctx.Value(app.TenantCtxKey).(*entity.Tenant)
path = fmt.Sprintf(path, a...)
if env.Config.CDN.Host != "" && hasTenant {
if env.IsSingleHostMode() {

if env.IsSingleHostMode() {
if env.Config.CDN.Host != "" {
return request.URL.Scheme + "://" + env.Config.CDN.Host + path
}
return path
}

tenant, hasTenant := ctx.Value(app.TenantCtxKey).(*entity.Tenant)
if env.Config.CDN.Host != "" && hasTenant {
return request.URL.Scheme + "://" + tenant.Subdomain + "." + env.Config.CDN.Host + path
}
return request.BaseURL() + path

return BaseURL(ctx) + path
}

// LogoURL return the full URL to the tenant-specific logo URL
Expand All @@ -575,6 +580,10 @@ func LogoURL(ctx context.Context) string {

// BaseURL return the base URL from given context
func BaseURL(ctx context.Context) string {
if env.IsSingleHostMode() {
return env.Config.BaseURL
}

request, ok := ctx.Value(app.RequestCtxKey).(Request)
if ok {
return request.BaseURL()
Expand Down
10 changes: 5 additions & 5 deletions app/pkg/web/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func TestTenantURL_SingleHostMode(t *testing.T) {
ID: 1,
Subdomain: "theavengers",
}
Expect(web.TenantBaseURL(ctx, tenant)).Equals("http://demo.test.fider.io:3000")
Expect(web.TenantBaseURL(ctx, tenant)).Equals("https://test.fider.io:3000")
}

func TestAssetsURL_SingleHostMode(t *testing.T) {
Expand All @@ -128,8 +128,8 @@ func TestAssetsURL_SingleHostMode(t *testing.T) {
Subdomain: "theavengers",
})

Expect(web.AssetsURL(ctx, "/assets/main.js")).Equals("http://feedback.theavengers.com:3000/assets/main.js")
Expect(web.AssetsURL(ctx, "/assets/main.css")).Equals("http://feedback.theavengers.com:3000/assets/main.css")
Expect(web.AssetsURL(ctx, "/assets/main.js")).Equals("/assets/main.js")
Expect(web.AssetsURL(ctx, "/assets/main.css")).Equals("/assets/main.css")

env.Config.CDN.Host = "fidercdn.com"
Expect(web.AssetsURL(ctx, "/assets/main.js")).Equals("http://fidercdn.com/assets/main.js")
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestGetOAuthBaseURL(t *testing.T) {
Expect(web.OAuthBaseURL(ctx)).Equals("https://login.test.fider.io")

env.Config.HostMode = "single"
Expect(web.OAuthBaseURL(ctx)).Equals("https://mydomain.com")
Expect(web.OAuthBaseURL(ctx)).Equals("https://test.fider.io:3000")
}

func TestGetOAuthBaseURL_WithPort(t *testing.T) {
Expand All @@ -212,5 +212,5 @@ func TestGetOAuthBaseURL_WithPort(t *testing.T) {
Expect(web.OAuthBaseURL(ctx)).Equals("http://login.test.fider.io:3000")

env.Config.HostMode = "single"
Expect(web.OAuthBaseURL(ctx)).Equals("http://demo.test.fider.io:3000")
Expect(web.OAuthBaseURL(ctx)).Equals("https://test.fider.io:3000")
}
11 changes: 4 additions & 7 deletions app/pkg/web/ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,12 @@ var errInvalidHostName = errors.New("autotls: invalid hostname")
func isValidHostName(ctx context.Context, host string) error {
// In this context, host can only be custom domains, not a subdomain of fider.io

if host == "" {
return errors.Wrap(errInvalidHostName, "host cannot be empty.")
if env.IsSingleHostMode() {
return nil
}

if env.IsSingleHostMode() {
if env.Config.HostDomain == host {
return nil
}
return errors.Wrap(errInvalidHostName, "server name mismatch")
if host == "" {
return errors.Wrap(errInvalidHostName, "host cannot be empty.")
}

trx, err := dbx.BeginTx(ctx)
Expand Down