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}