/* * Copyright 2011 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitblit.utils; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.slf4j.LoggerFactory; import com.gitblit.models.UserModel; import com.gitblit.utils.X509Utils.X509Metadata; /** * Collection of utility methods for http requests. * * @author James Moger * */ public class HttpUtils { /** * Returns the Gitblit URL based on the request. * * @param request * @return the host url */ public static String getGitblitURL(HttpServletRequest request) { // default to the request scheme and port String scheme = request.getScheme(); int port = request.getServerPort(); // try to use reverse-proxy server's port String forwardedPort = request.getHeader("X-Forwarded-Port"); if (StringUtils.isEmpty(forwardedPort)) { forwardedPort = request.getHeader("X_Forwarded_Port"); } if (!StringUtils.isEmpty(forwardedPort)) { // reverse-proxy server has supplied the original port try { port = Integer.parseInt(forwardedPort); } catch (Throwable t) { } } // try to use reverse-proxy server's scheme String forwardedScheme = request.getHeader("X-Forwarded-Proto"); if (StringUtils.isEmpty(forwardedScheme)) { forwardedScheme = request.getHeader("X_Forwarded_Proto"); } if (!StringUtils.isEmpty(forwardedScheme)) { // reverse-proxy server has supplied the original scheme scheme = forwardedScheme; if ("https".equals(scheme) && port == 80) { // proxy server is https, inside server is 80 // this is likely because the proxy server has not supplied // x-forwarded-port. since 80 is almost definitely wrong, // make an educated guess that 443 is correct. port = 443; } } // try to use reverse-proxy's context String context = request.getContextPath(); String forwardedContext = request.getHeader("X-Forwarded-Context"); if (StringUtils.isEmpty(forwardedContext)) { forwardedContext = request.getHeader("X_Forwarded_Context"); } if (!StringUtils.isEmpty(forwardedContext)) { context = forwardedContext; } // trim any trailing slash if (context.length() > 0 && context.charAt(context.length() - 1) == '/') { context = context.substring(1); } // try to use reverse-proxy's hostname String host = request.getServerName(); String forwardedHost = request.getHeader("X-Forwarded-Host"); if (StringUtils.isEmpty(forwardedHost)) { forwardedHost = request.getHeader("X_Forwarded_Host"); } if (!StringUtils.isEmpty(forwardedHost)) { host = forwardedHost; } // build result StringBuilder sb = new StringBuilder(); sb.append(scheme); sb.append("://"); sb.append(host); if (("http".equals(scheme) && port != 80) || ("https".equals(scheme) && port != 443)) { if (!host.endsWith(":" + port)) { sb.append(":").append(port); } } sb.append(context); return sb.toString(); } /** * Returns a user model object built from attributes in the SSL certificate. * This model is not retrieved from the user service. * * @param httpRequest * @param checkValidity ensure certificate can be used now * @param usernameOIDs if unspecified, CN is used as the username * @return a UserModel, if a valid certificate is in the request, null otherwise */ public static UserModel getUserModelFromCertificate(HttpServletRequest httpRequest, boolean checkValidity, String... usernameOIDs) { if (httpRequest.getAttribute("javax.servlet.request.X509Certificate") != null) { X509Certificate[] certChain = (X509Certificate[]) httpRequest .getAttribute("javax.servlet.request.X509Certificate"); if (certChain != null) { X509Certificate cert = certChain[0]; // ensure certificate is valid if (checkValidity) { try { cert.checkValidity(new Date()); } catch (CertificateNotYetValidException e) { LoggerFactory.getLogger(HttpUtils.class).info(MessageFormat.format("X509 certificate {0} is not yet valid", cert.getSubjectDN().getName())); return null; } catch (CertificateExpiredException e) { LoggerFactory.getLogger(HttpUtils.class).info(MessageFormat.format("X509 certificate {0} has expired", cert.getSubjectDN().getName())); return null; } } return getUserModelFromCertificate(cert, usernameOIDs); } } return null; } /** * Creates a UserModel from a certificate * @param cert * @param usernameOids if unspecified CN is used as the username * @return */ public static UserModel getUserModelFromCertificate(X509Certificate cert, String... usernameOIDs) { X509Metadata metadata = X509Utils.getMetadata(cert); UserModel user = new UserModel(metadata.commonName); user.emailAddress = metadata.emailAddress; user.isAuthenticated = false; if (usernameOIDs == null || usernameOIDs.length == 0) { // use default usename<->CN mapping usernameOIDs = new String [] { "CN" }; } // determine username from OID fingerprint StringBuilder an = new StringBuilder(); for (String oid : usernameOIDs) { String val = metadata.getOID(oid.toUpperCase(), null); if (val != null) { an.append(val).append(' '); } } user.username = an.toString().trim(); return user; } public static X509Metadata getCertificateMetadata(HttpServletRequest httpRequest) { if (httpRequest.getAttribute("javax.servlet.request.X509Certificate") != null) { X509Certificate[] certChain = (X509Certificate[]) httpRequest .getAttribute("javax.servlet.request.X509Certificate"); if (certChain != null) { X509Certificate cert = certChain[0]; return X509Utils.getMetadata(cert); } } return null; } public static boolean isIpAddress(String address) { if (StringUtils.isEmpty(address)) { return false; } String [] fields = address.split("\\."); if (fields.length == 4) { // IPV4 for (String field : fields) { try { int value = Integer.parseInt(field); if (value < 0 || value > 255) { return false; } } catch (Exception e) { return false; } } return true; } // TODO IPV6? return false; } }