public class AclParser { private static AclsAuthorizationMessages log = MessagesFactory.get(AclsAuthorizationMessages.class); public String resourceRole; public ArrayList<String> users; public ArrayList<String> groups; public boolean anyUser = true; public boolean anyGroup = true; public IpAddressValidator ipv; public AclParser() {} public void parseAcls(String resourceRole, String acls) throws InvalidACLException { if (acls != null) { String[] parts = acls.split(";"); if (parts.length != 3) { log.invalidAclsFoundForResource(resourceRole); throw new InvalidACLException( "Invalid ACLs specified for requested resource: " + resourceRole); } else { log.aclsFoundForResource(resourceRole); } parseUserAcls(parts); parseGroupAcls(parts); parseIpAddressAcls(parts); } else { log.noAclsFoundForResource(resourceRole); users = new ArrayList<String>(); groups = new ArrayList<String>(); ipv = new IpAddressValidator(null); } } private void parseUserAcls(String[] parts) { users = new ArrayList<String>(); Collections.addAll(users, parts[0].split(",")); if (!users.contains("*")) { anyUser = false; } } private void parseGroupAcls(String[] parts) { groups = new ArrayList<String>(); Collections.addAll(groups, parts[1].split(",")); if (!groups.contains("*")) { anyGroup = false; } } private void parseIpAddressAcls(String[] parts) { ipv = new IpAddressValidator(parts[2]); } }
public class UrlConnectionDispatch extends AbstractGatewayFilter { private static final GatewayMessages LOG = MessagesFactory.get(GatewayMessages.class); private static final GatewayResources RES = ResourcesFactory.get(GatewayResources.class); private static Auditor auditor = AuditServiceFactory.getAuditService() .getAuditor( AuditConstants.DEFAULT_AUDITOR_NAME, AuditConstants.KNOX_SERVICE_NAME, AuditConstants.KNOX_COMPONENT_NAME); @Override protected void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String method = request.getMethod().toUpperCase(); if (method.equals("GET")) { try { doGet(getDispatchUrl(request), request, response); } catch (URISyntaxException e) { throw new ServletException(e); } } else { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } } protected static URI getDispatchUrl(HttpServletRequest request) { StringBuffer str = request.getRequestURL(); String query = request.getQueryString(); if (query != null) { str.append('?'); str.append(query); } URI url = URI.create(str.toString()); return url; } public void doGet(URI url, HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException { String sourcePathInfo = request.getPathInfo(); String sourcePattern = getConfig().getInitParameter("pattern"); String targetPattern = getConfig().getInitParameter("target"); // TODO: Some of the compilation should be done at servlet init for performance reasons. Template sourceTemplate = Parser.parseTemplate(sourcePattern); Template targetTemplate = Parser.parseTemplate(targetPattern); Resolver resolver = new DispatchParamResolver(getConfig(), request); URI sourceUri = new URI(sourcePathInfo); URI targetUri = Rewriter.rewrite(sourceUri, sourceTemplate, targetTemplate, resolver, null); // //TODO: This should be more at filter init. // Pattern sourceRegex = UrlRewriter.compileUrlRegex( sourcePattern ); // Matcher matcher = sourceRegex.matcher( sourcePathInfo ); // String targetUrl = MessageFormat.format( targetPattern, Regex.toGroupArray( matcher ) ); // System.out.println( "Source URI: " + expect.getRequestURI() ); // System.out.println( "Source URL: " + expect.getRequestURL() ); // System.out.println( "Source Query: " + expect.getQueryString() ); // System.out.println( "Source pathInfo: " + sourcePathInfo ); // System.out.println( "Source pattern: " + sourcePattern ); // System.out.println( "Target pattern: " + targetPattern ); // System.out.println( "Resolved target: " + targetUrl ); StringBuilder paramStr = new StringBuilder(); Enumeration paramNames = request.getParameterNames(); if (paramNames.hasMoreElements()) { paramStr.append("?"); } while (paramNames.hasMoreElements()) { String paramName = (String) paramNames.nextElement(); String paramValue = request.getParameter(paramName); paramStr.append(paramName); paramStr.append("="); paramStr.append(URLEncoder.encode(paramValue, "UTF-8")); if (paramNames.hasMoreElements()) { paramStr.append("&"); } } String urlStr = targetUri.toString() + paramStr.toString(); try { URL clientUrl = new URL(urlStr); // System.out.println( "Resolved query: " + clientUrl ); AuthenticatedURL.Token token = new AuthenticatedURL.Token(); KerberosAuthenticator authenticator = new KerberosAuthenticator(); auditor.audit(Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.UNAVAILABLE); HttpURLConnection conn = new AuthenticatedURL(authenticator).openConnection(clientUrl, token); // System.out.println( "STATUS=" + conn.getResponseCode() ); InputStream input = conn.getInputStream(); if (input != null) { OutputStream output = response.getOutputStream(); try { IOUtils.copy(input, output); } finally { output.flush(); input.close(); } } auditor.audit(Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.SUCCESS); } catch (AuthenticationException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); LOG.failedToEstablishConnectionToUrl(urlStr, e); auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.FAILURE, RES.responseStatus(HttpServletResponse.SC_UNAUTHORIZED)); } catch (FileNotFoundException e) { response.sendError(HttpServletResponse.SC_NOT_FOUND); LOG.failedToEstablishConnectionToUrl(urlStr, e); auditor.audit( Action.DISPATCH, urlStr, ResourceType.URI, ActionOutcome.FAILURE, RES.responseStatus(HttpServletResponse.SC_NOT_FOUND)); } } }
public class JettySSLService implements SSLService { private static final String EPHEMERAL_DH_KEY_SIZE_PROPERTY = "jdk.tls.ephemeralDHKeySize"; private static final String GATEWAY_TRUSTSTORE_PASSWORD = "******"; private static final String GATEWAY_CREDENTIAL_STORE_NAME = "__gateway"; private static GatewayMessages log = MessagesFactory.get(GatewayMessages.class); private MasterService ms; private KeystoreService ks; private AliasService as; private List<String> sslExcludeProtocols = null; private boolean clientAuthNeeded; private boolean trustAllCerts; private String truststorePath; private String keystoreType; private String trustStoreType; public void setMasterService(MasterService ms) { this.ms = ms; } public void setAliasService(AliasService as) { this.as = as; } public void setKeystoreService(KeystoreService ks) { this.ks = ks; } @Override public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException { // set any JSSE or security related system properties System.setProperty(EPHEMERAL_DH_KEY_SIZE_PROPERTY, config.getEphemeralDHKeySize()); try { if (!ks.isCredentialStoreForClusterAvailable(GATEWAY_CREDENTIAL_STORE_NAME)) { log.creatingCredentialStoreForGateway(); ks.createCredentialStoreForCluster(GATEWAY_CREDENTIAL_STORE_NAME); // LET'S NOT GENERATE A DIFFERENT KEY PASSPHRASE BY DEFAULT ANYMORE // IF A DEPLOYMENT WANTS TO CHANGE THE KEY PASSPHRASE TO MAKE IT MORE SECURE THEN // THEY CAN ADD THE ALIAS EXPLICITLY WITH THE CLI // as.generateAliasForCluster(GATEWAY_CREDENTIAL_STORE_NAME, GATEWAY_IDENTITY_PASSPHRASE); } else { log.credentialStoreForGatewayFoundNotCreating(); } } catch (KeystoreServiceException e) { throw new ServiceLifecycleException( "Keystore was not loaded properly - the provided (or persisted) master secret may not match the password for the keystore.", e); } try { if (!ks.isKeystoreForGatewayAvailable()) { log.creatingKeyStoreForGateway(); ks.createKeystoreForGateway(); char[] passphrase = null; try { passphrase = as.getGatewayIdentityPassphrase(); } catch (AliasServiceException e) { throw new ServiceLifecycleException( "Error accessing credential store for the gateway.", e); } if (passphrase == null) { passphrase = ms.getMasterSecret(); } ks.addSelfSignedCertForGateway("gateway-identity", passphrase); } else { log.keyStoreForGatewayFoundNotCreating(); } logAndValidateCertificate(); } catch (KeystoreServiceException e) { throw new ServiceLifecycleException( "Keystore was not loaded properly - the provided (or persisted) master secret may not match the password for the keystore.", e); } keystoreType = config.getKeystoreType(); sslExcludeProtocols = config.getExcludedSSLProtocols(); clientAuthNeeded = config.isClientAuthNeeded(); truststorePath = config.getTruststorePath(); trustAllCerts = config.getTrustAllCerts(); trustStoreType = config.getTruststoreType(); } private void logAndValidateCertificate() throws ServiceLifecycleException { // let's log the hostname (CN) and cert expiry from the gateway's public cert to aid in SSL // debugging Certificate cert; try { cert = as.getCertificateForGateway("gateway-identity"); } catch (AliasServiceException e) { throw new ServiceLifecycleException( "Cannot Retreive Gateway SSL Certificate. Server will not start.", e); } if (cert != null) { if (cert instanceof X509Certificate) { X500Principal x500Principal = ((X509Certificate) cert).getSubjectX500Principal(); X500PrincipalParser parser = new X500PrincipalParser(x500Principal); log.certificateHostNameForGateway(parser.getCN()); Date notBefore = ((X509Certificate) cert).getNotBefore(); Date notAfter = ((X509Certificate) cert).getNotAfter(); log.certificateValidityPeriod(notBefore, notAfter); // let's not even start if the current date is not within the validity period for the SSL // cert try { ((X509Certificate) cert).checkValidity(); } catch (CertificateExpiredException e) { throw new ServiceLifecycleException( "Gateway SSL Certificate is Expired. Server will not start.", e); } catch (CertificateNotYetValidException e) { throw new ServiceLifecycleException( "Gateway SSL Certificate is not yet valid. Server will not start.", e); } } else { throw new ServiceLifecycleException( "Public certificate for the gateway cannot be found with the alias gateway-identity. Plase check the identity certificate alias."); } } else { throw new ServiceLifecycleException( "Public certificate for the gateway is not of the expected type of X509Certificate. Something is wrong with the gateway keystore."); } } public Object buildSSlConnector(String keystoreFileName) { SslContextFactory sslContextFactory = new SslContextFactory(true); sslContextFactory.setCertAlias("gateway-identity"); sslContextFactory.setKeyStoreType(keystoreType); sslContextFactory.setKeyStorePath(keystoreFileName); char[] master = ms.getMasterSecret(); sslContextFactory.setKeyStorePassword(new String(master)); char[] keypass = null; try { keypass = as.getGatewayIdentityPassphrase(); } catch (AliasServiceException e) { // nop - default passphrase will be used } if (keypass == null) { // there has been no alias created for the key - let's assume it is the same as the keystore // password keypass = master; } sslContextFactory.setKeyManagerPassword(new String(keypass)); String truststorePassword = null; if (clientAuthNeeded) { if (truststorePath != null) { sslContextFactory.setTrustStore(truststorePath); char[] truststorePwd = null; try { truststorePwd = as.getPasswordFromAliasForGateway(GATEWAY_TRUSTSTORE_PASSWORD); } catch (AliasServiceException e) { // nop - master secret will be used } if (truststorePwd != null) { truststorePassword = new String(truststorePwd); } else { truststorePassword = new String(master); } sslContextFactory.setTrustStorePassword(truststorePassword); sslContextFactory.setTrustStoreType(trustStoreType); } else { // when clientAuthIsNeeded but no truststore provided // default to the server's keystore and details sslContextFactory.setTrustStore(keystoreFileName); sslContextFactory.setTrustStorePassword(new String(master)); sslContextFactory.setTrustStoreType(keystoreType); } } sslContextFactory.setNeedClientAuth(clientAuthNeeded); sslContextFactory.setTrustAll(trustAllCerts); if (sslExcludeProtocols != null) { sslContextFactory.setExcludeProtocols((String[]) sslExcludeProtocols.toArray()); } SslConnector sslConnector = new SslSelectChannelConnector(sslContextFactory); return sslConnector; } @Override public void start() throws ServiceLifecycleException { // TODO Auto-generated method stub } @Override public void stop() throws ServiceLifecycleException { // TODO Auto-generated method stub } }
public class IdentityAsserterHttpServletRequestWrapper extends HttpServletRequestWrapper { private static SpiGatewayMessages log = MessagesFactory.get(SpiGatewayMessages.class); private static final String PRINCIPAL_PARAM = "user.name"; private static final String DOAS_PRINCIPAL_PARAM = "doAs"; String username = null; public IdentityAsserterHttpServletRequestWrapper(HttpServletRequest request, String principal) { super(request); username = principal; } @Override public String getParameter(String name) { if (name.equals(PRINCIPAL_PARAM)) { return username; } return super.getParameter(name); } @SuppressWarnings("rawtypes") @Override public Map getParameterMap() { return getParams(); } @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Enumeration getParameterNames() { Map<String, String[]> params = getParams(); if (params == null) { params = new HashMap<String, String[]>(); } Enumeration<String> e = Collections.enumeration((Collection<String>) params.keySet()); return e; } @Override public String[] getParameterValues(String name) { Map<String, String[]> params = getParams(); if (params == null) { params = new HashMap<String, String[]>(); } return params.get(name); } private Map<String, String[]> getParams(String qString) { Map<String, String[]> params = null; if (getMethod().equals("GET")) { if (qString != null && qString.length() > 0) { params = HttpUtils.parseQueryString(qString); } else { params = new HashMap<String, String[]>(); } } else { if (qString == null || qString.length() == 0) { return null; } else { params = HttpUtils.parseQueryString(qString); } } return params; } private Map<String, String[]> getParams() { return getParams(super.getQueryString()); } @Override public String getQueryString() { String q = null; Map<String, String[]> params = getParams(); if (params == null) { params = new HashMap<String, String[]>(); } ArrayList<String> al = new ArrayList<String>(); al.add(username); String[] a = {""}; if ("true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED))) { params.put(DOAS_PRINCIPAL_PARAM, al.toArray(a)); params.remove(PRINCIPAL_PARAM); } else { params.put(PRINCIPAL_PARAM, al.toArray(a)); } String encoding = getCharacterEncoding(); if (encoding == null) { encoding = Charset.defaultCharset().name(); } q = urlEncode(params, encoding); return q; } @Override public int getContentLength() { int len; String contentType = getContentType(); // If the content type is a form we might rewrite the body so default it to -1. if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) { len = -1; } else { len = super.getContentLength(); } return len; } @Override public ServletInputStream getInputStream() throws java.io.IOException { String contentType = getContentType(); if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) { String encoding = getCharacterEncoding(); if (encoding == null) { encoding = Charset.defaultCharset().name(); } String body = IOUtils.toString(super.getInputStream(), encoding); Map<String, String[]> params = getParams(body); if (params == null) { params = new HashMap<String, String[]>(); } body = urlEncode(params, encoding); // ASCII is OK here because the urlEncode about should have already escaped return new ServletInputStreamWrapper(new ByteArrayInputStream(body.getBytes("US-ASCII"))); } else { return super.getInputStream(); } } static String urlEncode(String string, String encoding) { try { return URLEncoder.encode(string, encoding); } catch (UnsupportedEncodingException e) { throw new UnsupportedOperationException(e); } } public static String urlEncode(Map<String, String[]> map, String encoding) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String[]> entry : map.entrySet()) { String name = entry.getKey(); if (name != null && name.length() > 0) { String[] values = entry.getValue(); if (values == null || values.length == 0) { sb.append(entry.getKey()); } else { for (int i = 0; i < values.length; i++) { String value = values[i]; if (value != null) { if (sb.length() > 0) { sb.append("&"); } try { sb.append(urlEncode(name, encoding)); sb.append("="); sb.append(urlEncode(value, encoding)); } catch (IllegalArgumentException e) { log.skippingUnencodableParameter(name, value, encoding, e); } } } } } } return sb.toString(); } private class ServletInputStreamWrapper extends ServletInputStream { private InputStream stream; private ServletInputStreamWrapper(InputStream stream) { this.stream = stream; } @Override public int read() throws IOException { return stream.read(); } } }
public class JWTFederationFilter implements Filter { private static final String BEARER = "Bearer "; private static JWTMessages log = MessagesFactory.get(JWTMessages.class); private JWTokenAuthority authority = null; @Override public void init(FilterConfig filterConfig) throws ServletException { GatewayServices services = (GatewayServices) filterConfig .getServletContext() .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); authority = (JWTokenAuthority) services.getService(GatewayServices.TOKEN_SERVICE); } public void destroy() {} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String header = ((HttpServletRequest) request).getHeader("Authorization"); if (header != null && header.startsWith(BEARER)) { // what follows the bearer designator should be the JWT token being used to request or as an // access token String wireToken = header.substring(BEARER.length()); JWTToken token; try { token = JWTToken.parseToken(wireToken); } catch (ParseException e) { throw new ServletException( "ParseException encountered while processing the JWT token: ", e); } boolean verified = false; try { verified = authority.verifyToken(token); } catch (TokenServiceException e) { log.unableToVerifyToken(e); } if (verified) { // TODO: validate expiration // confirm that audience matches intended target - which for this filter must be HSSO if (token.getAudience().equals("HSSO")) { // TODO: verify that the user requesting access to the service/resource is authorized for // it - need scopes? Subject subject = createSubjectFromToken(token); continueWithEstablishedSecurityContext( subject, (HttpServletRequest) request, (HttpServletResponse) response, chain); } else { ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED); return; // break filter chain } } else { ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED); return; // break filter chain } } else { // no token provided in header // TODO: may have to check cookie and url as well before sending error ((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED); return; // break filter chain } } private void continueWithEstablishedSecurityContext( Subject subject, final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException { try { Subject.doAs( subject, new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { chain.doFilter(request, response); return null; } }); } catch (PrivilegedActionException e) { Throwable t = e.getCause(); if (t instanceof IOException) { throw (IOException) t; } else if (t instanceof ServletException) { throw (ServletException) t; } else { throw new ServletException(t); } } } private Subject createSubjectFromToken(JWTToken token) { final String principal = token.getPrincipal(); HashSet emptySet = new HashSet(); Set<Principal> principals = new HashSet<Principal>(); Principal p = new Principal() { @Override public String getName() { return principal; } }; principals.add(p); // The newly constructed Sets check whether this Subject has been set read-only // before permitting subsequent modifications. The newly created Sets also prevent // illegal modifications by ensuring that callers have sufficient permissions. // // To modify the Principals Set, the caller must have AuthPermission("modifyPrincipals"). // To modify the public credential Set, the caller must have // AuthPermission("modifyPublicCredentials"). // To modify the private credential Set, the caller must have // AuthPermission("modifyPrivateCredentials"). javax.security.auth.Subject subject = new javax.security.auth.Subject(true, principals, emptySet, emptySet); return subject; } }
public abstract class HtmlFilterReaderBase extends Reader implements UrlRewriteFilterReader { private static List<String> JSTYPES = Arrays.asList( new String[] { "application/javascript", "text/javascript", "*/javascript", "application/x-javascript", "text/x-javascript", "*/x-javascript" }); private static final String SCRIPTTAG = "script"; private static final UrlRewriteFilterPathDescriptor.Compiler<Pattern> REGEX_COMPILER = new RegexCompiler(); private static final UrlRewriteMessages LOG = MessagesFactory.get(UrlRewriteMessages.class); private Document document; private Stack<Level> stack; private Reader reader; private StreamedSource parser; private Iterator<Segment> iterator; private int lastSegEnd; private int offset; private StringWriter writer; private StringBuffer buffer; private UrlRewriteFilterContentDescriptor config = null; protected HtmlFilterReaderBase(Reader reader) throws IOException, ParserConfigurationException { this.reader = reader; document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); stack = new Stack<Level>(); parser = new StreamedSource(reader); iterator = parser.iterator(); writer = new StringWriter(); buffer = writer.getBuffer(); offset = 0; } protected HtmlFilterReaderBase(Reader reader, UrlRewriteFilterContentDescriptor config) throws IOException, ParserConfigurationException { this(reader); this.config = config; } protected abstract String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName); protected abstract String filterText(QName elementName, String text, String ruleName); @Override public int read(char[] destBuffer, int destOffset, int destCount) throws IOException { int count = 0; int available = buffer.length() - offset; if (available == 0) { if (iterator.hasNext()) { iterator.next(); processCurrentSegment(); available = buffer.length() - offset; } else { count = -1; } } if (available > 0) { count = Math.min(destCount, available); buffer.getChars(offset, offset + count, destBuffer, destOffset); offset += count; if (offset == buffer.length()) { offset = 0; buffer.setLength(0); } } return count; } private void processCurrentSegment() { Segment segment = parser.getCurrentSegment(); // If this tag is inside the previous tag (e.g. a server tag) then // ignore it as it was already output along with the previous tag. if (segment.getEnd() <= lastSegEnd) { return; } lastSegEnd = segment.getEnd(); if (segment instanceof Tag) { if (segment instanceof StartTag) { processStartTag((StartTag) segment); } else if (segment instanceof EndTag) { processEndTag((EndTag) segment); } else { writer.write(segment.toString()); } } else { processText(segment); } } private void processEndTag(EndTag tag) { while (!stack.isEmpty()) { Level popped = stack.pop(); if (popped.getTag().getName().equalsIgnoreCase(tag.getName())) { break; } } writer.write(tag.toString()); } private void processStartTag(StartTag tag) { if ("<".equals(tag.getTagType().getStartDelimiter())) { Element e = document.createElement(tag.getNameSegment().toString()); stack.push(new Level(tag)); writer.write("<"); writer.write(tag.getNameSegment().toString()); Attributes attributes = tag.getAttributes(); if (!attributes.isEmpty()) { for (Attribute attribute : attributes) { processAttribute(attribute); } } if (tag.toString().trim().endsWith("/>") || tag.isEmptyElementTag()) { stack.pop(); writer.write("/>"); } else { writer.write(">"); } } else { writer.write(tag.toString()); } } private void processAttribute(Attribute attribute) { writer.write(" "); writer.write(attribute.getName()); if (attribute.hasValue()) { String inputValue = attribute.getValue(); String outputValue = inputValue; try { Level tag = stack.peek(); outputValue = filterAttribute(tag.getQName(), tag.getQName(attribute.getName()), inputValue, null); if (outputValue == null) { outputValue = inputValue; } } catch (Exception e) { LOG.failedToFilterAttribute(attribute.getName(), e); } writer.write("="); writer.write(attribute.getQuoteChar()); writer.write(outputValue); writer.write(attribute.getQuoteChar()); } } private void processText(Segment segment) { String inputValue = segment.toString(); String outputValue = inputValue; try { if (stack.isEmpty()) { // This can happen for whitespace outside of the root element. // outputValue = filterText( null, inputValue ); } else { String tagType = stack.peek().getTag().getAttributeValue("type"); String tagName = stack.peek().getTag().getName(); if (SCRIPTTAG.equals(tagName) && JSTYPES.contains(tagType) && config != null && !config.getSelectors().isEmpty()) { // embedded javascript content outputValue = UrlRewriteUtil.filterJavaScript(inputValue, config, this, REGEX_COMPILER); } else { outputValue = filterText(stack.peek().getQName(), inputValue, null); } } if (outputValue == null) { outputValue = inputValue; } } catch (Exception e) { LOG.failedToFilterValue(inputValue, null, e); } writer.write(outputValue); } @Override public void close() throws IOException { parser.close(); reader.close(); writer.close(); stack.clear(); } private String getNamespace(String prefix) { String namespace = null; for (Level level : stack) { namespace = level.getNamespace(prefix); if (namespace != null) { break; } } return namespace; } private static class Level { private StartTag tag; private QName name; private Map<String, String> namespaces; private Level(StartTag tag) { this.tag = tag; this.name = null; this.namespaces = null; } private StartTag getTag() { return tag; } private QName getQName() { if (name == null) { name = getQName(tag.getName()); } return name; } private String getNamespace(String prefix) { return getNamespaces().get(prefix); } private QName getQName(String name) { String prefix; String local; int colon = (name == null ? -1 : name.indexOf(':')); if (colon < 0) { prefix = ""; local = name; } else { prefix = name.substring(0, colon); local = (colon + 1 < name.length() ? name.substring(colon + 1) : ""); } String namespace = (prefix == null) ? null : getNamespace(prefix); return new QName(namespace, local, prefix); } private Map<String, String> getNamespaces() { if (namespaces == null) { namespaces = new HashMap<String, String>(); parseNamespaces(); } return namespaces; } private void parseNamespaces() { Attributes attributes = tag.getAttributes(); if (attributes != null) { for (Attribute attribute : tag.getAttributes()) { String name = attribute.getName(); if (name.toLowerCase().startsWith("xmlns")) { int colon = name.indexOf(":", 5); String prefix; if (colon == 0) { prefix = ""; } else { prefix = name.substring(colon); } namespaces.put(prefix, attribute.getValue()); } } } } } }
public class UrlRewriteServletContextListener implements ServletContextListener { public static final String PROCESSOR_ATTRIBUTE_NAME = UrlRewriteProcessor.class.getName(); public static final String DESCRIPTOR_LOCATION_INIT_PARAM_NAME = "rewriteDescriptorLocation"; public static final String DESCRIPTOR_DEFAULT_FILE_NAME = "rewrite.xml"; public static final String DESCRIPTOR_DEFAULT_LOCATION = "/WEB-INF/" + DESCRIPTOR_DEFAULT_FILE_NAME; private static final UrlRewriteMessages LOG = MessagesFactory.get(UrlRewriteMessages.class); @Override public void contextInitialized(ServletContextEvent event) { UrlRewriteRulesDescriptor descriptor = null; try { URL url = locateDescriptor(event.getServletContext()); descriptor = loadDescriptor(url); } catch (IOException e) { throw new IllegalStateException(e); } ServletContext context = event.getServletContext(); UrlRewriteEnvironment environment = new UrlRewriteServletEnvironment(context); UrlRewriteProcessor processor = new UrlRewriteProcessor(); processor.initialize(environment, descriptor); event.getServletContext().setAttribute(PROCESSOR_ATTRIBUTE_NAME, processor); } @Override public void contextDestroyed(ServletContextEvent event) { UrlRewriteProcessor processor = (UrlRewriteProcessor) event.getServletContext().getAttribute(PROCESSOR_ATTRIBUTE_NAME); event.getServletContext().removeAttribute(PROCESSOR_ATTRIBUTE_NAME); if (processor != null) { processor.destroy(); } } public static UrlRewriter getUrlRewriter(ServletContext context) { return ((UrlRewriteProcessor) context.getAttribute(PROCESSOR_ATTRIBUTE_NAME)); } private static URL locateDescriptor(ServletContext context) throws IOException { String param = context.getInitParameter(DESCRIPTOR_LOCATION_INIT_PARAM_NAME); if (param == null) { param = DESCRIPTOR_DEFAULT_LOCATION; } URL url; try { url = context.getResource(param); } catch (MalformedURLException e) { // Ignore it and try using the value directly as a URL. url = null; } if (url == null) { url = new URL(param); } if (url == null) { throw new FileNotFoundException(param); } return url; } private static UrlRewriteRulesDescriptor loadDescriptor(URL url) throws IOException { InputStream stream = url.openStream(); Reader reader = new InputStreamReader(stream, "UTF-8"); UrlRewriteRulesDescriptor descriptor = UrlRewriteRulesDescriptorFactory.load("xml", reader); try { reader.close(); } catch (IOException closeException) { LOG.failedToLoadRewriteRulesDescriptor(closeException); } return descriptor; } }
public class UrlRewriteRequest extends GatewayRequestWrapper implements Resolver { private static final UrlRewriteMessages LOG = MessagesFactory.get(UrlRewriteMessages.class); private UrlRewriter rewriter; private String urlRuleName; private String bodyFilterName; private String headersFilterName; private UrlRewriteFilterContentDescriptor headersFilterConfig; private String cookiesFilterName; private UrlRewriteFilterContentDescriptor cookiesFilterConfig; /** * Constructs a request object wrapping the given request. * * @throws IllegalArgumentException if the request is null */ public UrlRewriteRequest(FilterConfig config, HttpServletRequest request) throws IOException { super(request); this.rewriter = UrlRewriteServletContextListener.getUrlRewriter(config.getServletContext()); this.urlRuleName = config.getInitParameter(UrlRewriteServletFilter.REQUEST_URL_RULE_PARAM); this.bodyFilterName = config.getInitParameter(UrlRewriteServletFilter.REQUEST_BODY_FILTER_PARAM); this.headersFilterName = config.getInitParameter(UrlRewriteServletFilter.REQUEST_HEADERS_FILTER_PARAM); this.headersFilterConfig = getRewriteFilterConfig(headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE); this.cookiesFilterName = config.getInitParameter(UrlRewriteServletFilter.REQUEST_COOKIES_FILTER_PARAM); this.cookiesFilterConfig = getRewriteFilterConfig(cookiesFilterName, UrlRewriteServletFilter.COOKIES_MIME_TYPE); } private Template getSourceUrl() { Template urlTemplate = null; StringBuffer urlString = super.getRequestURL(); String queryString = super.getQueryString(); if (queryString != null) { urlString.append('?'); urlString.append(queryString); } try { urlTemplate = Parser.parse(urlString.toString()); } catch (URISyntaxException e) { LOG.failedToParseValueForUrlRewrite(urlString.toString()); // Shouldn't be possible given that the URL is constructed from parts of an existing URL. urlTemplate = null; } return urlTemplate; } // Note: Source url was added to the request attributes by the GatewayFilter doFilter method. private Template getTargetUrl() { boolean rewriteRequestUrl = true; Template targetUrl; if (rewriteRequestUrl) { targetUrl = (Template) getAttribute(AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME); if (targetUrl == null) { Template sourceUrl = getSourceUrl(); targetUrl = rewriter.rewrite(this, sourceUrl, UrlRewriter.Direction.IN, urlRuleName); setAttribute(AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME, targetUrl); } } else { targetUrl = (Template) getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME); } return targetUrl; } private String[] splitTargetUrl(Template url) { String s = url.toString(); return s.split("\\?"); } @Override public StringBuffer getRequestURL() { return new StringBuffer(getRequestURI()); } // TODO: I think this method is implemented wrong based on the HttpServletRequest.getRequestURI // docs. // It should not include the scheme or authority parts. @Override public String getRequestURI() { String[] split = splitTargetUrl(getTargetUrl()); if (split.length > 0) { return split[0]; } else { return ""; } } @Override public String getQueryString() { String[] split = splitTargetUrl(getTargetUrl()); if (split.length > 1) { return split[1]; } else { return null; } } private String rewriteValue(UrlRewriter rewriter, String value, String rule) { try { Template input = Parser.parse(value); Template output = rewriter.rewrite(this, input, UrlRewriter.Direction.IN, rule); value = output.toString(); } catch (URISyntaxException e) { LOG.failedToParseValueForUrlRewrite(value); } return value; } @Override public String getHeader(String name) { String value = super.getHeader(name); if (value != null) { value = rewriteValue( rewriter, super.getHeader(name), pickFirstRuleWithEqualsIgnoreCasePathMatch(headersFilterConfig, name)); } return value; } @SuppressWarnings("unchecked") public Enumeration getHeaders(String name) { return new EnumerationRewriter( rewriter, super.getHeaders(name), pickFirstRuleWithEqualsIgnoreCasePathMatch(headersFilterConfig, name)); } @Override public List<String> resolve(String name) { return Collections.emptyList(); } private class EnumerationRewriter implements Enumeration<String> { private UrlRewriter rewriter; private Enumeration<String> delegate; private String rule; private EnumerationRewriter(UrlRewriter rewriter, Enumeration<String> delegate, String rule) { this.rewriter = rewriter; this.delegate = delegate; this.rule = rule; } @Override public boolean hasMoreElements() { return delegate.hasMoreElements(); } @Override public String nextElement() { return rewriteValue(rewriter, delegate.nextElement(), rule); } } @Override public ServletInputStream getInputStream() throws IOException { MimeType mimeType = getMimeType(); UrlRewriteFilterContentDescriptor filterContentConfig = getRewriteFilterConfig(bodyFilterName, mimeType); InputStream stream = UrlRewriteStreamFilterFactory.create( mimeType, null, super.getInputStream(), rewriter, this, UrlRewriter.Direction.IN, filterContentConfig); return new UrlRewriteRequestStream(stream); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding())); } @Override public int getContentLength() { // The rewrite might change the content length so return the default of -1 to indicate the // length is unknown. return -1; } private UrlRewriteFilterContentDescriptor getRewriteFilterConfig( String filterName, MimeType mimeType) { UrlRewriteFilterContentDescriptor filterContentConfig = null; UrlRewriteRulesDescriptor rewriteConfig = rewriter.getConfig(); if (rewriteConfig != null) { UrlRewriteFilterDescriptor filterConfig = rewriteConfig.getFilter(filterName); if (filterConfig != null) { filterContentConfig = filterConfig.getContent(mimeType); } } return filterContentConfig; } }