2020-04-02 12:51:51 -07:00
// This file is part of MinIO Console Server
2021-01-19 17:04:13 -06:00
// Copyright (c) 2021 MinIO, Inc.
2020-04-01 18:18:57 -07:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// 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/>.
// This file is safe to edit. Once it exists it will not be overwritten
2023-12-26 15:07:30 -06:00
package api
2020-04-01 18:18:57 -07:00
import (
2020-10-21 13:13:40 -07:00
"bytes"
2022-04-28 12:55:06 -07:00
"context"
2020-04-01 18:18:57 -07:00
"crypto/tls"
2021-11-22 21:02:16 -08:00
"fmt"
2021-03-05 10:39:17 -08:00
"io"
"io/fs"
2021-06-15 17:52:39 -07:00
"log"
2021-09-13 20:09:19 -07:00
"net"
2020-04-01 18:18:57 -07:00
"net/http"
2022-04-25 13:23:40 -07:00
"path"
2021-09-13 20:09:19 -07:00
"path/filepath"
2022-03-07 17:30:36 -08:00
"regexp"
2020-04-01 18:18:57 -07:00
"strings"
2021-11-22 21:02:16 -08:00
"sync"
2020-10-21 13:13:40 -07:00
"time"
2020-04-01 18:18:57 -07:00
2023-12-11 09:57:02 -08:00
"github.com/google/uuid"
2022-04-28 12:55:06 -07:00
"github.com/minio/console/pkg/logger"
"github.com/minio/console/pkg/utils"
2022-07-07 14:28:25 -05:00
"github.com/minio/minio-go/v7/pkg/credentials"
2022-04-28 12:55:06 -07:00
2021-10-26 18:20:26 -07:00
"github.com/klauspost/compress/gzhttp"
2023-12-29 11:44:01 -06:00
portal_ui "github.com/minio/console/web-app"
2023-09-01 16:29:07 -07:00
"github.com/minio/pkg/v2/env"
"github.com/minio/pkg/v2/mimedb"
xnet "github.com/minio/pkg/v2/net"
2020-04-01 18:18:57 -07:00
"github.com/go-openapi/errors"
2020-10-29 22:26:48 -07:00
"github.com/go-openapi/swag"
2023-12-26 15:07:30 -06:00
"github.com/minio/console/api/operations"
2020-10-29 22:26:48 -07:00
"github.com/minio/console/models"
"github.com/minio/console/pkg/auth"
2020-04-06 13:24:15 -07:00
"github.com/unrolled/secure"
2020-04-01 18:18:57 -07:00
)
2020-07-26 00:34:17 -07:00
//go:generate swagger generate server --target ../../console --name Console --spec ../swagger.yml
2020-04-01 18:18:57 -07:00
2020-10-29 22:26:48 -07:00
var additionalServerFlags = struct {
CertsDir string ` long:"certs-dir" description:"path to certs directory" env:"CONSOLE_CERTS_DIR" `
} { }
2022-04-25 13:23:40 -07:00
const (
SubPath = "CONSOLE_SUBPATH"
)
2022-05-05 13:44:10 -07:00
var (
2023-06-14 12:36:48 -07:00
cfgSubPath = "/"
2022-05-05 13:44:10 -07:00
subPathOnce sync . Once
)
2021-11-22 21:02:16 -08:00
2020-07-26 00:34:17 -07:00
func configureFlags ( api * operations . ConsoleAPI ) {
2020-10-29 22:26:48 -07:00
api . CommandLineOptionsGroups = [ ] swag . CommandLineOptionsGroup {
{
ShortDescription : "additional server flags" ,
Options : & additionalServerFlags ,
} ,
}
2020-04-01 18:18:57 -07:00
}
2020-07-26 00:34:17 -07:00
func configureAPI ( api * operations . ConsoleAPI ) http . Handler {
2020-04-01 18:18:57 -07:00
// Applies when the "x-token" header is set
2020-04-02 12:06:52 -07:00
api . KeyAuth = func ( token string , scopes [ ] string ) ( * models . Principal , error ) {
2020-10-19 15:32:21 -07:00
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
2020-07-10 19:14:28 -07:00
// was generated and signed by us in the first place
2023-01-27 12:23:30 -08:00
if token == "Anonymous" {
return & models . Principal { } , nil
}
2022-04-28 12:55:06 -07:00
claims , err := auth . ParseClaimsFromToken ( token )
2020-07-10 19:14:28 -07:00
if err != nil {
2021-06-04 11:35:55 -07:00
api . Logger ( "Unable to validate the session token %s: %v" , token , err )
2020-07-10 19:14:28 -07:00
return nil , errors . New ( 401 , "incorrect api key auth" )
2020-04-01 18:18:57 -07:00
}
2020-07-10 19:14:28 -07:00
return & models . Principal {
2020-12-07 17:11:08 -06:00
STSAccessKeyID : claims . STSAccessKeyID ,
STSSecretAccessKey : claims . STSSecretAccessKey ,
STSSessionToken : claims . STSSessionToken ,
AccountAccessKey : claims . AccountAccessKey ,
2022-02-21 21:42:18 -08:00
Hm : claims . HideMenu ,
2022-07-07 14:28:25 -05:00
Ob : claims . ObjectBrowser ,
2022-08-17 11:06:10 -05:00
CustomStyleOb : claims . CustomStyleOB ,
2020-07-10 19:14:28 -07:00
} , nil
2020-04-01 18:18:57 -07:00
}
2023-01-27 12:23:30 -08:00
api . AnonymousAuth = func ( s string ) ( * models . Principal , error ) {
return & models . Principal { } , nil
}
2020-04-01 18:18:57 -07:00
// Register login handlers
registerLoginHandlers ( api )
2020-04-08 12:36:14 -07:00
// Register logout handlers
registerLogoutHandlers ( api )
2020-04-01 18:18:57 -07:00
// Register bucket handlers
registerBucketsHandlers ( api )
// Register all users handlers
registerUsersHandlers ( api )
// Register groups handlers
registerGroupsHandlers ( api )
// Register policies handlers
registersPoliciesHandler ( api )
// Register configurations handlers
registerConfigHandlers ( api )
// Register bucket events handlers
registerBucketEventsHandlers ( api )
2021-04-24 16:31:47 -05:00
// Register bucket lifecycle handlers
registerBucketsLifecycleHandlers ( api )
2020-04-01 18:18:57 -07:00
// Register service handlers
registerServiceHandlers ( api )
2020-04-02 15:54:34 -07:00
// Register session handlers
registerSessionHandlers ( api )
2020-04-02 20:15:39 -07:00
// Register admin info handlers
registerAdminInfoHandlers ( api )
2020-04-03 14:27:47 -07:00
// Register admin arns handlers
registerAdminArnsHandlers ( api )
2020-04-09 16:07:26 -07:00
// Register admin notification endpoints handlers
registerAdminNotificationEndpointsHandlers ( api )
2020-04-29 18:28:28 -07:00
// Register admin Service Account Handlers
registerServiceAccountsHandlers ( api )
2020-09-28 12:46:08 -05:00
// Register admin remote buckets
registerAdminBucketRemoteHandlers ( api )
2021-01-13 14:08:32 -06:00
// Register admin log search
registerLogSearchHandlers ( api )
2022-01-23 23:42:00 -06:00
// Register admin subnet handlers
registerSubnetHandlers ( api )
2022-09-20 18:09:30 -05:00
// Register admin KMS handlers
registerKMSHandlers ( api )
2022-12-06 14:33:17 -06:00
// Register admin IDP handlers
registerIDPHandlers ( api )
2021-04-24 16:31:47 -05:00
// Register Account handlers
registerAdminTiersHandlers ( api )
2022-05-05 13:44:10 -07:00
// Register Inspect Handler
2022-02-16 00:14:51 +00:00
registerInspectHandler ( api )
2022-04-04 11:54:03 -07:00
// Register nodes handlers
registerNodesHandler ( api )
2020-04-01 18:18:57 -07:00
2022-03-31 17:11:01 +00:00
registerSiteReplicationHandler ( api )
2022-04-14 07:21:43 +00:00
registerSiteReplicationStatusHandler ( api )
2023-02-13 17:04:35 -06:00
// Register Support Handler
registerSupportHandlers ( api )
2022-03-31 17:11:01 +00:00
2020-07-08 13:55:08 -07:00
// Operator Console
2021-07-19 11:48:50 -07:00
2020-09-29 14:34:51 -07:00
// Register Object's Handlers
registerObjectsHandlers ( api )
2020-10-01 18:59:20 -07:00
// Register Bucket Quota's Handlers
registerBucketQuotaHandlers ( api )
2020-12-07 17:11:08 -06:00
// Register Account handlers
registerAccountHandlers ( api )
2020-07-01 18:03:22 -07:00
2023-02-09 14:49:07 -06:00
registerReleasesHandlers ( api )
2020-04-01 18:18:57 -07:00
api . PreServerShutdown = func ( ) { }
api . ServerShutdown = func ( ) { }
2023-01-25 13:18:41 -08:00
// do an initial subnet plan caching
fetchLicensePlan ( )
2020-04-01 18:18:57 -07:00
return setupGlobalMiddleware ( api . Serve ( setupMiddlewares ) )
}
// The TLS configuration before HTTPS server starts.
func configureTLS ( tlsConfig * tls . Config ) {
2021-06-07 16:33:47 -07:00
tlsConfig . RootCAs = GlobalRootCAs
2021-06-15 17:52:39 -07:00
tlsConfig . GetCertificate = GlobalTLSCertsManager . GetCertificate
2020-04-01 18:18:57 -07:00
}
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares ( handler http . Handler ) http . Handler {
return handler
}
2022-04-28 12:55:06 -07:00
func ContextMiddleware ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2023-12-11 09:57:02 -08:00
requestID := uuid . NewString ( )
2022-04-28 12:55:06 -07:00
ctx := context . WithValue ( r . Context ( ) , utils . ContextRequestID , requestID )
ctx = context . WithValue ( ctx , utils . ContextRequestUserAgent , r . UserAgent ( ) )
ctx = context . WithValue ( ctx , utils . ContextRequestHost , r . Host )
ctx = context . WithValue ( ctx , utils . ContextRequestRemoteAddr , r . RemoteAddr )
2023-06-14 12:36:48 -07:00
ctx = context . WithValue ( ctx , utils . ContextClientIP , getClientIP ( r ) )
2022-04-28 12:55:06 -07:00
next . ServeHTTP ( w , r . WithContext ( ctx ) )
} )
}
func AuditLogMiddleware ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
rw := logger . NewResponseWriter ( w )
next . ServeHTTP ( rw , r )
if strings . HasPrefix ( r . URL . Path , "/ws" ) || strings . HasPrefix ( r . URL . Path , "/api" ) {
logger . AuditLog ( r . Context ( ) , rw , r , map [ string ] interface { } { } , "Authorization" , "Cookie" , "Set-Cookie" )
}
} )
}
2020-04-01 18:18:57 -07:00
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
2022-04-28 12:55:06 -07:00
// So this is a good place to plug in a panic handling middleware, logger and metrics
2020-04-01 18:18:57 -07:00
func setupGlobalMiddleware ( handler http . Handler ) http . Handler {
2022-04-28 12:55:06 -07:00
gnext := gzhttp . GzipHandler ( handler )
// if audit-log is enabled console will log all incoming request
next := AuditLogMiddleware ( gnext )
2020-04-01 18:18:57 -07:00
// serve static files
2020-11-13 16:26:03 -08:00
next = FileServerMiddleware ( next )
2022-04-28 12:55:06 -07:00
// add information to request context
next = ContextMiddleware ( next )
// handle cookie or authorization header for session
next = AuthenticationMiddleware ( next )
2021-09-13 20:09:19 -07:00
sslHostFn := secure . SSLHostFunc ( func ( host string ) string {
2022-05-09 11:24:43 -07:00
xhost , err := xnet . ParseHost ( host )
2021-09-13 20:09:19 -07:00
if err != nil {
return host
}
2022-05-09 11:24:43 -07:00
return net . JoinHostPort ( xhost . Name , TLSPort )
2021-09-13 20:09:19 -07:00
} )
2020-04-06 13:24:15 -07:00
// Secure middleware, this middleware wrap all the previous handlers and add
// HTTP security headers
secureOptions := secure . Options {
2021-07-19 11:48:50 -07:00
AllowedHosts : GetSecureAllowedHosts ( ) ,
AllowedHostsAreRegex : GetSecureAllowedHostsAreRegex ( ) ,
HostsProxyHeaders : GetSecureHostsProxyHeaders ( ) ,
2021-01-26 22:08:21 -06:00
SSLRedirect : GetTLSRedirect ( ) == "on" && len ( GlobalPublicCerts ) > 0 ,
2021-09-13 20:09:19 -07:00
SSLHostFunc : & sslHostFn ,
2021-07-19 11:48:50 -07:00
SSLHost : GetSecureTLSHost ( ) ,
STSSeconds : GetSecureSTSSeconds ( ) ,
STSIncludeSubdomains : GetSecureSTSIncludeSubdomains ( ) ,
STSPreload : GetSecureSTSPreload ( ) ,
2021-09-13 20:09:19 -07:00
SSLTemporaryRedirect : false ,
2021-07-19 11:48:50 -07:00
ForceSTSHeader : GetSecureForceSTSHeader ( ) ,
FrameDeny : GetSecureFrameDeny ( ) ,
ContentTypeNosniff : GetSecureContentTypeNonSniff ( ) ,
BrowserXssFilter : GetSecureBrowserXSSFilter ( ) ,
ContentSecurityPolicy : GetSecureContentSecurityPolicy ( ) ,
ContentSecurityPolicyReportOnly : GetSecureContentSecurityPolicyReportOnly ( ) ,
PublicKey : GetSecurePublicKey ( ) ,
ReferrerPolicy : GetSecureReferrerPolicy ( ) ,
FeaturePolicy : GetSecureFeaturePolicy ( ) ,
ExpectCTHeader : GetSecureExpectCTHeader ( ) ,
2021-06-18 15:40:25 -07:00
IsDevelopment : false ,
2020-04-06 13:24:15 -07:00
}
secureMiddleware := secure . New ( secureOptions )
2021-10-26 18:20:26 -07:00
next = secureMiddleware . Handler ( next )
2022-04-28 12:55:06 -07:00
return RejectS3Middleware ( next )
2021-10-20 02:18:16 -07:00
}
2022-08-08 13:37:01 -07:00
const apiRequestErr = ` <?xml version="1.0" encoding="UTF-8"?><Error><Code>InvalidArgument</Code><Message>S3 API Requests must be made to API port.</Message><RequestId>0</RequestId></Error> `
2021-10-20 02:18:16 -07:00
// RejectS3Middleware will reject requests that have AWS S3 specific headers.
func RejectS3Middleware ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if len ( r . Header . Get ( "X-Amz-Content-Sha256" ) ) > 0 ||
len ( r . Header . Get ( "X-Amz-Date" ) ) > 0 ||
strings . HasPrefix ( r . Header . Get ( "Authorization" ) , "AWS4-HMAC-SHA256" ) ||
r . URL . Query ( ) . Get ( "AWSAccessKeyId" ) != "" {
2022-08-08 13:37:01 -07:00
2022-05-09 11:24:43 -07:00
w . Header ( ) . Set ( "Location" , getMinIOServer ( ) )
2022-08-08 13:37:01 -07:00
w . WriteHeader ( http . StatusBadRequest )
w . Write ( [ ] byte ( apiRequestErr ) )
2021-10-20 02:18:16 -07:00
return
}
next . ServeHTTP ( w , r )
} )
2020-04-01 18:18:57 -07:00
}
2020-11-13 16:26:03 -08:00
func AuthenticationMiddleware ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2021-06-03 18:04:08 -07:00
token , err := auth . GetTokenFromRequest ( r )
if err != nil && err != auth . ErrNoAuthToken {
http . Error ( w , err . Error ( ) , http . StatusUnauthorized )
2020-11-13 16:26:03 -08:00
return
}
2022-04-28 12:55:06 -07:00
sessionToken , _ := auth . DecryptToken ( token )
2021-06-03 18:04:08 -07:00
// All handlers handle appropriately to return errors
// based on their swagger rules, we do not need to
// additionally return error here, let the next ServeHTTPs
// handle it appropriately.
2022-04-28 12:55:06 -07:00
if len ( sessionToken ) > 0 {
r . Header . Add ( "Authorization" , fmt . Sprintf ( "Bearer %s" , string ( sessionToken ) ) )
2023-01-27 12:23:30 -08:00
} else {
r . Header . Add ( "Authorization" , fmt . Sprintf ( "Bearer %s" , "Anonymous" ) )
2020-11-13 16:26:03 -08:00
}
2022-04-28 12:55:06 -07:00
ctx := r . Context ( )
claims , _ := auth . ParseClaimsFromToken ( string ( sessionToken ) )
if claims != nil {
// save user session id context
ctx = context . WithValue ( r . Context ( ) , utils . ContextRequestUserID , claims . STSSessionToken )
}
next . ServeHTTP ( w , r . WithContext ( ctx ) )
2020-11-13 16:26:03 -08:00
} )
}
2020-04-01 18:18:57 -07:00
// FileServerMiddleware serves files from the static folder
func FileServerMiddleware ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2021-02-16 12:53:18 -08:00
w . Header ( ) . Set ( "Server" , globalAppName ) // do not add version information
2020-04-30 10:50:51 -07:00
switch {
case strings . HasPrefix ( r . URL . Path , "/ws" ) :
serveWS ( w , r )
case strings . HasPrefix ( r . URL . Path , "/api" ) :
2020-04-01 18:18:57 -07:00
next . ServeHTTP ( w , r )
2020-04-30 10:50:51 -07:00
default :
2021-03-05 10:39:17 -08:00
buildFs , err := fs . Sub ( portal_ui . GetStaticAssets ( ) , "build" )
if err != nil {
panic ( err )
}
2023-01-19 13:15:28 -06:00
wrapHandlerSinglePageApplication ( requestBounce ( http . FileServer ( http . FS ( buildFs ) ) ) ) . ServeHTTP ( w , r )
2020-04-01 18:18:57 -07:00
}
} )
}
2020-05-08 16:36:08 -07:00
type notFoundRedirectRespWr struct {
http . ResponseWriter // We embed http.ResponseWriter
status int
}
func ( w * notFoundRedirectRespWr ) WriteHeader ( status int ) {
w . status = status // Store the status for our own use
if status != http . StatusNotFound {
w . ResponseWriter . WriteHeader ( status )
}
}
func ( w * notFoundRedirectRespWr ) Write ( p [ ] byte ) ( int , error ) {
if w . status != http . StatusNotFound {
return w . ResponseWriter . Write ( p )
}
return len ( p ) , nil // Lie that we successfully wrote it
}
2022-07-07 14:28:25 -05:00
// handleSPA handles the serving of the React Single Page Application
2021-09-13 20:09:19 -07:00
func handleSPA ( w http . ResponseWriter , r * http . Request ) {
basePath := "/"
2021-11-22 21:02:16 -08:00
// For SPA mode we will replace root base with a sub path if configured unless we received cp=y and cpb=/NEW/BASE
2021-09-13 20:09:19 -07:00
if v := r . URL . Query ( ) . Get ( "cp" ) ; v == "y" {
2021-11-22 21:02:16 -08:00
if base := r . URL . Query ( ) . Get ( "cpb" ) ; base != "" {
// make sure the subpath has a trailing slash
if ! strings . HasSuffix ( base , "/" ) {
base = fmt . Sprintf ( "%s/" , base )
}
basePath = base
}
2021-09-13 20:09:19 -07:00
}
indexPage , err := portal_ui . GetStaticAssets ( ) . Open ( "build/index.html" )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2022-07-07 14:28:25 -05:00
sts := r . URL . Query ( ) . Get ( "sts" )
stsAccessKey := r . URL . Query ( ) . Get ( "sts_a" )
stsSecretKey := r . URL . Query ( ) . Get ( "sts_s" )
2022-08-17 11:06:10 -05:00
overridenStyles := r . URL . Query ( ) . Get ( "ov_st" )
2022-07-07 14:28:25 -05:00
// if these three parameters are present we are being asked to issue a session with these values
if sts != "" && stsAccessKey != "" && stsSecretKey != "" {
creds := credentials . NewStaticV4 ( stsAccessKey , stsSecretKey , sts )
consoleCreds := & ConsoleCredentials {
ConsoleCredentials : creds ,
AccountAccessKey : stsAccessKey ,
}
sf := & auth . SessionFeatures { }
sf . HideMenu = true
sf . ObjectBrowser = true
2023-03-16 09:42:35 -06:00
if overridenStyles != "" {
err := ValidateEncodedStyles ( overridenStyles )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2022-08-17 11:06:10 -05:00
2023-03-16 09:42:35 -06:00
sf . CustomStyleOB = overridenStyles
}
2022-10-04 10:39:28 -07:00
2022-07-07 14:28:25 -05:00
sessionID , err := login ( consoleCreds , sf )
if err != nil {
2022-10-04 10:39:28 -07:00
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
2022-07-07 14:28:25 -05:00
}
2022-10-04 10:39:28 -07:00
cookie := NewSessionCookieForConsole ( * sessionID )
http . SetCookie ( w , & cookie )
2022-07-22 00:03:52 -07:00
// Allow us to be iframed
w . Header ( ) . Del ( "X-Frame-Options" )
2022-07-07 14:28:25 -05:00
}
2021-09-13 20:09:19 -07:00
indexPageBytes , err := io . ReadAll ( indexPage )
if err != nil {
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
}
2021-11-22 21:02:16 -08:00
// if we have a seeded basePath. This should override CONSOLE_SUBPATH every time, thus the `if else`
if basePath != "/" {
indexPageBytes = replaceBaseInIndex ( indexPageBytes , basePath )
// if we have a custom subpath replace it in
} else if getSubPath ( ) != "/" {
indexPageBytes = replaceBaseInIndex ( indexPageBytes , getSubPath ( ) )
}
2023-01-25 13:18:41 -08:00
indexPageBytes = replaceLicense ( indexPageBytes )
2021-11-22 21:02:16 -08:00
mimeType := mimedb . TypeByExtension ( filepath . Ext ( r . URL . Path ) )
if mimeType == "application/octet-stream" {
mimeType = "text/html"
2021-09-13 20:09:19 -07:00
}
2021-11-22 21:02:16 -08:00
w . Header ( ) . Set ( "Content-Type" , mimeType )
2021-09-13 20:09:19 -07:00
http . ServeContent ( w , r , "index.html" , time . Now ( ) , bytes . NewReader ( indexPageBytes ) )
}
2021-09-01 16:56:06 -07:00
2020-05-08 16:36:08 -07:00
// 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 ) {
2021-09-13 20:09:19 -07:00
if r . URL . Path == "/" {
handleSPA ( w , r )
return
2021-09-07 19:57:00 -05:00
}
2021-09-13 20:09:19 -07:00
w . Header ( ) . Set ( "Content-Type" , mimedb . TypeByExtension ( filepath . Ext ( r . URL . Path ) ) )
nfw := & notFoundRedirectRespWr { ResponseWriter : w }
h . ServeHTTP ( nfw , r )
if nfw . status == http . StatusNotFound {
handleSPA ( w , r )
2020-05-08 16:36:08 -07:00
}
}
}
2021-06-07 19:30:53 -07:00
2021-06-18 15:40:25 -07:00
type nullWriter struct { }
2021-06-15 17:52:39 -07:00
2021-06-18 15:40:25 -07:00
func ( lw nullWriter ) Write ( b [ ] byte ) ( int , error ) {
2021-06-15 17:52:39 -07:00
return len ( b ) , nil
}
2021-06-07 19:30:53 -07:00
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix"
2021-06-15 17:52:39 -07:00
func configureServer ( s * http . Server , _ , _ string ) {
2022-04-28 12:55:06 -07:00
// Turn-off random logger by Go net/http
2021-06-18 15:40:25 -07:00
s . ErrorLog = log . New ( & nullWriter { } , "" , 0 )
2021-06-07 19:30:53 -07:00
}
2021-11-22 21:02:16 -08:00
func getSubPath ( ) string {
subPathOnce . Do ( func ( ) {
2023-06-14 12:36:48 -07:00
cfgSubPath = parseSubPath ( env . Get ( SubPath , "" ) )
2021-11-22 21:02:16 -08:00
} )
2023-06-14 12:36:48 -07:00
return cfgSubPath
2021-11-22 21:02:16 -08:00
}
2022-04-25 13:23:40 -07:00
func parseSubPath ( v string ) string {
v = strings . TrimSpace ( v )
if v == "" {
return SlashSeparator
}
// Replace all unnecessary `\` to `/`
// also add pro-actively at the end.
2023-06-14 12:36:48 -07:00
subPath := path . Clean ( filepath . ToSlash ( v ) )
2022-04-25 13:23:40 -07:00
if ! strings . HasPrefix ( subPath , SlashSeparator ) {
subPath = SlashSeparator + subPath
}
if ! strings . HasSuffix ( subPath , SlashSeparator ) {
2022-05-05 13:44:10 -07:00
subPath += SlashSeparator
2022-04-25 13:23:40 -07:00
}
return subPath
}
2021-11-22 21:02:16 -08:00
func replaceBaseInIndex ( indexPageBytes [ ] byte , basePath string ) [ ] byte {
2022-03-07 17:30:36 -08:00
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 )
indexPageStr = strings . Replace ( indexPageStr , "<base href=\"/\"/>" , newBase , 1 )
indexPageBytes = [ ] byte ( indexPageStr )
}
2021-11-22 21:02:16 -08:00
return indexPageBytes
}
2023-01-19 13:15:28 -06:00
2023-01-25 13:18:41 -08:00
func replaceLicense ( indexPageBytes [ ] byte ) [ ] byte {
indexPageStr := string ( indexPageBytes )
newPlan := fmt . Sprintf ( "<meta name=\"minio-license\" content=\"%s\" />" , InstanceLicensePlan . String ( ) )
indexPageStr = strings . Replace ( indexPageStr , "<meta name=\"minio-license\" content=\"apgl\"/>" , newPlan , 1 )
indexPageBytes = [ ] byte ( indexPageStr )
return indexPageBytes
}
2023-01-19 13:15:28 -06:00
func requestBounce ( handler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if strings . HasSuffix ( r . URL . Path , "/" ) {
http . NotFound ( w , r )
return
}
handler . ServeHTTP ( w , r )
} )
}