66import jakarta .servlet .http .HttpServletResponse ;
77import java .io .ByteArrayInputStream ;
88import java .io .IOException ;
9+ import java .security .cert .CertificateException ;
910import java .security .cert .CertificateFactory ;
1011import java .security .cert .X509Certificate ;
1112import java .util .Base64 ;
12- import lombok .Setter ;
13+ import lombok .RequiredArgsConstructor ;
14+ import org .cloudfoundry .autoscaler .scheduler .conf .CfServerConfiguration ;
1315import org .slf4j .Logger ;
1416import org .slf4j .LoggerFactory ;
15- import org .springframework .beans .factory .annotation .Value ;
16- import org .springframework .boot .context .properties .ConfigurationProperties ;
1717import org .springframework .core .annotation .Order ;
1818import org .springframework .stereotype .Component ;
1919import org .springframework .web .filter .OncePerRequestFilter ;
2020
2121@ Component
2222@ Order (0 )
23- @ ConfigurationProperties (prefix = "cfserver" )
24- @ Setter
23+ @ RequiredArgsConstructor
2524public class HttpAuthFilter extends OncePerRequestFilter {
26- private Logger logger = LoggerFactory .getLogger (this .getClass ());
27-
28- private String validSpaceGuid ;
29- private String validOrgGuid ;
30-
31- @ Value ("${cfserver.healthserver.username}" )
32- private String healthServerUsername ;
3325
34- @ Value ("${cfserver.healthserver.password}" )
35- private String healthServerPassword ;
36-
37- public void setHealthServerUsername (String healthServerUsername ) {
38- this .healthServerUsername = healthServerUsername ;
39- }
26+ private static final String AUTHORIZATION_HEADER = "Authorization" ;
27+ private static final String BASIC_AUTH_PREFIX = "Basic " ;
28+ private static final String XFCC_HEADER = "X-Forwarded-Client-Cert" ;
29+ private static final String HEALTH_ENDPOINT = "/health" ;
4030
41- public void setHealthServerPassword (String healthServerPassword ) {
42- this .healthServerPassword = healthServerPassword ;
43- }
31+ private Logger logger = LoggerFactory .getLogger (this .getClass ());
32+ private final CfServerConfiguration cfServerConfiguration ;
4433
4534 @ Override
4635 protected void doFilterInternal (
4736 HttpServletRequest request , HttpServletResponse response , FilterChain filterChain )
4837 throws ServletException , IOException {
4938
50- logger .info (
51- "Received request with request "
52- + request .getRequestURI ()
53- + " method"
54- + request .getMethod ());
55-
56- // Debug logging
5739 String forwardedProto = request .getHeader ("X-Forwarded-Proto" );
58- boolean isHealthEndpoint = request .getRequestURI ().contains ("/health" );
40+ boolean isHealthEndpoint = request .getRequestURI ().contains (HEALTH_ENDPOINT );
41+
5942 logger .info (
60- "DEBUG: scheme={}, X-Forwarded-Proto={}, isHealthEndpoint={}, healthServerUsername ={},"
61- + " healthServerPassword={}" ,
43+ "Received {} request, scheme={},X-Forwarded-Proto={}, isHealthEndpoint={}, username ={}" ,
44+ request . getMethod () ,
6245 request .getScheme (),
6346 forwardedProto ,
6447 isHealthEndpoint ,
65- healthServerUsername ,
66- healthServerPassword );
67-
68- // Skip filter if X-Forwarded-Client-Cert is missing and not a health request
69- String xfccHeader = request .getHeader ("X-Forwarded-Client-Cert" );
70- if ((xfccHeader == null || xfccHeader .isEmpty ()) && !isHealthEndpoint ) {
71- logger .info (
72- "DEBUG: Skipping request without X-Forwarded-Client-Cert - URI={}" ,
73- request .getRequestURI ());
74- filterChain .doFilter (request , response );
75- return ;
76- }
77-
78- // handles /health endpoint with basic auth
79- if (request .getRequestURI ().contains ("/health" )) {
80- logger .info ("DEBUG: Processing health endpoint request" );
81- // parse request basic auth header
82- String authHeader = request .getHeader ("Authorization" );
83- logger .info ("DEBUG: Authorization header: {}" , authHeader != null ? "present" : "missing" );
84- if (authHeader == null || !authHeader .startsWith ("Basic " )) {
85- logger .warn ("Missing or invalid Authorization header for health check request" );
86- response .sendError (HttpServletResponse .SC_UNAUTHORIZED , "Unauthorized" );
87- return ;
88- }
89- String [] credentials =
90- new String (Base64 .getDecoder ().decode (authHeader .substring (6 ))).split (":" );
91-
92- if (credentials .length != 2 ) {
93- logger .warn ("Invalid Authorization header format for health check request" );
94- response .sendError (HttpServletResponse .SC_BAD_REQUEST , "Bad Request" );
95- return ;
96- }
97- if (!credentials [0 ].equals (healthServerUsername )
98- || !credentials [1 ].equals (healthServerPassword )) {
99- logger .warn ("Invalid credentials for health check request" );
100- response .sendError (HttpServletResponse .SC_UNAUTHORIZED , "Unauthorized" );
101- return ;
102- } else {
103- response .setStatus (HttpServletResponse .SC_OK );
104- response .setContentType ("application/json" );
105- response .getWriter ().write ("{\" status\" :\" UP\" }" );
106- response .getWriter ().flush ();
107- }
48+ cfServerConfiguration .getHealthserver ().getUsername ());
10849
50+ if (isHealthEndpoint ) {
51+ handleHealthEndpoint (request , response );
10952 return ;
11053 }
111-
54+ // Check for XFCC header
55+ String xfccHeader = request .getHeader (XFCC_HEADER );
11256 if (xfccHeader == null || xfccHeader .isEmpty ()) {
113- logger .warn ("Missing X-Forwarded-Client-Cert header" );
114- response .sendError (
115- HttpServletResponse .SC_BAD_REQUEST ,
116- "Missing X-Forwarded-Client-Cert header in the request" );
57+ logger .warn ("Missing X-Forwarded-Client-Cert header, URI={}" , request .getRequestURI ());
58+ filterChain .doFilter (request , response );
11759 return ;
11860 }
119- logger . info (
120- "X-Forwarded-Client-Cert header received ... checking authorized org and space in OU" );
121- logger . info ( "X-Forwarded-Client-Cert header: " + xfccHeader );
61+ validateOrganizationAndSpace ( xfccHeader , response );
62+ filterChain . doFilter ( request , response );
63+ }
12264
65+ private void validateOrganizationAndSpace (String xfccHeader , HttpServletResponse response )
66+ throws IOException {
67+ logger .info (
68+ "X-Forwarded-Client-Cert header received ... checking authorized cf organization and space"
69+ + " in Organizational Unit" );
12370 try {
12471 String organizationalUnit = extractOrganizationalUnit (xfccHeader );
125-
12672 // Validate both key-value pairs in OrganizationalUnit
12773 if (!isValidOrganizationalUnit (organizationalUnit )) {
12874 logger .warn ("Unauthorized OrganizationalUnit: " + organizationalUnit );
12975 response .sendError (HttpServletResponse .SC_FORBIDDEN , "Unauthorized OrganizationalUnit" );
130- return ;
13176 }
132- } catch (Exception e ) {
77+ } catch (CertificateException e ) {
13378 logger .warn ("Invalid certificate: " + e .getMessage ());
13479 response .sendError (
13580 HttpServletResponse .SC_BAD_REQUEST , "Invalid certificate: " + e .getMessage ());
81+ }
82+ }
83+
84+ private void handleHealthEndpoint (HttpServletRequest request , HttpServletResponse response )
85+ throws IOException {
86+ logger .info ("Handling health check request with Basic Auth" );
87+ String authHeader = request .getHeader (AUTHORIZATION_HEADER );
88+ logger .info ("Authorization header: {}" , authHeader != null ? "present" : "missing" );
89+
90+ if (authHeader == null || !authHeader .startsWith (BASIC_AUTH_PREFIX )) {
91+ logger .warn ("Missing or invalid Authorization header for health check request" );
92+ response .sendError (HttpServletResponse .SC_UNAUTHORIZED , "Unauthorized" );
13693 return ;
13794 }
138- // Proceed with the request
139- filterChain .doFilter (request , response );
95+ String [] credentials = decodeBasicAuth (authHeader );
96+ if (credentials .length != 2 ) {
97+ logger .warn ("Invalid Authorization header format for health check request" );
98+ response .sendError (HttpServletResponse .SC_BAD_REQUEST , "Bad Request" );
99+ return ;
100+ }
101+ if (!credentials [0 ].equals (cfServerConfiguration .getHealthserver ().getUsername ())
102+ || !credentials [1 ].equals (cfServerConfiguration .getHealthserver ().getPassword ())) {
103+ logger .warn ("Invalid credentials for health check request" );
104+ response .sendError (HttpServletResponse .SC_UNAUTHORIZED , "Unauthorized" );
105+ return ;
106+ }
107+
108+ response .setStatus (HttpServletResponse .SC_OK );
109+ response .setContentType ("application/json" );
110+ response .getWriter ().write ("{\" status\" :\" UP\" }" );
111+ response .getWriter ().flush ();
112+ }
113+
114+ private String [] decodeBasicAuth (String authHeader ) {
115+ try {
116+ return new String (
117+ Base64 .getDecoder ().decode (authHeader .substring (BASIC_AUTH_PREFIX .length ())))
118+ .split (":" );
119+ } catch (IllegalArgumentException e ) {
120+ logger .warn ("Failed to decode Basic Auth header: {}" , e .getMessage ());
121+ return null ;
122+ }
140123 }
141124
142- private String extractOrganizationalUnit (String certValue ) throws Exception {
125+ private String extractOrganizationalUnit (String certValue ) throws CertificateException {
143126 X509Certificate certificate = parseCertificate (certValue );
144127 return certificate .getSubjectX500Principal ().getName ();
145128 }
146129
147- private X509Certificate parseCertificate (String certValue ) throws Exception {
130+ private X509Certificate parseCertificate (String certValue ) throws CertificateException {
148131 // Extract the base64-encoded certificate from the XFCC header
149132 String base64Cert =
150133 certValue
@@ -159,8 +142,10 @@ private X509Certificate parseCertificate(String certValue) throws Exception {
159142 }
160143
161144 private boolean isValidOrganizationalUnit (String organizationalUnit ) {
162- boolean isSpaceValid = organizationalUnit .contains ("space:" + validSpaceGuid );
163- boolean isOrgValid = organizationalUnit .contains ("organization:" + validOrgGuid );
145+ boolean isSpaceValid =
146+ organizationalUnit .contains ("space:" + cfServerConfiguration .getValidSpaceGuid ());
147+ boolean isOrgValid =
148+ organizationalUnit .contains ("organization:" + cfServerConfiguration .getValidOrgGuid ());
164149 return isSpaceValid && isOrgValid ;
165150 }
166151}
0 commit comments