22
33import com .sun .net .httpserver .Authenticator ;
44import com .sun .net .httpserver .HttpContext ;
5+ import com .sun .net .httpserver .HttpExchange ;
56import com .sun .net .httpserver .HttpHandler ;
67import com .sun .net .httpserver .HttpServer ;
78import com .sun .net .httpserver .HttpsConfigurator ;
1011import io .prometheus .metrics .model .registry .PrometheusRegistry ;
1112import java .io .Closeable ;
1213import java .io .IOException ;
14+ import java .io .InputStream ;
1315import java .net .InetAddress ;
1416import java .net .InetSocketAddress ;
17+ import java .security .PrivilegedActionException ;
18+ import java .security .PrivilegedExceptionAction ;
1519import java .util .concurrent .ExecutionException ;
1620import java .util .concurrent .ExecutorService ;
1721import java .util .concurrent .RejectedExecutionHandler ;
1822import java .util .concurrent .SynchronousQueue ;
1923import java .util .concurrent .ThreadPoolExecutor ;
2024import java .util .concurrent .TimeUnit ;
25+ import javax .security .auth .Subject ;
2126
2227/**
2328 * Expose Prometheus metrics using a plain Java HttpServer.
@@ -51,16 +56,25 @@ private HTTPServer(
5156 HttpServer httpServer ,
5257 PrometheusRegistry registry ,
5358 Authenticator authenticator ,
59+ String authenticatedSubjectAttributeName ,
5460 HttpHandler defaultHandler ) {
5561 if (httpServer .getAddress () == null ) {
5662 throw new IllegalArgumentException ("HttpServer hasn't been bound to an address" );
5763 }
5864 this .server = httpServer ;
5965 this .executorService = executorService ;
6066 registerHandler (
61- "/" , defaultHandler == null ? new DefaultHandler () : defaultHandler , authenticator );
62- registerHandler ("/metrics" , new MetricsHandler (config , registry ), authenticator );
63- registerHandler ("/-/healthy" , new HealthyHandler (), authenticator );
67+ "/" ,
68+ defaultHandler == null ? new DefaultHandler () : defaultHandler ,
69+ authenticator ,
70+ authenticatedSubjectAttributeName );
71+ registerHandler (
72+ "/metrics" ,
73+ new MetricsHandler (config , registry ),
74+ authenticator ,
75+ authenticatedSubjectAttributeName );
76+ registerHandler (
77+ "/-/healthy" , new HealthyHandler (), authenticator , authenticatedSubjectAttributeName );
6478 try {
6579 // HttpServer.start() starts the HttpServer in a new background thread.
6680 // If we call HttpServer.start() from a thread of the executorService,
@@ -74,13 +88,54 @@ private HTTPServer(
7488 }
7589 }
7690
77- private void registerHandler (String path , HttpHandler handler , Authenticator authenticator ) {
78- HttpContext context = server .createContext (path , handler );
91+ private void registerHandler (
92+ String path , HttpHandler handler , Authenticator authenticator , String subjectAttributeName ) {
93+ HttpContext context = server .createContext (path , wrapWithDoAs (handler , subjectAttributeName ));
7994 if (authenticator != null ) {
8095 context .setAuthenticator (authenticator );
8196 }
8297 }
8398
99+ private HttpHandler wrapWithDoAs (HttpHandler handler , String subjectAttributeName ) {
100+ if (subjectAttributeName == null ) {
101+ return handler ;
102+ }
103+
104+ // invoke handler using the subject.doAs from the named attribute
105+ return new HttpHandler () {
106+ @ Override
107+ public void handle (HttpExchange exchange ) throws IOException {
108+ Object authSubject = exchange .getAttribute (subjectAttributeName );
109+ if (authSubject instanceof Subject ) {
110+ try {
111+ Subject .doAs (
112+ (Subject ) authSubject ,
113+ (PrivilegedExceptionAction <IOException >)
114+ () -> {
115+ handler .handle (exchange );
116+ return null ;
117+ });
118+ } catch (PrivilegedActionException e ) {
119+ if (e .getException () != null ) {
120+ throw new IOException (e .getException ());
121+ } else throw new IOException (e );
122+ }
123+ } else {
124+ drainInputAndClose (exchange );
125+ exchange .sendResponseHeaders (403 , -1 );
126+ }
127+ }
128+ };
129+ }
130+
131+ private void drainInputAndClose (HttpExchange httpExchange ) throws IOException {
132+ InputStream inputStream = httpExchange .getRequestBody ();
133+ byte [] b = new byte [4096 ];
134+ while (inputStream .read (b ) != -1 )
135+ ;
136+ inputStream .close ();
137+ }
138+
84139 /** Stop the HTTP server. Same as {@link #close()}. */
85140 public void stop () {
86141 close ();
@@ -120,6 +175,7 @@ public static class Builder {
120175 private Authenticator authenticator = null ;
121176 private HttpsConfigurator httpsConfigurator = null ;
122177 private HttpHandler defaultHandler = null ;
178+ private String authenticatedSubjectAttributeName = null ;
123179
124180 private Builder (PrometheusProperties config ) {
125181 this .config = config ;
@@ -171,6 +227,12 @@ public Builder authenticator(Authenticator authenticator) {
171227 return this ;
172228 }
173229
230+ /** Optional: the attribute name of a Subject from a custom authenticator. */
231+ public Builder authenticatedSubjectAttributeName (String authenticatedSubjectAttributeName ) {
232+ this .authenticatedSubjectAttributeName = authenticatedSubjectAttributeName ;
233+ return this ;
234+ }
235+
174236 /** Optional: {@link HttpsConfigurator} for TLS/SSL */
175237 public Builder httpsConfigurator (HttpsConfigurator configurator ) {
176238 this .httpsConfigurator = configurator ;
@@ -201,7 +263,13 @@ public HTTPServer buildAndStart() throws IOException {
201263 ExecutorService executorService = makeExecutorService ();
202264 httpServer .setExecutor (executorService );
203265 return new HTTPServer (
204- config , executorService , httpServer , registry , authenticator , defaultHandler );
266+ config ,
267+ executorService ,
268+ httpServer ,
269+ registry ,
270+ authenticator ,
271+ authenticatedSubjectAttributeName ,
272+ defaultHandler );
205273 }
206274
207275 private InetSocketAddress makeInetSocketAddress () {
0 commit comments