Authentication against the REST API, in the Java client, makes
use of a few Jersey utilities. In the RestClient provided in
com.oracle.ovm.mgr.ws.client
, a login
method has been defined:
... public class RestClient { public static final String URI_BASE_PATH = "/ovm/core/wsapi/rest"; String SECURE_PROTOCOL = "https"; String INSECURE_PROTOCOL = "http"; private static final Map<Class, GenericType> genericTypeMap = new HashMap<Class, GenericType>(); private boolean debug = false; private URI baseURI; private Client webClient = null; private List<NewCookie> cookies; private String mediaType = MediaType.APPLICATION_XML; private Locale locale = null; private SSLSocketFactory sslSocketFactory = null; ... public boolean login(final String username, final String password, final Locale locale, final String path) throws WsException { try { // Make a dummy request to pass the authorization info to the server get // the session id (which is returned in a cookie) if (getCookies() == null) { final WebResource resource = getWebClient().resource(getBuilder().path(path).build()); // Specify media type to accept final Builder resourceBuilder = resource.accept(getMediaType()); // Append auth info resourceBuilder.header("Authorization", "Basic " + new String(Base64.encode(username + ":" + password))); if (locale != null) { resourceBuilder.acceptLanguage(locale); } final ClientResponse response = resourceBuilder.head(); setCookies(response.getCookies()); } return true; } catch (final UniformInterfaceException ex) { throw convertException(ex); } } ... }
Note that an initial request is sent and an Authorization header is attached with the Base64 encoded username and password required for HTTP Basic authentication. The session information is returned as a cookie in the server response, and this cookie can be used for all subsequent requests.
The OvmWsRestClient class in
com.oracle.ovm.mgr.ws.client
extends the
RestClient class. It provides the API function that gets
called in the sample application and defines the dummy query
that is performed against the API to achieve authentication:
public class OvmWsRestClient extends RestClient implements OvmWsClient { public static final String URI_BASE_PATH = "/ovm/core/wsapi/rest"; public OvmWsRestClient() { super(); } @Override public boolean login(final String username, final String password, final Locale locale) throws WsException { return super.login(username, password, locale, "Server"); } ... }
Now in the WsDevClient class (the sample application), all we need to do is call this method:
... public class WsDevClient { ... public void run() { try { ... api = OvmWsClientFactory.getOvmWsClient(wsimpl); ... api.initialize(hostname, port, true); // Authenticate with the OvmApi Service api.login(username, password, Locale.getDefault()); ... } ... }
There are a few things to note about how this has been implemented in the sample client. The first point is that we refer to the OvmWsClientFactory class to determine whether we are using the REST client or the SOAP client, using the string variable 'wsimpl'. This class allows us to use the same demo code to show both APIs. It contains a switch statement that sets up the appropriate client object:
switch (implementation) { case SOAP: return new OvmWsSoapClient(); case REST: return new OvmWsRestClient(); }
Before the login method is called, an initialize method is used to set the hostname and port number on which the Oracle VM Manager is running. This method is ultimately defined in the RestClient class and simply takes all of the components that make up the base URI and constructs the base URI that is used thereafter.
A final thing to note, is that the call to the initialize and
login methods provide some preset variables. In fact, the
com.oracle.ovm.mgr.ws.sample
package also
includes a properties file:
WsDevClient.properties
. This file
contains the default values for many of the variables
referenced throughout this guide. For the sample code to work
according to the specifics of your own environment, many of
these variables must be overridden. Instructions for handling
variable overriding are provided in the comments of this
properties file itself.
What about Certificate-based Authentication?
Since the code in the SDK does not assume that you have set up user certificates, there are no examples showing how to authenticate using a signed SSL certificate. However, all that is required for this to happen is for you to use the certificate for all REST requests in your session. In Java, the easiest way to do this is to ensure that you have the certificate and its associated key stored in a keystore file. You can then use the keystore to load your key manager and trust manager that can be used to initialize an SSL context that is used for all connections.
The following example code, should help to get you started. It has been stripped of any error handling that may obfuscate what is required. This code expects the following variables to be initialized:
File keystoreFile; // A file representing the location of the KeyStore char[] keystorePassword; // The KeyStore password char[] keyPassword; // The key password - if you didn't specify one when creating your // key, then use the keystorePassword for this as well
The code required to use this keystore to initialize an SSL context follows:
// Load your keystore FileInputStream stream = new FileInputStream(keystoreFile); KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(stream, keystorePassword); stream.close(); // Initialize the key manager from the keystore KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmFactory.init(keystore, keyPassword); KeyManager km[] = kmFactory.getKeyManagers(); // Initialize the trust manager from the keystore TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmFactory.init(keystore); TrustManager tm[] = tmFactory.getTrustManagers(); // Initialize an SSL context and make it the default for all connections SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(km, tm, null); SSLContext.setDefault(ctx);
This code could be substituted in the WsDevClient class where the initial SSL context is set:
public void run() { try { // Configure the SSLContext with an insecure TrustManager // This is done to avoid the need to use valid certificates in the // development environment. // This should not be done in a real / secure environment. final SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(new KeyManager[0], new TrustManager[]{ new InsecureTrustManager() }, new SecureRandom()); SSLContext.setDefault(ctx); ... }