Skip to content

Commit

Permalink
Fixed When the MINIO_BROWSER_REDIRECT_URL parameter contains path, th…
Browse files Browse the repository at this point in the history
…e console cannot be accessed normally.
  • Loading branch information
polaris-phecda committed May 22, 2023
1 parent 629dd66 commit d41ffa1
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 58 deletions.
2 changes: 1 addition & 1 deletion portal-ui/src/api/consoleApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,7 @@ export enum ContentType {
}

export class HttpClient<SecurityDataType = unknown> {
public baseUrl: string = "/api/v1";
public baseUrl: string = `${new URL(document.baseURI).pathname}api/v1`;
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private abortControllers = new Map<CancelToken, AbortController>();
Expand Down
57 changes: 11 additions & 46 deletions restapi/configure_console.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ import (
"log"
"net"
"net/http"
"path"
"path/filepath"
"regexp"
"strings"
"sync"
"time"

"github.com/minio/console/pkg/logger"
Expand All @@ -42,7 +40,6 @@ import (
"github.com/klauspost/compress/gzhttp"

portal_ui "github.com/minio/console/portal-ui"
"github.com/minio/pkg/env"
"github.com/minio/pkg/mimedb"
xnet "github.com/minio/pkg/net"

Expand All @@ -60,15 +57,6 @@ var additionalServerFlags = struct {
CertsDir string `long:"certs-dir" description:"path to certs directory" env:"CONSOLE_CERTS_DIR"`
}{}

const (
SubPath = "CONSOLE_SUBPATH"
)

var (
subPath = "/"
subPathOnce sync.Once
)

func configureFlags(api *operations.ConsoleAPI) {
api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
{
Expand Down Expand Up @@ -318,17 +306,18 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
func FileServerMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", globalAppName) // do not add version information
basePath := operations.GetSubPath()
switch {
case strings.HasPrefix(r.URL.Path, "/ws"):
case strings.HasPrefix(r.URL.Path, basePath+"/ws"):
serveWS(w, r)
case strings.HasPrefix(r.URL.Path, "/api"):
case strings.HasPrefix(r.URL.Path, basePath+"/api"):
next.ServeHTTP(w, r)
default:
buildFs, err := fs.Sub(portal_ui.GetStaticAssets(), "build")
if err != nil {
panic(err)
}
wrapHandlerSinglePageApplication(requestBounce(http.FileServer(http.FS(buildFs)))).ServeHTTP(w, r)
wrapHandlerSinglePageApplication(requestBounce(http.StripPrefix(basePath, http.FileServer(http.FS(buildFs))))).ServeHTTP(w, r)
}
})
}
Expand All @@ -354,12 +343,12 @@ func (w *notFoundRedirectRespWr) Write(p []byte) (int, error) {

// handleSPA handles the serving of the React Single Page Application
func handleSPA(w http.ResponseWriter, r *http.Request) {
basePath := "/"
basePath := operations.GetSubPath()
// For SPA mode we will replace root base with a sub path if configured unless we received cp=y and cpb=/NEW/BASE
if v := r.URL.Query().Get("cp"); v == "y" {
if base := r.URL.Query().Get("cpb"); base != "" {
// make sure the subpath has a trailing slash
if !strings.HasSuffix(base, "/") {
if !strings.HasSuffix(base, basePath) {
base = fmt.Sprintf("%s/", base)
}
basePath = base
Expand Down Expand Up @@ -419,11 +408,11 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
}

// if we have a seeded basePath. This should override CONSOLE_SUBPATH every time, thus the `if else`
if basePath != "/" {
if basePath != operations.GetSubPath() {
indexPageBytes = replaceBaseInIndex(indexPageBytes, basePath)
// if we have a custom subpath replace it in
} else if getSubPath() != "/" {
indexPageBytes = replaceBaseInIndex(indexPageBytes, getSubPath())
} else if operations.GetSubPath() != "/" {
indexPageBytes = replaceBaseInIndex(indexPageBytes, operations.GetSubPath())
}
indexPageBytes = replaceLicense(indexPageBytes)

Expand All @@ -440,7 +429,7 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
// wrapHandlerSinglePageApplication handles a http.FileServer returning a 404 and overrides it with index.html
func wrapHandlerSinglePageApplication(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
if r.URL.Path == operations.GetSubPath()+"/" {
handleSPA(w, r)
return
}
Expand Down Expand Up @@ -469,38 +458,14 @@ func configureServer(s *http.Server, _, _ string) {
s.ErrorLog = log.New(&nullWriter{}, "", 0)
}

func getSubPath() string {
subPathOnce.Do(func() {
subPath = parseSubPath(env.Get(SubPath, ""))
})
return subPath
}

func parseSubPath(v string) string {
v = strings.TrimSpace(v)
if v == "" {
return SlashSeparator
}
// Replace all unnecessary `\` to `/`
// also add pro-actively at the end.
subPath = path.Clean(filepath.ToSlash(v))
if !strings.HasPrefix(subPath, SlashSeparator) {
subPath = SlashSeparator + subPath
}
if !strings.HasSuffix(subPath, SlashSeparator) {
subPath += SlashSeparator
}
return subPath
}

func replaceBaseInIndex(indexPageBytes []byte, basePath string) []byte {
if basePath != "" {
validBasePath := regexp.MustCompile(`^[0-9a-zA-Z\/-]+$`)
if !validBasePath.MatchString(basePath) {
return indexPageBytes
}
indexPageStr := string(indexPageBytes)
newBase := fmt.Sprintf("<base href=\"%s\"/>", basePath)
newBase := fmt.Sprintf("<base href=\"%s/\"/>", basePath)
indexPageStr = strings.Replace(indexPageStr, "<base href=\"/\"/>", newBase, 1)
indexPageBytes = []byte(indexPageStr)

Expand Down
27 changes: 25 additions & 2 deletions restapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions restapi/operations/console_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package restapi
package operations

import (
"github.com/stretchr/testify/assert"
"os"
"sync"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_parseSubPath(t *testing.T) {
Expand Down Expand Up @@ -59,14 +58,14 @@ func Test_parseSubPath(t *testing.T) {
args: args{
v: "route",
},
want: "/route/",
want: "/route",
},
{
name: "No trailing slashes",
args: args{
v: "/route",
},
want: "/route/",
want: "/route",
},
}
for _, tt := range tests {
Expand Down Expand Up @@ -104,22 +103,22 @@ func Test_getSubPath(t *testing.T) {
args: args{
envValue: "/subpath/",
},
want: "/subpath/",
want: "/subpath",
},
{
name: "No starting slash",
args: args{
envValue: "subpath/",
},
want: "/subpath/",
want: "/subpath",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(SubPath, tt.args.envValue)
defer os.Unsetenv(SubPath)
subPathOnce = sync.Once{}
assert.Equalf(t, tt.want, getSubPath(), "getSubPath()")
assert.Equalf(t, tt.want, GetSubPath(), "getSubPath()")
})
}
}
3 changes: 2 additions & 1 deletion restapi/ws_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"github.com/minio/console/restapi/operations"
"net"
"net/http"
"strconv"
Expand Down Expand Up @@ -139,7 +140,7 @@ func (c wsConn) readMessage() (messageType int, p []byte, err error) {
// Request should come like ws://<host>:<port>/ws/<api>
func serveWS(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
wsPath := strings.TrimPrefix(req.URL.Path, wsBasePath)
wsPath := strings.TrimPrefix(req.URL.Path, operations.GetSubPath()+wsBasePath)
// Perform authentication before upgrading to a Websocket Connection
// authenticate WS connection with Console
session, err := auth.GetClaimsFromTokenInRequest(req)
Expand Down

0 comments on commit d41ffa1

Please sign in to comment.