| /* |
| * 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(); |
| |
| } |