@@ -49,6 +49,7 @@ type VICEProxy struct {
4949 checkResourceAccessBase string // The base URL for the check-resource-access service.
5050 sessionStore * sessions.CookieStore // The backend session storage.
5151 ssoClient http.Client // The HTTP client for back-channel requests to the IDP.
52+ disableAuth bool // If true, authentication and authorization are disabled.
5253}
5354
5455// Analysis contains the ID for the Analysis, which gets used as the resource
@@ -579,6 +580,47 @@ func (c *VICEProxy) GetFrontendHost() (string, error) {
579580 return svcURL .Host , nil
580581}
581582
583+ // authenticateAndAuthorize validates the user's session and checks if they have permission
584+ // to access the resource. Returns the username on success, or an error on failure.
585+ func (c * VICEProxy ) authenticateAndAuthorize (w http.ResponseWriter , r * http.Request ) (string , error ) {
586+ // Get the username from the cookie
587+ session , err := c .sessionStore .Get (r , sessionName )
588+ if err != nil {
589+ return "" , errors .Wrap (err , "failed to get session" )
590+ }
591+
592+ // Check if the session contains a username
593+ usernameValue , ok := session .Values [sessionKey ]
594+ if ! ok || usernameValue == nil {
595+ return "" , errors .New ("no session found" )
596+ }
597+
598+ username , ok := usernameValue .(string )
599+ if ! ok || username == "" {
600+ return "" , errors .New ("username was empty or invalid" )
601+ }
602+ log .Infof ("authenticated user: %s" , username )
603+
604+ // Check to make sure the user can access the resource.
605+ allowed , err := c .IsAllowed (username , c .resourceName )
606+ if ! allowed || err != nil {
607+ if err != nil {
608+ return "" , errors .Wrap (err , "access denied" )
609+ }
610+ return "" , errors .New ("access denied" )
611+ }
612+ log .Infof ("user %s authorized for resource %s" , username , c .resourceName )
613+
614+ // CRITICAL: Don't reset session for WebSocket upgrades (would corrupt the upgrade handshake)
615+ if ! c .isWebsocket (r ) {
616+ if err = c .ResetSessionExpiration (w , r ); err != nil {
617+ return "" , errors .Wrap (err , "error resetting session expiration" )
618+ }
619+ }
620+
621+ return username , nil
622+ }
623+
582624// Proxy returns a handler that can support both websockets and http requests.
583625func (c * VICEProxy ) Proxy () (http.Handler , error ) {
584626 rp , err := c .ReverseProxy ()
@@ -594,51 +636,22 @@ func (c *VICEProxy) Proxy() (http.Handler, error) {
594636 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
595637 log .Infof ("handling request for %s from remote address %s" , r .URL .String (), r .RemoteAddr )
596638
597- //Get the username from the cookie
598- session , err := c .sessionStore .Get (r , sessionName )
599- if err != nil {
600- err = errors .Wrap (err , "failed to get session" )
601- log .Errorf ("session error: %v" , err )
602- http .Error (w , err .Error (), http .StatusInternalServerError )
603- return
604- }
605-
606- username := session .Values [sessionKey ].(string )
607- if username == "" {
608- err = errors .Wrap (err , "username was empty" )
609- log .Errorf ("authentication error: %v" , err )
610- http .Error (w , err .Error (), http .StatusForbidden )
611- return
612- }
613- log .Infof ("authenticated user: %s" , username )
614-
615- // Check to make sure the user can access the resource.
616- allowed , err := c .IsAllowed (username , c .resourceName )
617- if ! allowed || err != nil {
639+ // Conditionally perform authentication and authorization
640+ if ! c .disableAuth {
641+ username , err := c .authenticateAndAuthorize (w , r )
618642 if err != nil {
619- err = errors . Wrap ( err , "access denied" )
620- } else {
621- err = errors . New ( "access denied" )
643+ log . Errorf ( "auth/authz error: %v" , err )
644+ http . Error ( w , err . Error (), http . StatusForbidden )
645+ return
622646 }
623- log .Errorf ( "authorization error for user %s : %v " , username , err )
624- http . Error ( w , err . Error (), http . StatusForbidden )
625- return
647+ log .Debugf ( "request authorized for user: %s " , username )
648+ } else {
649+ log . Debug ( "authentication disabled, allowing unauthenticated access" )
626650 }
627- log .Infof ("user %s authorized for resource %s" , username , c .resourceName )
628651
629652 // Override the X-Forwarded-Host header.
630653 r .Header .Set ("X-Forwarded-Host" , frontendHost )
631654
632- // CRITICAL: Don't reset session for WebSocket upgrades (would corrupt the upgrade handshake)
633- if ! c .isWebsocket (r ) {
634- if err = c .ResetSessionExpiration (w , r ); err != nil {
635- err = errors .Wrap (err , "error resetting session expiration" )
636- log .Errorf ("session expiration error: %v" , err )
637- http .Error (w , err .Error (), http .StatusInternalServerError )
638- return
639- }
640- }
641-
642655 // The reverse proxy handles both HTTP and WebSocket upgrade requests transparently
643656 log .Infof ("proxying request to %s%s" , c .backendURL , r .URL .Path )
644657 rp .ServeHTTP (w , r )
@@ -681,6 +694,7 @@ func main() {
681694 encodedReadTimeout = flag .String ("read-timeout" , "48h" , "The maximum duration for reading the entire request, including the body." )
682695 encodedWriteTimeout = flag .String ("write-timeout" , "48h" , "The maximum duration before timing out writes of the response." )
683696 encodedIdleTimeout = flag .String ("idle-timeout" , "5000s" , "The maximum amount of time to wait for the next request when keep-alives are enabled." )
697+ disableAuth = flag .Bool ("disable-auth" , false , "Disable authentication and authorization. When true, allows unauthenticated access to the proxied application." )
684698 )
685699
686700 flag .Var (& corsOrigins , "allowed-origins" , "List of allowed origins, separated by commas." )
@@ -721,6 +735,7 @@ func main() {
721735 log .Infof ("read timeout is %s" , * encodedReadTimeout )
722736 log .Infof ("write timeout is %s" , * encodedWriteTimeout )
723737 log .Infof ("idle timeout is %s" , * encodedIdleTimeout )
738+ log .Infof ("authentication disabled: %v" , * disableAuth )
724739
725740 for _ , c := range corsOrigins {
726741 log .Infof ("Origin: %s\n " , c )
@@ -778,6 +793,7 @@ func main() {
778793 checkResourceAccessBase : * checkResourceAccessBase ,
779794 sessionStore : sessionStore ,
780795 ssoClient : * client ,
796+ disableAuth : * disableAuth ,
781797 }
782798
783799 resourceName , err := p .getResourceName (* externalID )
@@ -793,11 +809,18 @@ func main() {
793809
794810 r := mux .NewRouter ()
795811
796- // If the query contains a ticket in the query params, then it needs to be
797- // validated.
812+ // Health check endpoint - always available
798813 r .PathPrefix ("/url-ready" ).HandlerFunc (p .URLIsReady )
799- r .PathPrefix ("/" ).Queries ("code" , "" ).Handler (http .HandlerFunc (p .HandleAuthorizationCode ))
800- r .PathPrefix ("/" ).MatcherFunc (p .Session ).Handler (http .HandlerFunc (p .RequireKeycloakAuth ))
814+
815+ // Conditionally add authentication routes based on --disable-auth flag
816+ if ! * disableAuth {
817+ // If the query contains a code parameter, handle the OAuth authorization code
818+ r .PathPrefix ("/" ).Queries ("code" , "" ).Handler (http .HandlerFunc (p .HandleAuthorizationCode ))
819+ // If the request doesn't have a valid session, redirect to Keycloak for authentication
820+ r .PathPrefix ("/" ).MatcherFunc (p .Session ).Handler (http .HandlerFunc (p .RequireKeycloakAuth ))
821+ }
822+
823+ // Proxy all requests to the backend
801824 r .PathPrefix ("/" ).Handler (proxy )
802825
803826 c := cors .New (cors.Options {
0 commit comments