Merge branch 'master' of github.com:Jasig/java-cas-client
This commit is contained in:
commit
826432d3f7
|
|
@ -30,7 +30,6 @@ import java.util.Map;
|
|||
* Concrete implementation of the AttributePrincipal interface.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AttributePrincipalImpl extends SimplePrincipal implements AttributePrincipal {
|
||||
|
|
@ -80,7 +79,7 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a new principal witht he supplied name, attributes, and proxying capabilities.
|
||||
* Constructs a new principal with the supplied name, attributes, and proxying capabilities.
|
||||
*
|
||||
* @param name the unique identifier for the principal.
|
||||
* @param attributes the key/value pairs for this principal.
|
||||
|
|
|
|||
|
|
@ -18,33 +18,35 @@
|
|||
*/
|
||||
package org.jasig.cas.client.proxy;
|
||||
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Implementation of a ProxyRetriever that follows the CAS 2.0 specification.
|
||||
* For more information on the CAS 2.0 specification, please see the <a
|
||||
* href="http://www.ja-sig.org/products/cas/overview/protocol/index.html">specification
|
||||
* href="http://www.jasig.org/cas/protocol">specification
|
||||
* document</a>.
|
||||
* <p/>
|
||||
* In general, this class will make a call to the CAS server with some specified
|
||||
* parameters and receive an XML response to parse.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class Cas20ProxyRetriever implements ProxyRetriever {
|
||||
|
||||
/** Unique Id for serialization. */
|
||||
private static final long serialVersionUID = 560409469568911791L;
|
||||
private static final long serialVersionUID = 560409469568911791L;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Instance of Commons Logging.
|
||||
*/
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
|
@ -56,39 +58,45 @@ public final class Cas20ProxyRetriever implements ProxyRetriever {
|
|||
|
||||
private final String encoding;
|
||||
|
||||
/** Url connection factory to use when communicating with the server **/
|
||||
private final HttpURLConnectionFactory urlConnectionFactory;
|
||||
|
||||
/**
|
||||
* Main Constructor.
|
||||
*
|
||||
* @param casServerUrl the URL to the CAS server (i.e. http://localhost/cas/)
|
||||
* @param encoding the encoding to use.
|
||||
* @param urlFactory url connection factory use when retrieving proxy responses from the server
|
||||
*/
|
||||
public Cas20ProxyRetriever(final String casServerUrl, final String encoding) {
|
||||
public Cas20ProxyRetriever(final String casServerUrl, final String encoding, final HttpURLConnectionFactory urlFactory) {
|
||||
CommonUtils.assertNotNull(casServerUrl, "casServerUrl cannot be null.");
|
||||
this.casServerUrl = casServerUrl;
|
||||
this.encoding = encoding;
|
||||
this.urlConnectionFactory = urlFactory;
|
||||
}
|
||||
|
||||
public String getProxyTicketIdFor(final String proxyGrantingTicketId,
|
||||
final String targetService) {
|
||||
|
||||
final String url = constructUrl(proxyGrantingTicketId, targetService);
|
||||
final String response = CommonUtils.getResponseFromServer(url, this.encoding);
|
||||
|
||||
public String getProxyTicketIdFor(final String proxyGrantingTicketId, final String targetService) {
|
||||
CommonUtils.assertNotNull(proxyGrantingTicketId, "proxyGrantingTicketId cannot be null.");
|
||||
CommonUtils.assertNotNull(targetService, "targetService cannot be null.");
|
||||
|
||||
final URL url = constructUrl(proxyGrantingTicketId, targetService);
|
||||
final String response = CommonUtils.getResponseFromServer(url, this.urlConnectionFactory, this.encoding);
|
||||
final String error = XmlUtils.getTextForElement(response, "proxyFailure");
|
||||
|
||||
|
||||
if (CommonUtils.isNotEmpty(error)) {
|
||||
logger.debug(error);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return XmlUtils.getTextForElement(response, "proxyTicket");
|
||||
}
|
||||
|
||||
private String constructUrl(final String proxyGrantingTicketId, final String targetService) {
|
||||
private URL constructUrl(final String proxyGrantingTicketId, final String targetService) {
|
||||
try {
|
||||
return this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt="
|
||||
+ proxyGrantingTicketId + "&targetService="
|
||||
+ URLEncoder.encode(targetService, "UTF-8");
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
return new URL(this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy"
|
||||
+ "?pgt=" + proxyGrantingTicketId
|
||||
+ "&targetService=" + URLEncoder.encode(targetService, "UTF-8"));
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you 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 the following location:
|
||||
*
|
||||
* 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 org.jasig.cas.client.ssl;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* A factory to prepare and configure {@link java.net.URLConnection} instances.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3
|
||||
*/
|
||||
public interface HttpURLConnectionFactory {
|
||||
|
||||
/**
|
||||
* Receives a {@link URLConnection} instance typically as a result of a {@link URL}
|
||||
* opening a connection to a remote resource. The received url connection is then
|
||||
* configured and prepared appropriately depending on its type and is then returned to the caller
|
||||
* to accommodate method chaining.
|
||||
*
|
||||
* @param url The url connection that needs to be configured
|
||||
* @return The configured {@link HttpURLConnection} instance
|
||||
*
|
||||
* @see {@link HttpsURLConnectionFactory}
|
||||
*/
|
||||
HttpURLConnection buildHttpURLConnection(final URLConnection url);
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package org.jasig.cas.client.ssl;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URLConnection;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An implementation of the {@link HttpURLConnectionFactory} whose responsible to configure
|
||||
* the underlying <i>https</i> connection, if needed, with a given hostname and SSL socket factory based on the
|
||||
* configuration provided.
|
||||
*
|
||||
* @author Misagh Moayyed
|
||||
* @since 3.3
|
||||
* @see #setHostnameVerifier(HostnameVerifier)
|
||||
* @see #setSSLConfiguration(Properties)
|
||||
*/
|
||||
public final class HttpsURLConnectionFactory implements HttpURLConnectionFactory {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpsURLConnectionFactory.class);
|
||||
|
||||
/**
|
||||
* Hostname verifier used when making an SSL request to the CAS server.
|
||||
* Defaults to {@link HttpsURLConnection#getDefaultHostnameVerifier()}
|
||||
*/
|
||||
private HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
|
||||
/**
|
||||
* Properties file that can contains key/trust info for Client Side Certificates
|
||||
*/
|
||||
private Properties sslConfiguration = new Properties();
|
||||
|
||||
public HttpsURLConnectionFactory() {}
|
||||
|
||||
public HttpsURLConnectionFactory(final HostnameVerifier verifier, final Properties config) {
|
||||
setHostnameVerifier(verifier);
|
||||
setSSLConfiguration(config);
|
||||
}
|
||||
|
||||
public final void setSSLConfiguration(final Properties config) {
|
||||
this.sslConfiguration = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the host name verifier for the https connection received.
|
||||
*
|
||||
* @see AnyHostnameVerifier
|
||||
* @see RegexHostnameVerifier
|
||||
* @see WhitelistHostnameVerifier
|
||||
*/
|
||||
public final void setHostnameVerifier(final HostnameVerifier verifier) {
|
||||
this.hostnameVerifier = verifier;
|
||||
}
|
||||
|
||||
public HttpURLConnection buildHttpURLConnection(final URLConnection url) {
|
||||
return this.configureHttpsConnectionIfNeeded(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the connection with specific settings for secure http connections
|
||||
* If the connection instance is not a {@link HttpsURLConnection},
|
||||
* no additional changes will be made and the connection itself is simply returned.
|
||||
*
|
||||
* @param conn the http connection
|
||||
*/
|
||||
private HttpURLConnection configureHttpsConnectionIfNeeded(final URLConnection conn) {
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
final HttpsURLConnection httpsConnection = (HttpsURLConnection) conn;
|
||||
final SSLSocketFactory socketFactory = this.createSSLSocketFactory();
|
||||
if (socketFactory != null) {
|
||||
httpsConnection.setSSLSocketFactory(socketFactory);
|
||||
}
|
||||
|
||||
if (this.hostnameVerifier != null) {
|
||||
httpsConnection.setHostnameVerifier(this.hostnameVerifier);
|
||||
}
|
||||
}
|
||||
return (HttpURLConnection) conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SSLSocketFactory} based on the configuration specified
|
||||
* <p>
|
||||
* Sample properties file:
|
||||
* <pre>
|
||||
* protocol=TLS
|
||||
* keyStoreType=JKS
|
||||
* keyStorePath=/var/secure/location/.keystore
|
||||
* keyStorePass=changeit
|
||||
* certificatePassword=aGoodPass
|
||||
* </pre>
|
||||
* @return the {@link SSLSocketFactory}
|
||||
*/
|
||||
private SSLSocketFactory createSSLSocketFactory() {
|
||||
InputStream keyStoreIS = null;
|
||||
try {
|
||||
final SSLContext sslContext = SSLContext.getInstance(this.sslConfiguration.getProperty("protocol", "SSL"));
|
||||
|
||||
if (this.sslConfiguration.getProperty("keyStoreType") != null) {
|
||||
final KeyStore keyStore = KeyStore.getInstance(this.sslConfiguration.getProperty("keyStoreType"));
|
||||
if (this.sslConfiguration.getProperty("keyStorePath") != null) {
|
||||
keyStoreIS = new FileInputStream(this.sslConfiguration.getProperty("keyStorePath"));
|
||||
if (this.sslConfiguration.getProperty("keyStorePass") != null) {
|
||||
keyStore.load(keyStoreIS, this.sslConfiguration.getProperty("keyStorePass").toCharArray());
|
||||
LOGGER.debug("Keystore has {} keys", keyStore.size());
|
||||
final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(this.sslConfiguration.getProperty("keyManagerType", "SunX509"));
|
||||
keyManager.init(keyStore, this.sslConfiguration.getProperty("certificatePassword").toCharArray());
|
||||
sslContext.init(keyManager.getKeyManagers(), null, null);
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
LOGGER.error(e.getMessage(), e);
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(keyStoreIS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -67,6 +67,10 @@ public abstract class AbstractConfigurationFilter implements Filter {
|
|||
final String value = filterConfig.getInitParameter(propertyName);
|
||||
|
||||
if (CommonUtils.isNotBlank(value)) {
|
||||
if ("renew".equals(propertyName)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Renew MUST be specified via context parameter or JNDI environment to avoid misconfiguration.");
|
||||
}
|
||||
logger.info("Property [{}] loaded from FilterConfig.getInitParameter with value [{}]", propertyName, value);
|
||||
return value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,25 +19,27 @@
|
|||
package org.jasig.cas.client.util;
|
||||
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.validation.ProxyList;
|
||||
import org.jasig.cas.client.validation.ProxyListEditor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
|
@ -323,28 +325,16 @@ public final class CommonUtils {
|
|||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param constructedUrl the url to contact.
|
||||
* @param factory connection factory to prepare the URL connection instance
|
||||
* @param encoding the encoding to use.
|
||||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final URL constructedUrl, final String encoding) {
|
||||
return getResponseFromServer(constructedUrl, HttpsURLConnection.getDefaultHostnameVerifier(), encoding);
|
||||
}
|
||||
public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory, final String encoding) {
|
||||
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param constructedUrl the url to contact.
|
||||
* @param hostnameVerifier Host name verifier to use for HTTPS connections.
|
||||
* @param encoding the encoding to use.
|
||||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final URL constructedUrl, final HostnameVerifier hostnameVerifier, final String encoding) {
|
||||
URLConnection conn = null;
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
conn = constructedUrl.openConnection();
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
|
||||
}
|
||||
conn = factory.buildHttpURLConnection(constructedUrl.openConnection());
|
||||
|
||||
final BufferedReader in;
|
||||
|
||||
if (CommonUtils.isEmpty(encoding)) {
|
||||
|
|
@ -365,27 +355,12 @@ public final class CommonUtils {
|
|||
LOGGER.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (conn != null && conn instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)conn).disconnect();
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Contacts the remote URL and returns the response.
|
||||
*
|
||||
* @param url the url to contact.
|
||||
* @param encoding the encoding to use.
|
||||
* @return the response.
|
||||
*/
|
||||
public static String getResponseFromServer(final String url, String encoding) {
|
||||
try {
|
||||
return getResponseFromServer(new URL(url), encoding);
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ProxyList createProxyList(final String proxies) {
|
||||
if (CommonUtils.isBlank(proxies)) {
|
||||
return new ProxyList();
|
||||
|
|
@ -410,4 +385,19 @@ public final class CommonUtils {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally close a {@link Closeable}. Equivalent to {@link java.io.Closeable#close()}close(), except any exceptions
|
||||
* will be ignored. This is typically used in finally blocks.
|
||||
* @param resource
|
||||
*/
|
||||
public static void closeQuietly(final Closeable resource) {
|
||||
try {
|
||||
if (resource != null) {
|
||||
resource.close();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,10 +43,6 @@ public abstract class AbstractCasProtocolUrlBasedTicketValidator extends Abstrac
|
|||
* Retrieves the response from the server by opening a connection and merely reading the response.
|
||||
*/
|
||||
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
|
||||
if (this.hostnameVerifier != null) {
|
||||
return CommonUtils.getResponseFromServer(validationUrl, this.hostnameVerifier, getEncoding());
|
||||
} else {
|
||||
return CommonUtils.getResponseFromServer(validationUrl, getEncoding());
|
||||
}
|
||||
return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The filter that handles all the work of validating ticket requests.
|
||||
|
|
@ -81,6 +83,31 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
|
|||
return this.ticketValidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ssl config to use for HTTPS connections
|
||||
* if one is configured for this filter.
|
||||
* @param filterConfig Servlet filter configuration.
|
||||
* @return Properties that can contains key/trust info for Client Side Certificates
|
||||
*/
|
||||
protected Properties getSSLConfig(final FilterConfig filterConfig) {
|
||||
final Properties properties = new Properties();
|
||||
final String fileName = getPropertyFromInitParams(filterConfig, "sslConfigFile", null);
|
||||
|
||||
if (fileName != null) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(fileName);
|
||||
properties.load(fis);
|
||||
logger.trace("Loaded {} entries from {}", properties.size(), fileName);
|
||||
} catch(final IOException ioe) {
|
||||
logger.error(ioe.getMessage(), ioe);
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(fis);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured {@link HostnameVerifier} to use for HTTPS connections
|
||||
* if one is configured for this filter.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -29,24 +31,22 @@ import java.net.URLEncoder;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
|
||||
/**
|
||||
* Abstract validator implementation for tickets that must be validated against a server.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractUrlBasedTicketValidator implements TicketValidator {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
||||
/**
|
||||
* Hostname verifier used when making an SSL request to the CAS server.
|
||||
* URLConnection factory instance to use when making validation requests to the CAS server.
|
||||
* Defaults to {@link HttpsURLConnectionFactory}
|
||||
*/
|
||||
protected HostnameVerifier hostnameVerifier;
|
||||
|
||||
private HttpURLConnectionFactory urlConnectionFactory = new HttpsURLConnectionFactory();
|
||||
|
||||
/**
|
||||
* Prefix for the CAS server. Should be everything up to the url endpoint, including the /.
|
||||
*
|
||||
|
|
@ -217,10 +217,6 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
public final void setCustomParameters(final Map<String,String> customParameters) {
|
||||
this.customParameters = customParameters;
|
||||
}
|
||||
|
||||
public final void setHostnameVerifier(final HostnameVerifier verifier) {
|
||||
this.hostnameVerifier = verifier;
|
||||
}
|
||||
|
||||
public final void setEncoding(final String encoding) {
|
||||
this.encoding = encoding;
|
||||
|
|
@ -241,4 +237,12 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
|
|||
protected final Map<String, String> getCustomParameters() {
|
||||
return this.customParameters;
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpURLConnectionFactory getURLConnectionFactory() {
|
||||
return this.urlConnectionFactory;
|
||||
}
|
||||
|
||||
public void setURLConnectionFactory(final HttpURLConnectionFactory urlConnectionFactory) {
|
||||
this.urlConnectionFactory = urlConnectionFactory;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ package org.jasig.cas.client.validation;
|
|||
|
||||
import javax.servlet.FilterConfig;
|
||||
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
|
||||
/**
|
||||
* Implementation of AbstractTicketValidatorFilter that instanciates a Cas10TicketValidator.
|
||||
* <p>Deployers can provide the "casServerPrefix" and the "renew" attributes via the standard context or filter init
|
||||
|
|
@ -35,7 +38,9 @@ public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter
|
|||
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
|
||||
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
|
||||
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
|
||||
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig), getSSLConfig(filterConfig));
|
||||
validator.setURLConnectionFactory(factory);
|
||||
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
|
||||
|
||||
return validator;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.proxy.*;
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
|
||||
|
|
@ -143,7 +145,11 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
}
|
||||
validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
|
||||
validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
|
||||
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getPropertyFromInitParams(filterConfig, "encoding", null)));
|
||||
|
||||
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig), getSSLConfig(filterConfig));
|
||||
validator.setURLConnectionFactory(factory);
|
||||
|
||||
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getPropertyFromInitParams(filterConfig, "encoding", null), factory));
|
||||
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
|
||||
|
||||
|
|
@ -159,8 +165,6 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
|
|||
}
|
||||
|
||||
validator.setCustomParameters(additionalParameters);
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
|||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.XmlUtils;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
|
@ -41,7 +40,6 @@ import java.util.*;
|
|||
* Implementation of the TicketValidator that will validate Service Tickets in compliance with the CAS 2.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTicketValidator {
|
||||
|
|
@ -60,10 +58,11 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
|
|||
* CAS server url prefix.
|
||||
*
|
||||
* @param casServerUrlPrefix the CAS Server URL prefix.
|
||||
* @param urlFactory URL connection factory to use when communicating with the server
|
||||
*/
|
||||
public Cas20ServiceTicketValidator(final String casServerUrlPrefix) {
|
||||
super(casServerUrlPrefix);
|
||||
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding());
|
||||
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding(), getURLConnectionFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ package org.jasig.cas.client.validation;
|
|||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
|
||||
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
|
||||
|
||||
/**
|
||||
* Implementation of TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator.
|
||||
* <p>
|
||||
|
|
@ -53,7 +56,10 @@ public class Saml11TicketValidationFilter extends AbstractTicketValidationFilter
|
|||
final String tolerance = getPropertyFromInitParams(filterConfig, "tolerance", "1000");
|
||||
validator.setTolerance(Long.parseLong(tolerance));
|
||||
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
|
||||
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
|
||||
|
||||
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig), getSSLConfig(filterConfig));
|
||||
validator.setURLConnectionFactory(factory);
|
||||
|
||||
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
|
||||
validator.setDisableXmlSchemaValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "disableXmlSchemaValidation", "false")));
|
||||
return validator;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.opensaml.xml.io.UnmarshallingException;
|
|||
import org.opensaml.xml.parse.BasicParserPool;
|
||||
import org.opensaml.xml.parse.XMLParserException;
|
||||
import org.opensaml.xml.schema.XSAny;
|
||||
import org.opensaml.xml.schema.XSString;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
|
@ -45,13 +46,10 @@ import java.net.URL;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator {
|
||||
|
|
@ -220,6 +218,8 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
|
|||
for (final Object o : attribute.getAttributeValues()) {
|
||||
if (o instanceof XSAny) {
|
||||
list.add(((XSAny) o).getTextContent());
|
||||
} else if (o instanceof XSString) {
|
||||
list.add(((XSString) o).getValue());
|
||||
} else {
|
||||
list.add(o.toString());
|
||||
}
|
||||
|
|
@ -232,12 +232,11 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
|
|||
+ "<samlp:AssertionArtifact>" + ticket
|
||||
+ "</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
|
||||
HttpURLConnection conn = null;
|
||||
|
||||
DataOutputStream out = null;
|
||||
BufferedReader in = null;
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection) validationUrl.openConnection();
|
||||
if (this.hostnameVerifier != null && conn instanceof HttpsURLConnection) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(this.hostnameVerifier);
|
||||
}
|
||||
conn = this.getURLConnectionFactory().buildHttpURLConnection(validationUrl.openConnection());
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "text/xml");
|
||||
conn.setRequestProperty("Content-Length", Integer.toString(MESSAGE_TO_SEND.length()));
|
||||
|
|
@ -246,12 +245,11 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
|
|||
conn.setDoInput(true);
|
||||
conn.setDoOutput(true);
|
||||
|
||||
final DataOutputStream out = new DataOutputStream(conn.getOutputStream());
|
||||
out = new DataOutputStream(conn.getOutputStream());
|
||||
out.writeBytes(MESSAGE_TO_SEND);
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
final BufferedReader in = new BufferedReader(CommonUtils.isNotBlank(getEncoding()) ? new InputStreamReader(conn.getInputStream(), Charset.forName(getEncoding())) : new InputStreamReader(conn.getInputStream()));
|
||||
|
||||
in = new BufferedReader(CommonUtils.isNotBlank(getEncoding()) ? new InputStreamReader(conn.getInputStream(), Charset.forName(getEncoding())) : new InputStreamReader(conn.getInputStream()));
|
||||
final StringBuilder buffer = new StringBuilder(256);
|
||||
|
||||
String line;
|
||||
|
|
@ -263,6 +261,8 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
|
|||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
CommonUtils.closeQuietly(out);
|
||||
CommonUtils.closeQuietly(in);
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@
|
|||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.validation.AssertionImpl;
|
||||
|
|
@ -25,13 +34,7 @@ import org.springframework.mock.web.MockFilterConfig;
|
|||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
/**
|
||||
* Tests for the AuthenticationFilter.
|
||||
|
|
@ -177,4 +180,30 @@ public final class AuthenticationFilterTests extends TestCase {
|
|||
assertNull(session.getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY));
|
||||
assertNull(response2.getRedirectedUrl());
|
||||
}
|
||||
|
||||
public void testRenewInitParamThrows() throws Exception {
|
||||
final AuthenticationFilter f = new AuthenticationFilter();
|
||||
final MockFilterConfig config = new MockFilterConfig();
|
||||
config.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL);
|
||||
config.addInitParameter("service", "https://localhost:8443/service");
|
||||
config.addInitParameter("renew", "true");
|
||||
try {
|
||||
f.init(config);
|
||||
fail("Should have thrown IllegalArgumentException.");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Renew MUST"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAllowsRenewContextParam() throws Exception {
|
||||
final AuthenticationFilter f = new AuthenticationFilter();
|
||||
final MockServletContext context = new MockServletContext();
|
||||
context.addInitParameter("casServerLoginUrl", "https://cas.example.com/login");
|
||||
context.addInitParameter("service", "https://localhost:8443/service");
|
||||
context.addInitParameter("renew", "true");
|
||||
f.init(new MockFilterConfig(context));
|
||||
final Field renewField = AuthenticationFilter.class.getDeclaredField("renew");
|
||||
renewField.setAccessible(true);
|
||||
assertTrue((Boolean) renewField.get(f));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you 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 the following location:
|
||||
* *
|
||||
* 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 org.jasig.cas.client.validation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Cas10TicketValidationFilter}.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
*/
|
||||
public class Cas10TicketValidationFilterTests {
|
||||
@Test
|
||||
public void testThrowsRenewInitParam() throws Exception {
|
||||
final Cas10TicketValidationFilter f = new Cas10TicketValidationFilter();
|
||||
final MockFilterConfig config = new MockFilterConfig();
|
||||
config.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
config.addInitParameter("renew", "true");
|
||||
try {
|
||||
f.init(config);
|
||||
fail("Should have thrown IllegalArgumentException.");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Renew MUST"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllowsRenewContextParam() throws Exception {
|
||||
final Cas10TicketValidationFilter f = new Cas10TicketValidationFilter();
|
||||
final MockServletContext context = new MockServletContext();
|
||||
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
context.addInitParameter("renew", "true");
|
||||
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
|
||||
assertTrue(validator instanceof Cas10TicketValidator);
|
||||
assertTrue(((Cas10TicketValidator) validator).isRenew());
|
||||
}
|
||||
}
|
||||
|
|
@ -18,15 +18,16 @@
|
|||
*/
|
||||
package org.jasig.cas.client.validation;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jasig.cas.client.proxy.CleanUpTimerTask;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.util.MethodFlag;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
/**
|
||||
* Unit test for {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter}
|
||||
|
|
@ -179,6 +180,29 @@ public void testCallsCleanAllOnSchedule() throws Exception {
|
|||
assertNotNull(filter.getTicketValidator(config3));
|
||||
}
|
||||
|
||||
public void testRenewInitParamThrows() throws Exception {
|
||||
final Cas20ProxyReceivingTicketValidationFilter f = new Cas20ProxyReceivingTicketValidationFilter();
|
||||
final MockFilterConfig config = new MockFilterConfig();
|
||||
config.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
config.addInitParameter("renew", "true");
|
||||
try {
|
||||
f.init(config);
|
||||
fail("Should have thrown IllegalArgumentException.");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Renew MUST"));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAllowsRenewContextParam() throws Exception {
|
||||
final Cas20ProxyReceivingTicketValidationFilter f = new Cas20ProxyReceivingTicketValidationFilter();
|
||||
final MockServletContext context = new MockServletContext();
|
||||
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
context.addInitParameter("renew", "true");
|
||||
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
|
||||
assertTrue(validator instanceof AbstractUrlBasedTicketValidator);
|
||||
assertTrue(((AbstractUrlBasedTicketValidator) validator).isRenew());
|
||||
}
|
||||
|
||||
/**
|
||||
* construct a working {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import org.jasig.cas.client.PublicTestHttpServer;
|
|||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Licensed to Jasig under one or more contributor license
|
||||
* agreements. See the NOTICE file distributed with this work
|
||||
* for additional information regarding copyright ownership.
|
||||
* Jasig licenses this file to you 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 the following location:
|
||||
* *
|
||||
* 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 org.jasig.cas.client.validation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Unit test for {@link Saml11TicketValidationFilter}.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
*/
|
||||
public class Saml11TicketValidationFilterTests {
|
||||
@Test
|
||||
public void testRenewInitParamThrows() throws Exception {
|
||||
final Saml11TicketValidationFilter f = new Saml11TicketValidationFilter();
|
||||
final MockFilterConfig config = new MockFilterConfig();
|
||||
config.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
config.addInitParameter("renew", "true");
|
||||
try {
|
||||
f.init(config);
|
||||
fail("Should have thrown IllegalArgumentException.");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
assertTrue(e.getMessage().contains("Renew MUST"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllowsRenewContextParam() throws Exception {
|
||||
final Saml11TicketValidationFilter f = new Saml11TicketValidationFilter();
|
||||
final MockServletContext context = new MockServletContext();
|
||||
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
|
||||
context.addInitParameter("renew", "true");
|
||||
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
|
||||
assertTrue(validator instanceof Saml11TicketValidator);
|
||||
assertTrue(((Saml11TicketValidator) validator).isRenew());
|
||||
}
|
||||
}
|
||||
2
pom.xml
2
pom.xml
|
|
@ -75,6 +75,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
|
|
@ -94,6 +95,7 @@
|
|||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
|
|
|
|||
Loading…
Reference in New Issue