blob: 91dd1b75bafb00639d8971bfd615c42dd2ab66cc [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.plugins.repository.ssh;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.ivy.core.settings.TimeoutConstraint;
import org.apache.ivy.plugins.repository.AbstractRepository;
import org.apache.ivy.util.Credentials;
import org.apache.ivy.util.CredentialsUtil;
import org.apache.ivy.util.Message;
import com.jcraft.jsch.ConfigRepository;
import com.jcraft.jsch.ConfigRepository.Config;
import com.jcraft.jsch.OpenSSHConfig;
import com.jcraft.jsch.Session;
public abstract class AbstractSshBasedRepository extends AbstractRepository {
private File keyFile = null;
private File passFile = null;
private String userPassword = null;
private String keyFilePassword = null;
private String user = null;
private String host = null;
private int port = -1;
private boolean allowedAgentUse = false;
private String sshConfig = null;
public AbstractSshBasedRepository() {
super();
}
public AbstractSshBasedRepository(final TimeoutConstraint timeoutConstraint) {
super(timeoutConstraint);
}
/**
* hashmap of user/hosts with credentials. key is hostname, value is Credentials
**/
private static final Map<String, Credentials> credentialsCache = new HashMap<>();
private static final int MAX_CREDENTIALS_CACHE_SIZE = 100;
/**
* get a new session using the default attributes if the given String is a full uri, use the
* data from the uri instead
*
* @param pathOrUri
* might be just a path or a full ssh or sftp uri
* @return matching Session
* @throws IOException if something goes wrong
*/
protected Session getSession(String pathOrUri) throws IOException {
URI uri = parseURI(pathOrUri);
String host = getHost();
int port = getPort();
String user = getUser();
String userPassword = getUserPassword();
String sshConfig = getSshConfig();
File keyFile = getKeyFile();
if (uri != null && uri.getScheme() != null) {
if (uri.getHost() != null) {
host = uri.getHost();
}
if (uri.getPort() != -1) {
port = uri.getPort();
}
if (uri.getUserInfo() != null) {
String userInfo = uri.getUserInfo();
if (!userInfo.contains(":")) {
user = userInfo;
} else {
user = userInfo.substring(0, userInfo.indexOf(":"));
userPassword = userInfo.substring(userInfo.indexOf(":") + 1);
}
}
}
if (sshConfig != null) {
ConfigRepository configRepository = OpenSSHConfig.parseFile(sshConfig);
Config config = configRepository.getConfig(host);
host = config.getHostname();
if (user == null) {
user = config.getUser();
}
String keyFilePath = config.getValue("IdentityFile");
if (keyFilePath != null && keyFile == null) {
keyFile = new File(keyFilePath);
}
}
if (host == null) {
throw new IllegalArgumentException(
"missing host information. host should be provided either "
+ "directly on the repository or in the connection URI "
+ ", or in the openssh config file specified by sshConfig");
}
if (user == null) {
Credentials c = requestCredentials(host);
if (c != null) {
user = c.getUserName();
userPassword = c.getPasswd();
} else {
Message.error("username is not set");
}
}
return SshCache.getInstance().getSession(host, port, user, userPassword, keyFile,
getKeyFilePassword(), getPassFile(), isAllowedAgentUse());
}
/**
* Just check the uri for sanity
*
* @param source
* String of the uri
* @return URI object of the String or null
*/
private URI parseURI(String source) {
try {
URI uri = new URI(source);
if (uri.getScheme() != null
&& !uri.getScheme().toLowerCase(Locale.US)
.equals(getRepositoryScheme().toLowerCase(Locale.US))) {
throw new URISyntaxException(source, "Wrong scheme in URI. Expected "
+ getRepositoryScheme() + " as scheme!");
}
if (uri.getHost() == null && getHost() == null) {
throw new URISyntaxException(source, "Missing host in URI or in resolver");
}
if (uri.getPath() == null) {
throw new URISyntaxException(source, "Missing path in URI");
}
// if (uri.getUserInfo() == null && getUser() == null) {
// throw new URISyntaxException(source, "Missing username in URI or in resolver");
// }
return uri;
} catch (URISyntaxException e) {
Message.error(e.getMessage());
Message.error("The uri '" + source + "' is in the wrong format.");
Message.error("Please use " + getRepositoryScheme()
+ "://user:pass@hostname/path/to/repository");
return null;
}
}
/**
* Called, when user was not found in URL. Maintain static hash of credentials and retrieve or
* ask credentials for host.
*
* @param host
* host for which we want to get credentials.
* @return credentials for given host
**/
private Credentials requestCredentials(String host) {
Credentials c = credentialsCache.get(host);
if (c == null) {
c = CredentialsUtil.promptCredentials(new Credentials(null, host, user,
userPassword), getPassFile());
if (c != null) {
if (credentialsCache.size() > MAX_CREDENTIALS_CACHE_SIZE) {
credentialsCache.clear();
}
credentialsCache.put(host, c);
}
}
return c;
}
/**
* closes the session and remove it from the cache (eg. on case of errors)
*
* @param session
* key for the cache
* @param pathOrUri
* to release
*/
protected void releaseSession(Session session, String pathOrUri) {
session.disconnect();
SshCache.getInstance().clearSession(session);
}
/**
* set the default user to use for the connection if no user is given or a PEM file is used
*
* @param user
* to use
*/
public void setUser(String user) {
this.user = user;
}
/**
* @return the user to use for the connection if no user is given or a PEM file is used
*/
public String getUser() {
return user;
}
/**
* Sets the full file path to use for accessing a PEM key file
*
* @param filePath
* fully qualified name
*/
public void setKeyFile(File filePath) {
this.keyFile = filePath;
if (!keyFile.exists()) {
Message.warn("Pemfile " + keyFile.getAbsolutePath() + " doesn't exist.");
keyFile = null;
} else if (!keyFile.canRead()) {
Message.warn("Pemfile " + keyFile.getAbsolutePath() + " not readable.");
keyFile = null;
} else {
Message.debug("Using " + keyFile.getAbsolutePath() + " as keyfile.");
}
}
/**
* @return the keyFile
*/
public File getKeyFile() {
return keyFile;
}
/**
* @param password
* password to use for user/password authentication
*/
public void setUserPassword(String password) {
this.userPassword = password;
}
/**
* @return the keyFile password for public key based authentication
*/
public String getKeyFilePassword() {
return keyFilePassword;
}
/**
* @param keyFilePassword
* sets password for public key based authentication
*/
public void setKeyFilePassword(String keyFilePassword) {
this.keyFilePassword = keyFilePassword;
}
/**
* @return the user password
*/
public String getUserPassword() {
return userPassword;
}
/**
* @return the host
*/
public String getHost() {
return host;
}
/**
* @param host
* the host to set
*/
public void setHost(String host) {
this.host = host;
}
/**
* @return the port
*/
public int getPort() {
return port;
}
/**
* @param port
* the port to set
*/
public void setPort(int port) {
this.port = port;
}
/**
* @param passFile
* the passfile to set
*/
public void setPassFile(File passFile) {
this.passFile = passFile;
}
/**
* @return the passFile
*/
public File getPassFile() {
return passFile;
}
/**
* @return allowedAgentUse Whether use of a local SSH agent for authentication is allowed
*/
public boolean isAllowedAgentUse() {
return allowedAgentUse;
}
/**
* @param allowedAgentUse
* Whether use of a local SSH agent for authentication is allowed
*/
public void setAllowedAgentUse(boolean allowedAgentUse) {
this.allowedAgentUse = allowedAgentUse;
}
/**
* @return sshConfig Path to a local ssh config file
*/
public String getSshConfig() {
return sshConfig;
}
/**
* @param sshConfig
* Path to a local ssh config file
*/
public void setSshConfig(String sshConfig) {
this.sshConfig = sshConfig;
}
protected abstract String getRepositoryScheme();
}