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
9 * Sonatype, Inc. - initial API and implementation
10 *******************************************************************************/
11 package org
.eclipse
.aether
.repository
;
13 import java
.io
.Closeable
;
15 import java
.util
.Arrays
;
16 import java
.util
.HashMap
;
19 import org
.eclipse
.aether
.RepositorySystemSession
;
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:
29 * AuthenticationContext context = AuthenticationContext.forRepository( session, repository );
32 * char[] password = context.get( AuthenticationContext.PASSWORD, char[].class );
33 * // perform network operation using retrieved credentials
36 * // erase confidential authentication data from heap memory
37 * AuthenticationContext.close( context );
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:
45 * <li>{@code String}, {@code char[]}</li>
46 * <li>{@code String}, {@code File}</li>
48 * An authentication context is thread-safe.
50 public final class AuthenticationContext
55 * The key used to store the username. The corresponding authentication data should be of type {@link String}.
57 public static final String USERNAME
= "username";
60 * The key used to store the password. The corresponding authentication data should be of type {@code char[]} or
63 public static final String PASSWORD
= "password";
66 * The key used to store the NTLM domain. The corresponding authentication data should be of type {@link String}.
68 public static final String NTLM_DOMAIN
= "ntlm.domain";
71 * The key used to store the NTML workstation. The corresponding authentication data should be of type
74 public static final String NTLM_WORKSTATION
= "ntlm.workstation";
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}.
80 public static final String PRIVATE_KEY_PATH
= "privateKey.path";
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}.
86 public static final String PRIVATE_KEY_PASSPHRASE
= "privateKey.passphrase";
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.
94 public static final String HOST_KEY_ACCEPTANCE
= "hostKey.acceptance";
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.
101 public static final String HOST_KEY_REMOTE
= "hostKey.remote";
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.
108 public static final String HOST_KEY_LOCAL
= "hostKey.local";
111 * The key used to store the SSL context. The corresponding authentication data should be of type
112 * {@link javax.net.ssl.SSLContext}.
114 public static final String SSL_CONTEXT
= "ssl.context";
117 * The key used to store the SSL hostname verifier. The corresponding authentication data should be of type
118 * {@link javax.net.ssl.HostnameVerifier}.
120 public static final String SSL_HOSTNAME_VERIFIER
= "ssl.hostnameVerifier";
122 private final RepositorySystemSession session
;
124 private final RemoteRepository repository
;
126 private final Proxy proxy
;
128 private final Authentication auth
;
130 private final Map
<String
, Object
> authData
;
132 private boolean fillingAuthData
;
135 * Gets an authentication context for the specified repository.
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.
141 public static AuthenticationContext
forRepository( RepositorySystemSession session
, RemoteRepository repository
)
143 return newInstance( session
, repository
, null, repository
.getAuthentication() );
147 * Gets an authentication context for the proxy of the specified repository.
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
154 public static AuthenticationContext
forProxy( RepositorySystemSession session
, RemoteRepository repository
)
156 Proxy proxy
= repository
.getProxy();
157 return newInstance( session
, repository
, proxy
, ( proxy
!= null ) ? proxy
.getAuthentication() : null );
160 private static AuthenticationContext
newInstance( RepositorySystemSession session
, RemoteRepository repository
,
161 Proxy proxy
, Authentication auth
)
167 return new AuthenticationContext( session
, repository
, proxy
, auth
);
170 private AuthenticationContext( RepositorySystemSession session
, RemoteRepository repository
, Proxy proxy
,
171 Authentication auth
)
173 if ( session
== null )
175 throw new IllegalArgumentException( "repository system session missing" );
177 this.session
= session
;
178 this.repository
= repository
;
181 authData
= new HashMap
<String
, Object
>();
185 * Gets the repository system session during which the authentication happens.
187 * @return The repository system session, never {@code null}.
189 public RepositorySystemSession
getSession()
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.
198 * @return The repository to be contacted, never {@code null}.
200 public RemoteRepository
getRepository()
206 * Gets the proxy (if any) to be authenticated with.
208 * @return The proxy or {@code null} if authenticating directly with the repository's host.
210 public Proxy
getProxy()
216 * Gets the authentication data for the specified key.
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.
221 public String
get( String key
)
223 return get( key
, null, String
.class );
227 * Gets the authentication data for the specified key.
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.
234 public <T
> T
get( String key
, Class
<T
> type
)
236 return get( key
, null, type
);
240 * Gets the authentication data for the specified key.
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.
249 public <T
> T
get( String key
, Map
<String
, String
> data
, Class
<T
> type
)
253 throw new IllegalArgumentException( "authentication data key missing" );
256 synchronized ( authData
)
258 value
= authData
.get( key
);
259 if ( value
== null && !authData
.containsKey( key
) && !fillingAuthData
)
265 fillingAuthData
= true;
266 auth
.fill( this, key
, data
);
270 fillingAuthData
= false;
272 value
= authData
.get( key
);
276 authData
.put( key
, value
);
281 return convert( value
, type
);
284 private <T
> T
convert( Object value
, Class
<T
> type
)
286 if ( !type
.isInstance( value
) )
288 if ( String
.class.equals( type
) )
290 if ( value
instanceof File
)
292 value
= ( (File
) value
).getPath();
294 else if ( value
instanceof char[] )
296 value
= new String( (char[]) value
);
299 else if ( File
.class.equals( type
) )
301 if ( value
instanceof String
)
303 value
= new File( (String
) value
);
306 else if ( char[].class.equals( type
) )
308 if ( value
instanceof String
)
310 value
= ( (String
) value
).toCharArray();
315 if ( type
.isInstance( value
) )
317 return type
.cast( value
);
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.
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}.
331 public void put( String key
, Object value
)
335 throw new IllegalArgumentException( "authentication data key missing" );
337 synchronized ( authData
)
339 Object oldValue
= authData
.put( key
, value
);
340 if ( oldValue
instanceof char[] )
342 Arrays
.fill( (char[]) oldValue
, '\0' );
348 * Closes this authentication context and erases sensitive authentication data from heap memory. Closing an already
349 * closed context has no effect.
353 synchronized ( authData
)
355 for ( Object value
: authData
.values() )
357 if ( value
instanceof char[] )
359 Arrays
.fill( (char[]) value
, '\0' );
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.
370 * @param context The authentication context to close, may be {@code null}.
372 public static void close( AuthenticationContext context
)
374 if ( context
!= null )