001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.ivy.plugins.repository.ssh;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.net.URI;
023: import java.net.URISyntaxException;
024: import java.util.HashMap;
025: import java.util.Locale;
026:
027: import org.apache.ivy.plugins.repository.AbstractRepository;
028: import org.apache.ivy.util.Credentials;
029: import org.apache.ivy.util.CredentialsUtil;
030: import org.apache.ivy.util.Message;
031:
032: import com.jcraft.jsch.Session;
033:
034: public abstract class AbstractSshBasedRepository extends
035: AbstractRepository {
036:
037: private File keyFile = null;
038:
039: private File passFile = null;
040:
041: private String userPassword = null;
042:
043: private String keyFilePassword = null;
044:
045: private String user = null;
046:
047: private String host = null;
048:
049: private int port = -1;
050:
051: public AbstractSshBasedRepository() {
052: super ();
053: }
054:
055: /**
056: * hashmap of user/hosts with credentials.
057: * key is hostname, value is Credentials
058: **/
059: private static HashMap credentialsCache = new HashMap();
060:
061: private static final int MAX_CREDENTILAS_CACHE_SIZE = 100;
062:
063: /**
064: * get a new session using the default attributes if the given String is a full uri, use the
065: * data from the uri instead
066: *
067: * @param pathOrUri
068: * might be just a path or a full ssh or sftp uri
069: * @return matching Session
070: */
071: protected Session getSession(String pathOrUri) throws IOException {
072: URI uri = parseURI(pathOrUri);
073: String host = getHost();
074: int port = getPort();
075: String user = getUser();
076: String userPassword = getUserPassword();
077: if (uri != null && uri.getScheme() != null) {
078: if (uri.getHost() != null) {
079: host = uri.getHost();
080: }
081: if (uri.getPort() != -1) {
082: port = uri.getPort();
083: }
084: if (uri.getUserInfo() != null) {
085: String userInfo = uri.getUserInfo();
086: if (userInfo.indexOf(":") == -1) {
087: user = userInfo;
088: } else {
089: user = userInfo.substring(0, userInfo.indexOf(":"));
090: userPassword = userInfo.substring(userInfo
091: .indexOf(":") + 1);
092: }
093: }
094: }
095: if (user == null) {
096: Credentials c = requestCredentials(host);
097: if (c != null) {
098: user = c.getUserName();
099: userPassword = c.getPasswd();
100: } else {
101: Message.error("username is not set");
102: }
103: }
104: return SshCache.getInstance().getSession(host, port, user,
105: userPassword, getKeyFile(), getKeyFilePassword(),
106: getPassFile());
107: }
108:
109: /**
110: * Just check the uri for sanity
111: *
112: * @param source
113: * String of the uri
114: * @return URI object of the String or null
115: */
116: private URI parseURI(String source) {
117: try {
118: URI uri = new URI(source);
119: if (uri.getScheme() != null
120: && !uri.getScheme().toLowerCase(Locale.US).equals(
121: getRepositoryScheme()
122: .toLowerCase(Locale.US))) {
123: throw new URISyntaxException(source,
124: "Wrong scheme in URI. Expected "
125: + getRepositoryScheme() + " as scheme!");
126: }
127: if (uri.getHost() == null && getHost() == null) {
128: throw new URISyntaxException(source,
129: "Missing host in URI or in resolver");
130: }
131: if (uri.getPath() == null) {
132: throw new URISyntaxException(source,
133: "Missing path in URI");
134: }
135: //if (uri.getUserInfo() == null && getUser() == null) {
136: // throw new URISyntaxException(source, "Missing username in URI or in resolver");
137: //}
138: return uri;
139: } catch (URISyntaxException e) {
140: Message.error(e.getMessage());
141: Message.error("The uri is in the wrong format.");
142: Message
143: .error("Please use scheme://user:pass@hostname/path/to/repository");
144: return null;
145: }
146: }
147:
148: /**
149: * Called, when user was not found in URL.
150: * Maintain static hashe of credentials and retrieve or ask credentials
151: * for host.
152: *
153: * @param host
154: * host for which we want to get credentials.
155: * @return credentials for given host
156: **/
157: private Credentials requestCredentials(String host) {
158: Object o = credentialsCache.get(host);
159: if (o == null) {
160: Credentials c = CredentialsUtil.promptCredentials(
161: new Credentials(null, host, user, userPassword),
162: getPassFile());
163: if (c != null) {
164: if (credentialsCache.size() > MAX_CREDENTILAS_CACHE_SIZE) {
165: credentialsCache.clear();
166: }
167: credentialsCache.put(host, c);
168: }
169: return c;
170: } else {
171: return (Credentials) o;
172: }
173: }
174:
175: /**
176: * closes the session and remove it from the cache (eg. on case of errors)
177: *
178: * @param session
179: * key for the cache
180: * @param pathOrUri
181: * to release
182: */
183: protected void releaseSession(Session session, String pathOrUri) {
184: session.disconnect();
185: SshCache.getInstance().clearSession(session);
186: }
187:
188: /**
189: * set the default user to use for the connection if no user is given or a PEM file is used
190: *
191: * @param user
192: * to use
193: */
194: public void setUser(String user) {
195: this .user = user;
196: }
197:
198: /**
199: * @return the user to use for the connection if no user is given or a PEM file is used
200: */
201: public String getUser() {
202: return user;
203: }
204:
205: /**
206: * Sets the full file path to use for accessing a PEM key file
207: *
208: * @param filePath
209: * fully qualified name
210: */
211: public void setKeyFile(File filePath) {
212: this .keyFile = filePath;
213: if (!keyFile.exists()) {
214: Message.warn("Pemfile " + keyFile.getAbsolutePath()
215: + " doesn't exist.");
216: keyFile = null;
217: } else if (!keyFile.canRead()) {
218: Message.warn("Pemfile " + keyFile.getAbsolutePath()
219: + " not readable.");
220: keyFile = null;
221: } else {
222: Message.debug("Using " + keyFile.getAbsolutePath()
223: + " as keyfile.");
224: }
225: }
226:
227: /**
228: * @return the keyFile
229: */
230: public File getKeyFile() {
231: return keyFile;
232: }
233:
234: /**
235: * @param password
236: * password to use for user/password authentication
237: */
238: public void setUserPassword(String password) {
239: this .userPassword = password;
240: }
241:
242: /**
243: * @return the keyFile password for public key based authentication
244: */
245: public String getKeyFilePassword() {
246: return keyFilePassword;
247: }
248:
249: /**
250: * @param keyFilePassword
251: * sets password for public key based authentication
252: */
253: public void setKeyFilePassword(String keyFilePassword) {
254: this .keyFilePassword = keyFilePassword;
255: }
256:
257: /**
258: * @return the user password
259: */
260: public String getUserPassword() {
261: return userPassword;
262: }
263:
264: /**
265: * @return the host
266: */
267: public String getHost() {
268: return host;
269: }
270:
271: /**
272: * @param host
273: * the host to set
274: */
275: public void setHost(String host) {
276: this .host = host;
277: }
278:
279: /**
280: * @return the port
281: */
282: public int getPort() {
283: return port;
284: }
285:
286: /**
287: * @param port
288: * the port to set
289: */
290: public void setPort(int port) {
291: this .port = port;
292: }
293:
294: /**
295: * @param passFile
296: * the passfile to set
297: */
298: public void setPassFile(File passFile) {
299: this .passFile = passFile;
300: }
301:
302: /**
303: * @return the passFile
304: */
305: public File getPassFile() {
306: return passFile;
307: }
308:
309: protected abstract String getRepositoryScheme();
310:
311: }
|