blob: efe1fa05f6a279366eb3d3eaecce80354826db4c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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
*
* https://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.apache.ivy.util.url;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import org.apache.ivy.util.Credentials;
import org.apache.ivy.util.Message;
import static org.apache.ivy.util.StringUtils.isNullOrEmpty;
/**
*
*/
public final class IvyAuthenticator extends Authenticator {
private Authenticator original;
private static boolean securityWarningLogged = false;
/**
* Private c'tor to prevent instantiation.
*/
private IvyAuthenticator(Authenticator original) {
this.original = original;
}
/**
* Installs an <tt>IvyAuthenticator</tt> as default <tt>Authenticator</tt>. Call this method
* before opening HTTP(S) connections to enable Ivy authentication.
*/
public static void install() {
// We will try to use the original authenticator as backup authenticator.
// Since there is no getter available, so try to use some reflection to
// obtain it. If that doesn't work, assume there is no original authenticator
Authenticator original = getCurrentAuthenticator();
if (original instanceof IvyAuthenticator) {
return;
}
try {
Authenticator.setDefault(new IvyAuthenticator(original));
} catch (SecurityException e) {
if (!securityWarningLogged) {
securityWarningLogged = true;
Message.warn("Not enough permissions to set the IvyAuthenticator. "
+ "HTTP(S) authentication will be disabled!");
}
}
}
// API ******************************************************************
// Overriding Authenticator *********************************************
protected PasswordAuthentication getPasswordAuthentication() {
PasswordAuthentication result = null;
if (isProxyAuthentication()) {
String proxyUser = System.getProperty("http.proxyUser");
if (!isNullOrEmpty(proxyUser)) {
String proxyPass = System.getProperty("http.proxyPassword", "");
Message.debug("authenticating to proxy server with username [" + proxyUser + "]");
result = new PasswordAuthentication(proxyUser, proxyPass.toCharArray());
}
} else {
Credentials c = CredentialsStore.INSTANCE.getCredentials(getRequestingPrompt(),
getRequestingHost());
Message.debug("authentication: k='"
+ Credentials.buildKey(getRequestingPrompt(), getRequestingHost()) + "' c='"
+ c + "'");
if (c != null) {
final String password = c.getPasswd() == null ? "" : c.getPasswd();
result = new PasswordAuthentication(c.getUserName(), password.toCharArray());
}
}
if (result == null && original != null) {
Authenticator.setDefault(original);
try {
result = Authenticator.requestPasswordAuthentication(getRequestingHost(),
getRequestingSite(), getRequestingPort(), getRequestingProtocol(),
getRequestingPrompt(), getRequestingScheme(), getRequestingURL(), getRequestorType());
} finally {
Authenticator.setDefault(this);
}
}
return result;
}
/**
* The {@link Authenticator} doesn't have API before Java 9 to get hold of the current system
* level {@link Authenticator}. This method does a best-effort attempt to try and get hold of
* the current {@link Authenticator} in a way that's specific to the implementation of this
* method. There's no guarantee that this method will return the current authenticator.
* <strong>Note: this method is intended to be used exclusively by tests.</strong>
*
* @return Returns the currently setup system level {@link Authenticator}. In cases where this
* method isn't able to get the current authenticator, this method returns null
*/
static Authenticator getCurrentAuthenticator() {
return (getJavaVersion() < 9) ? getTheAuthenticator() : getDefaultAuthenticator();
}
/**
* Checks if the current authentication request is for the proxy server.
*/
private boolean isProxyAuthentication() {
return RequestorType.PROXY.equals(getRequestorType());
}
private static Authenticator getDefaultAuthenticator() {
try {
final Method m = Authenticator.class.getDeclaredMethod("getDefault");
return (Authenticator) m.invoke(null);
} catch (final Throwable t) {
handleReflectionException(t);
}
return null;
}
private static Authenticator getTheAuthenticator() {
try {
Field f = Authenticator.class.getDeclaredField("theAuthenticator");
f.setAccessible(true);
return (Authenticator) f.get(null);
} catch (final Throwable t) {
handleReflectionException(t);
}
return null;
}
private static void handleReflectionException(final Throwable t) {
Message.debug("Error occurred while getting the original authenticator: "
+ t.getMessage());
}
private static int getJavaVersion() {
// See https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html#wp90002
final String[] version = System.getProperty("java.specification.version").split("\\.");
final int major = Integer.parseInt(version[0]);
return major == 1 ? Integer.parseInt(version[1]) : major;
}
}