diff --git a/CHANGELOG.md b/CHANGELOG.md index d397b8a3..9c69a038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [0.25.1] - 2024-10-02 + +- Adds support for normalizing the connection URI's before returning them in dashboard GET response. + ## [0.25.0] - 2024-09-25 ### Changes diff --git a/recipe/dashboard/api/implementation.go b/recipe/dashboard/api/implementation.go index 322a565f..b6c0ea1e 100644 --- a/recipe/dashboard/api/implementation.go +++ b/recipe/dashboard/api/implementation.go @@ -17,6 +17,7 @@ package api import ( "strconv" + "strings" "github.com/supertokens/supertokens-golang/recipe/dashboard/constants" "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" @@ -45,7 +46,22 @@ func MakeAPIImplementation() dashboardmodels.APIInterface { if err != nil { return "", err } - connectionURI := stInstance.SuperTokens.ConnectionURI + + // We are splitting the passed URI here so that if multiple URI's are passed + // separated by a colon, the first one is returned. + connectionURIToNormalize := strings.Split(stInstance.SuperTokens.ConnectionURI, ";")[0] + + // This normalizes the URI to make sure that it has things like protocol etc + // injected into it before it is returned. + var normalizationError error + normalizedConnectionURI, normalizationError := supertokens.NewNormalisedURLDomain(connectionURIToNormalize) + if normalizationError != nil { + // In case of failures, we want to return a 500 here, mainly because that + // is what we return if the connectionURI is invalid which is the case here + // if normalization fails. + return "", normalizationError + } + connectionURI := normalizedConnectionURI.GetAsStringDangerous() normalizedDashboardPath, err := supertokens.NewNormalisedURLPath(constants.DashboardAPI) if err != nil { diff --git a/recipe/dashboard/dashboardGet_test.go b/recipe/dashboard/dashboardGet_test.go new file mode 100644 index 00000000..ef315ffa --- /dev/null +++ b/recipe/dashboard/dashboardGet_test.go @@ -0,0 +1,162 @@ +package dashboard + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/supertokens/supertokens-golang/recipe/emailpassword" + + "github.com/stretchr/testify/assert" + "github.com/supertokens/supertokens-golang/recipe/dashboard/dashboardmodels" + "github.com/supertokens/supertokens-golang/supertokens" + "github.com/supertokens/supertokens-golang/test/unittesting" +) + +func TestThatDashboardGetNormalizesConnectionURIWithoutHTTP(t *testing.T) { + connectionURI := "http://localhost:8080" + connectionURIWithoutProtocol := strings.Replace(connectionURI, "http://", "", -1) + config := supertokens.TypeInput{ + OnSuperTokensAPIError: func(err error, req *http.Request, res http.ResponseWriter) { + print(err) + }, + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: connectionURIWithoutProtocol, + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + emailpassword.Init(nil), + Init(&dashboardmodels.TypeInput{ + ApiKey: "testapikey", + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(config) + if err != nil { + t.Error(err.Error()) + } + + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/dashboard", strings.NewReader(`{}`)) + req.Header.Set("Authorization", "Bearer testapikey") + res, err := http.DefaultClient.Do(req) + assert.Equal(t, res.StatusCode, 200) + + if err != nil { + t.Error(err.Error()) + } + + body, _ := io.ReadAll(res.Body) + assert.True(t, strings.Contains(string(body), fmt.Sprintf("window.connectionURI = \"%s\"", connectionURI))) +} + +func TestThatDashboardGetNormalizesConnectionURIWithoutHTTPS(t *testing.T) { + connectionURI := "https://try.supertokens.com" + connectionURIWithoutProtocol := strings.Replace(connectionURI, "https://", "", -1) + config := supertokens.TypeInput{ + OnSuperTokensAPIError: func(err error, req *http.Request, res http.ResponseWriter) { + print(err) + }, + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: connectionURIWithoutProtocol, + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + emailpassword.Init(nil), + Init(&dashboardmodels.TypeInput{ + ApiKey: "testapikey", + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(config) + if err != nil { + t.Error(err.Error()) + } + + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/dashboard", strings.NewReader(`{}`)) + req.Header.Set("Authorization", "Bearer testapikey") + res, err := http.DefaultClient.Do(req) + assert.Equal(t, res.StatusCode, 200) + + if err != nil { + t.Error(err.Error()) + } + + body, _ := io.ReadAll(res.Body) + assert.True(t, strings.Contains(string(body), fmt.Sprintf("window.connectionURI = \"%s\"", connectionURI))) +} + +func TestThatDashboardGetReturnsFirstURIWhenMultipleArePassed(t *testing.T) { + firstConnectionURI := "http://localhost:8080" + secondConnectionURI := "https://try.supertokens.com" + multiplConnectionURIs := fmt.Sprintf("%s;%s", firstConnectionURI, secondConnectionURI) + config := supertokens.TypeInput{ + OnSuperTokensAPIError: func(err error, req *http.Request, res http.ResponseWriter) { + print(err) + }, + Supertokens: &supertokens.ConnectionInfo{ + ConnectionURI: multiplConnectionURIs, + }, + AppInfo: supertokens.AppInfo{ + APIDomain: "api.supertokens.io", + AppName: "SuperTokens", + WebsiteDomain: "supertokens.io", + }, + RecipeList: []supertokens.Recipe{ + emailpassword.Init(nil), + Init(&dashboardmodels.TypeInput{ + ApiKey: "testapikey", + }), + }, + } + + BeforeEach() + unittesting.StartUpST("localhost", "8080") + defer AfterEach() + err := supertokens.Init(config) + if err != nil { + t.Error(err.Error()) + } + + mux := http.NewServeMux() + testServer := httptest.NewServer(supertokens.Middleware(mux)) + defer testServer.Close() + + req, err := http.NewRequest(http.MethodGet, testServer.URL+"/auth/dashboard", strings.NewReader(`{}`)) + req.Header.Set("Authorization", "Bearer testapikey") + res, err := http.DefaultClient.Do(req) + assert.Equal(t, res.StatusCode, 200) + + if err != nil { + t.Error(err.Error()) + } + + body, _ := io.ReadAll(res.Body) + assert.True(t, strings.Contains(string(body), fmt.Sprintf("window.connectionURI = \"%s\"", firstConnectionURI))) +} diff --git a/supertokens/constants.go b/supertokens/constants.go index 40e7b7a7..e17ec4e1 100644 --- a/supertokens/constants.go +++ b/supertokens/constants.go @@ -21,7 +21,7 @@ const ( ) // VERSION current version of the lib -const VERSION = "0.25.0" +const VERSION = "0.25.1" var ( cdiSupported = []string{"3.1"}