]> git.argeo.org Git - gpl/argeo-slc.git/blob - AuthenticationContext.java
5b1ba2c0a88bdb86dd909cf3854468788dad35f9
[gpl/argeo-slc.git] / AuthenticationContext.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2014 Sonatype, Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Sonatype, Inc. - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.aether.repository;
12
13 import java.io.Closeable;
14 import java.io.File;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.Map;
18
19 import org.eclipse.aether.RepositorySystemSession;
20
21 /**
22 * A glorified map of key value pairs holding (cleartext) authentication data. Authentication contexts are used
23 * internally when network operations need to access secured repositories or proxies. Each authentication context
24 * manages the credentials required to access a single host. Unlike {@link Authentication} callbacks which exist for a
25 * potentially long time like the duration of a repository system session, an authentication context has a supposedly
26 * short lifetime and should be {@link #close() closed} as soon as the corresponding network operation has finished:
27 *
28 * <pre>
29 * AuthenticationContext context = AuthenticationContext.forRepository( session, repository );
30 * try {
31 * // get credentials
32 * char[] password = context.get( AuthenticationContext.PASSWORD, char[].class );
33 * // perform network operation using retrieved credentials
34 * ...
35 * } finally {
36 * // erase confidential authentication data from heap memory
37 * AuthenticationContext.close( context );
38 * }
39 * </pre>
40 *
41 * The same authentication data can often be presented using different data types, e.g. a password can be presented
42 * using a character array or (less securely) using a string. For ease of use, an authentication context treats the
43 * following groups of data types as equivalent and converts values automatically during retrieval:
44 * <ul>
45 * <li>{@code String}, {@code char[]}</li>
46 * <li>{@code String}, {@code File}</li>
47 * </ul>
48 * An authentication context is thread-safe.
49 */
50 public final class AuthenticationContext
51 implements Closeable
52 {
53
54 /**
55 * The key used to store the username. The corresponding authentication data should be of type {@link String}.
56 */
57 public static final String USERNAME = "username";
58
59 /**
60 * The key used to store the password. The corresponding authentication data should be of type {@code char[]} or
61 * {@link String}.
62 */
63 public static final String PASSWORD = "password";
64
65 /**
66 * The key used to store the NTLM domain. The corresponding authentication data should be of type {@link String}.
67 */
68 public static final String NTLM_DOMAIN = "ntlm.domain";
69
70 /**
71 * The key used to store the NTML workstation. The corresponding authentication data should be of type
72 * {@link String}.
73 */
74 public static final String NTLM_WORKSTATION = "ntlm.workstation";
75
76 /**
77 * The key used to store the pathname to a private key file. The corresponding authentication data should be of type
78 * {@link String} or {@link File}.
79 */
80 public static final String PRIVATE_KEY_PATH = "privateKey.path";
81
82 /**
83 * The key used to store the passphrase protecting the private key. The corresponding authentication data should be
84 * of type {@code char[]} or {@link String}.
85 */
86 public static final String PRIVATE_KEY_PASSPHRASE = "privateKey.passphrase";
87
88 /**
89 * The key used to store the acceptance policy for unknown host keys. The corresponding authentication data should
90 * be of type {@link Boolean}. When querying this authentication data, the extra data should provide
91 * {@link #HOST_KEY_REMOTE} and {@link #HOST_KEY_LOCAL}, e.g. to enable a well-founded decision of the user during
92 * an interactive prompt.
93 */
94 public static final String HOST_KEY_ACCEPTANCE = "hostKey.acceptance";
95
96 /**
97 * The key used to store the fingerprint of the public key advertised by remote host. Note that this key is used to
98 * query the extra data passed to {@link #get(String, Map, Class)} when getting {@link #HOST_KEY_ACCEPTANCE}, not
99 * the authentication data in a context.
100 */
101 public static final String HOST_KEY_REMOTE = "hostKey.remote";
102
103 /**
104 * The key used to store the fingerprint of the public key expected from remote host as recorded in a known hosts
105 * database. Note that this key is used to query the extra data passed to {@link #get(String, Map, Class)} when
106 * getting {@link #HOST_KEY_ACCEPTANCE}, not the authentication data in a context.
107 */
108 public static final String HOST_KEY_LOCAL = "hostKey.local";
109
110 /**
111 * The key used to store the SSL context. The corresponding authentication data should be of type
112 * {@link javax.net.ssl.SSLContext}.
113 */
114 public static final String SSL_CONTEXT = "ssl.context";
115
116 /**
117 * The key used to store the SSL hostname verifier. The corresponding authentication data should be of type
118 * {@link javax.net.ssl.HostnameVerifier}.
119 */
120 public static final String SSL_HOSTNAME_VERIFIER = "ssl.hostnameVerifier";
121
122 private final RepositorySystemSession session;
123
124 private final RemoteRepository repository;
125
126 private final Proxy proxy;
127
128 private final Authentication auth;
129
130 private final Map<String, Object> authData;
131
132 private boolean fillingAuthData;
133
134 /**
135 * Gets an authentication context for the specified repository.
136 *
137 * @param session The repository system session during which the repository is accessed, must not be {@code null}.
138 * @param repository The repository for which to create an authentication context, must not be {@code null}.
139 * @return An authentication context for the repository or {@code null} if no authentication is configured for it.
140 */
141 public static AuthenticationContext forRepository( RepositorySystemSession session, RemoteRepository repository )
142 {
143 return newInstance( session, repository, null, repository.getAuthentication() );
144 }
145
146 /**
147 * Gets an authentication context for the proxy of the specified repository.
148 *
149 * @param session The repository system session during which the repository is accessed, must not be {@code null}.
150 * @param repository The repository for whose proxy to create an authentication context, must not be {@code null}.
151 * @return An authentication context for the proxy or {@code null} if no proxy is set or no authentication is
152 * configured for it.
153 */
154 public static AuthenticationContext forProxy( RepositorySystemSession session, RemoteRepository repository )
155 {
156 Proxy proxy = repository.getProxy();
157 return newInstance( session, repository, proxy, ( proxy != null ) ? proxy.getAuthentication() : null );
158 }
159
160 private static AuthenticationContext newInstance( RepositorySystemSession session, RemoteRepository repository,
161 Proxy proxy, Authentication auth )
162 {
163 if ( auth == null )
164 {
165 return null;
166 }
167 return new AuthenticationContext( session, repository, proxy, auth );
168 }
169
170 private AuthenticationContext( RepositorySystemSession session, RemoteRepository repository, Proxy proxy,
171 Authentication auth )
172 {
173 if ( session == null )
174 {
175 throw new IllegalArgumentException( "repository system session missing" );
176 }
177 this.session = session;
178 this.repository = repository;
179 this.proxy = proxy;
180 this.auth = auth;
181 authData = new HashMap<String, Object>();
182 }
183
184 /**
185 * Gets the repository system session during which the authentication happens.
186 *
187 * @return The repository system session, never {@code null}.
188 */
189 public RepositorySystemSession getSession()
190 {
191 return session;
192 }
193
194 /**
195 * Gets the repository requiring authentication. If {@link #getProxy()} is not {@code null}, the data gathered by
196 * this authentication context does not apply to the repository's host but rather the proxy.
197 *
198 * @return The repository to be contacted, never {@code null}.
199 */
200 public RemoteRepository getRepository()
201 {
202 return repository;
203 }
204
205 /**
206 * Gets the proxy (if any) to be authenticated with.
207 *
208 * @return The proxy or {@code null} if authenticating directly with the repository's host.
209 */
210 public Proxy getProxy()
211 {
212 return proxy;
213 }
214
215 /**
216 * Gets the authentication data for the specified key.
217 *
218 * @param key The key whose authentication data should be retrieved, must not be {@code null}.
219 * @return The requested authentication data or {@code null} if none.
220 */
221 public String get( String key )
222 {
223 return get( key, null, String.class );
224 }
225
226 /**
227 * Gets the authentication data for the specified key.
228 *
229 * @param <T> The data type of the authentication data.
230 * @param key The key whose authentication data should be retrieved, must not be {@code null}.
231 * @param type The expected type of the authentication data, must not be {@code null}.
232 * @return The requested authentication data or {@code null} if none or if the data doesn't match the expected type.
233 */
234 public <T> T get( String key, Class<T> type )
235 {
236 return get( key, null, type );
237 }
238
239 /**
240 * Gets the authentication data for the specified key.
241 *
242 * @param <T> The data type of the authentication data.
243 * @param key The key whose authentication data should be retrieved, must not be {@code null}.
244 * @param data Any (read-only) extra data in form of key value pairs that might be useful when getting the
245 * authentication data, may be {@code null}.
246 * @param type The expected type of the authentication data, must not be {@code null}.
247 * @return The requested authentication data or {@code null} if none or if the data doesn't match the expected type.
248 */
249 public <T> T get( String key, Map<String, String> data, Class<T> type )
250 {
251 if ( key == null )
252 {
253 throw new IllegalArgumentException( "authentication data key missing" );
254 }
255 Object value;
256 synchronized ( authData )
257 {
258 value = authData.get( key );
259 if ( value == null && !authData.containsKey( key ) && !fillingAuthData )
260 {
261 if ( auth != null )
262 {
263 try
264 {
265 fillingAuthData = true;
266 auth.fill( this, key, data );
267 }
268 finally
269 {
270 fillingAuthData = false;
271 }
272 value = authData.get( key );
273 }
274 if ( value == null )
275 {
276 authData.put( key, value );
277 }
278 }
279 }
280
281 return convert( value, type );
282 }
283
284 private <T> T convert( Object value, Class<T> type )
285 {
286 if ( !type.isInstance( value ) )
287 {
288 if ( String.class.equals( type ) )
289 {
290 if ( value instanceof File )
291 {
292 value = ( (File) value ).getPath();
293 }
294 else if ( value instanceof char[] )
295 {
296 value = new String( (char[]) value );
297 }
298 }
299 else if ( File.class.equals( type ) )
300 {
301 if ( value instanceof String )
302 {
303 value = new File( (String) value );
304 }
305 }
306 else if ( char[].class.equals( type ) )
307 {
308 if ( value instanceof String )
309 {
310 value = ( (String) value ).toCharArray();
311 }
312 }
313 }
314
315 if ( type.isInstance( value ) )
316 {
317 return type.cast( value );
318 }
319
320 return null;
321 }
322
323 /**
324 * Puts the specified authentication data into this context. This method should only be called from implementors of
325 * {@link Authentication#fill(AuthenticationContext, String, Map)}. Passed in character arrays are not cloned and
326 * become owned by this context, i.e. get erased when the context gets closed.
327 *
328 * @param key The key to associate the authentication data with, must not be {@code null}.
329 * @param value The (cleartext) authentication data to store, may be {@code null}.
330 */
331 public void put( String key, Object value )
332 {
333 if ( key == null )
334 {
335 throw new IllegalArgumentException( "authentication data key missing" );
336 }
337 synchronized ( authData )
338 {
339 Object oldValue = authData.put( key, value );
340 if ( oldValue instanceof char[] )
341 {
342 Arrays.fill( (char[]) oldValue, '\0' );
343 }
344 }
345 }
346
347 /**
348 * Closes this authentication context and erases sensitive authentication data from heap memory. Closing an already
349 * closed context has no effect.
350 */
351 public void close()
352 {
353 synchronized ( authData )
354 {
355 for ( Object value : authData.values() )
356 {
357 if ( value instanceof char[] )
358 {
359 Arrays.fill( (char[]) value, '\0' );
360 }
361 }
362 authData.clear();
363 }
364 }
365
366 /**
367 * Closes the specified authentication context. This is a convenience method doing a {@code null} check before
368 * calling {@link #close()} on the given context.
369 *
370 * @param context The authentication context to close, may be {@code null}.
371 */
372 public static void close( AuthenticationContext context )
373 {
374 if ( context != null )
375 {
376 context.close();
377 }
378 }
379
380 }