001package com.nimbusds.common.servlet;
002
003
004import java.io.IOException;
005import java.util.Properties;
006import javax.servlet.ServletConfig;
007import javax.servlet.ServletException;
008import javax.servlet.http.HttpServletRequest;
009import javax.servlet.http.HttpServletResponse;
010
011import com.codahale.metrics.servlets.AdminServlet;
012import com.thetransactioncompany.util.PropertyParseException;
013import com.thetransactioncompany.util.PropertyRetriever;
014import org.apache.logging.log4j.LogManager;
015import org.apache.logging.log4j.Logger;
016
017import com.nimbusds.common.config.ConfigurationException;
018import com.nimbusds.common.oauth2.MasterAccessTokenValidator;
019import com.nimbusds.common.oauth2.SHA256BasedAccessTokenValidator;
020
021
022/**
023 * Monitor servlet for exposing Dropwizard metrics and health checks, requires
024 * an OAuth 2.0 bearer token for access.
025 *
026 * <p>The access token is defined by a
027 * {@link Configuration#API_TOKEN_SHA256_PROPERTY_NAME Java property} obtained
028 * from 1) system properties or from 2) a properties file specified by
029 * {@link com.nimbusds.common.servlet.MonitorLauncher#CONFIG_CTX_PARAMETER_NAME
030 * servlet context parameter}.
031 */
032public class MonitorServlet extends AdminServlet {
033        
034        
035        /**
036         * The monitor servlet access token configuration.
037         */
038        public static class Configuration {
039                
040                
041                /**
042                 * The property name for the SHA-256 hash of the API access
043                 * token.
044                 */
045                public static final String API_TOKEN_SHA256_PROPERTY_NAME = "monitor.apiAccessTokenSHA256";
046                
047                
048                /**
049                 * The property name for the SHA-256 hash of the secondary API
050                 * access token.
051                 */
052                public static final String SECONDARY_API_TOKEN_SHA256_PROPERTY_NAME = "monitor.secondaryAPIAccessTokenSHA256";
053                
054                
055                /**
056                 * The SHA-256 hash based (in hexadecimal format) monitor API
057                 * access token validator, if {@code null} the API is disabled.
058                 */
059                public final SHA256BasedAccessTokenValidator apiAccessTokenSHA256;
060                
061                
062                /**
063                 * Creates a new monitor servlet access token configuration.
064                 *
065                 * @param properties The configuration properties, can be
066                 *                   overridden with system properties.
067                 *
068                 * @throws ConfigurationException On a invalid configuration
069                 *                                property.
070                 */
071                public Configuration(final Properties properties)
072                        throws ConfigurationException {
073                        
074                        // Retrofit SECONDARY_API_TOKEN_SHA256_PROPERTY_NAME from local / sys properties
075                        String secondaryTokenHash = properties.getProperty(SECONDARY_API_TOKEN_SHA256_PROPERTY_NAME);
076                        if (secondaryTokenHash != null && ! secondaryTokenHash.trim().isEmpty()) {
077                                properties.setProperty(API_TOKEN_SHA256_PROPERTY_NAME + ".xxx", secondaryTokenHash);
078                        }
079                        
080                        secondaryTokenHash = System.getProperties().getProperty(SECONDARY_API_TOKEN_SHA256_PROPERTY_NAME);
081                        if (secondaryTokenHash != null && ! secondaryTokenHash.trim().isEmpty()) {
082                                properties.setProperty(API_TOKEN_SHA256_PROPERTY_NAME + ".xxx", secondaryTokenHash);
083                        }
084                        
085                        PropertyRetriever pr = new PropertyRetriever(properties, true);
086                        
087                        try {
088                                apiAccessTokenSHA256 = SHA256BasedAccessTokenValidator.from(
089                                        pr,
090                                        API_TOKEN_SHA256_PROPERTY_NAME,
091                                        false,
092                                        API_TOKEN_SHA256_PROPERTY_NAME + ".");
093                        } catch (PropertyParseException e) {
094                                throw new ConfigurationException(e.getMessage() + ": Property: " + e.getPropertyKey(), e);
095                        }
096                }
097        }
098
099
100        /**
101         * The access token validator.
102         */
103        protected MasterAccessTokenValidator tokenValidator;
104        
105        
106        /**
107         * Creates a new access token validator.
108         *
109         * @param config The servlet configuration, used to retrieve the
110         *               monitor configuration properties.
111         *
112         * @return The basic access token validator.
113         *
114         * @throws ServletException If a configuration property is missing or
115         *                          invalid.
116         */
117        static MasterAccessTokenValidator createAccessTokenValidator(final ServletConfig config)
118                throws ServletException {
119                
120                Logger log = LogManager.getLogger("MAIN");
121                
122                try {
123                        Properties props = ResourceRetriever.getProperties(
124                                config.getServletContext(),
125                                MonitorLauncher.CONFIG_CTX_PARAMETER_NAME,
126                                log);
127                        
128                        return new Configuration(props).apiAccessTokenSHA256;
129                        
130                } catch (Exception e) {
131                        log.error(e.getMessage(), e);
132                        throw new ServletException(e.getMessage(), e);
133                }
134        }
135
136
137        @Override
138        public void init(final ServletConfig config)
139                throws ServletException {
140
141                super.init(config);
142                
143                tokenValidator = createAccessTokenValidator(config);
144                
145                LogManager.getLogger("MAIN").info("[CM7110] Loaded monitor API servlet");
146        }
147
148
149        @Override
150        protected void doGet(final HttpServletRequest req,
151                             final HttpServletResponse resp)
152                throws ServletException, IOException {
153
154                if (! tokenValidator.validateBearerAccessToken(req, resp)) {
155                        return; // invalid or missing token, or web API disabled
156                }
157
158                super.doGet(req, resp);
159        }
160
161
162        @Override
163        protected void service(final HttpServletRequest req,
164                               final HttpServletResponse resp)
165                throws ServletException, IOException {
166
167                if (! tokenValidator.validateBearerAccessToken(req, resp)) {
168                        return; // invalid or missing token, or web API disabled
169                }
170
171                super.service(req, resp);
172        }
173}