Administration authentication requests
Amazon’s S3 service uses the access key and a hash of the request header and the secret key to authenticate the request. It has the benefit of providing an authenticated request, especially large uploads, without SSL overhead.
Most use cases for the S3 API involve using open-source S3 clients such as the
AmazonS3Client in the Amazon SDK for Java or Python Boto. These libraries do not
support the Ceph Object Gateway Admin API. You can subclass and extend these libraries to support
the Ceph Admin API. Alternatively, you can create a unique Gateway client.
Creating an execute() method
execute() method that
can take request parameters, authenticate the request, call the Ceph Admin API and receive a
response.CephAdminAPI class example is not supported or
intended for commercial use. It is for illustrative purposes only.Calling the Ceph Object Gateway
The client code contains five calls to the Ceph Object Gateway to demonstrate CRUD operations:
-
Create a User
-
Get a User
-
Modify a User
-
Create a Subuser
-
Delete a User
To use this example, get the httpcomponents-client-4.5.3 Apache HTTP components.
You can download it for example here: http://hc.apache.org/downloads.cgi. Then unzip the tar file, navigate to its
lib directory and copy the contents to the /jre/lib/ext directory
of the JAVA_HOME directory, or a custom classpath.
As you examine the CephAdminAPI class example, notice that the execute() method takes an HTTP
method, a request path, an optional subresource, null if not specified, and a map
of parameters. To run with subresources, for example, subuser, and
key, you will need to specify the subresource as an argument in the
execute() method.
The example method:
-
Builds a URI.
-
Builds an HTTP header string.
-
Instantiates an HTTP request, for example,
PUT,POST,GET,DELETE. -
Adds the
Dateheader to the HTTP header string and the request header. -
Adds the
Authorizationheader to the HTTP request header. -
Instantiates an HTTP client and passes it the instantiated HTTP request.
-
Makes a request.
-
Returns a response.
Building the header string
Building the header string is the portion of the process that involves Amazon’s S3 authentication procedure. Specifically, the example method does the following:
-
Adds a request type, for example,
PUT,POST,GET,DELETE. -
Adds the date.
-
Adds the requestPath.
The request type should be uppercase with no leading or trailing white space. If you do not trim white space, authentication will fail. The date MUST be expressed in GMT, or authentication will fail.
The exemplary method does not have any other headers. The Amazon S3 authentication procedure
sorts x-amz headers lexicographically. So if you are adding x-amz
headers, be sure to add them lexicographically.
Once you have built the header string, the next step is to instantiate an HTTP request and pass
it the URI. The exemplary method uses PUT for creating a user and subuser,
GET for getting a user, POST for modifying a user and
DELETE for deleting a user.
Once you instantiate a request, add the Date header followed by the
Authorization header. Amazon’s S3 authentication uses the standard
Authorization header, and has the following structure:
Authorization: AWS _ACCESS_KEY_:_HASH_OF_HEADER_AND_SECRET_
The CephAdminAPI
example class has a base64Sha1Hmac() method, which takes the header string and the
secret key for the admin user, and returns a SHA1 HMAC as a base-64 encoded string. Each
execute() call will invoke the same line of code to build the
Authorization header:
httpRequest.addHeader("Authorization", "AWS " + this.getAccessKey() + ":" + base64Sha1Hmac(headerString.toString(), this.getSecretKey()));
The following CephAdminAPI example class requires you to pass the access key,
secret key, and an endpoint to the constructor. The class provides accessor methods to change them
at runtime.
Example
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.Header;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.utils.URIBuilder;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;
import java.util.Map;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
public class CephAdminAPI {
/*
* Each call must specify an access key, secret key, endpoint and format.
*/
String accessKey;
String secretKey;
String endpoint;
String scheme = "http"; //http only.
int port = 80;
/*
* A constructor that takes an access key, secret key, endpoint and format.
*/
public CephAdminAPI(String accessKey, String secretKey, String endpoint){
this.accessKey = accessKey;
this.secretKey = secretKey;
this.endpoint = endpoint;
}
/*
* Accessor methods for access key, secret key, endpoint and format.
*/
public String getEndpoint(){
return this.endpoint;
}
public void setEndpoint(String endpoint){
this.endpoint = endpoint;
}
public String getAccessKey(){
return this.accessKey;
}
public void setAccessKey(String accessKey){
this.accessKey = accessKey;
}
public String getSecretKey(){
return this.secretKey;
}
public void setSecretKey(String secretKey){
this.secretKey = secretKey;
}
/*
* Takes an HTTP Method, a resource and a map of arguments and
* returns a CloseableHTTPResponse.
*/
public CloseableHttpResponse execute(String HTTPMethod, String resource,
String subresource, Map arguments) {
String httpMethod = HTTPMethod;
String requestPath = resource;
StringBuffer request = new StringBuffer();
StringBuffer headerString = new StringBuffer();
HttpRequestBase httpRequest;
CloseableHttpClient httpclient;
URI uri;
CloseableHttpResponse httpResponse = null;
try {
uri = new URIBuilder()
.setScheme(this.scheme)
.setHost(this.getEndpoint())
.setPath(requestPath)
.setPort(this.port)
.build();
if (subresource != null){
uri = new URIBuilder(uri)
.setCustomQuery(subresource)
.build();
}
for (Iterator iter = arguments.entrySet().iterator();
iter.hasNext();) {
Entry entry = (Entry)iter.next();
uri = new URIBuilder(uri)
.setParameter(entry.getKey().toString(),
entry.getValue().toString())
.build();
}
request.append(uri);
headerString.append(HTTPMethod.toUpperCase().trim() + "\n\n\n");
OffsetDateTime dateTime = OffsetDateTime.now(ZoneId.of("GMT"));
DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
String date = dateTime.format(formatter);
headerString.append(date + "\n");
headerString.append(requestPath);
if (HTTPMethod.equalsIgnoreCase("PUT")){
httpRequest = new HttpPut(uri);
} else if (HTTPMethod.equalsIgnoreCase("POST")){
httpRequest = new HttpPost(uri);
} else if (HTTPMethod.equalsIgnoreCase("GET")){
httpRequest = new HttpGet(uri);
} else if (HTTPMethod.equalsIgnoreCase("DELETE")){
httpRequest = new HttpDelete(uri);
} else {
System.err.println("The HTTP Method must be PUT,
POST, GET or DELETE.");
throw new IOException();
}
httpRequest.addHeader("Date", date);
httpRequest.addHeader("Authorization", "AWS " + this.getAccessKey()
+ ":" + base64Sha1Hmac(headerString.toString(),
this.getSecretKey()));
httpclient = HttpClients.createDefault();
httpResponse = httpclient.execute(httpRequest);
} catch (URISyntaxException e){
System.err.println("The URI is not formatted properly.");
e.printStackTrace();
} catch (IOException e){
System.err.println("There was an error making the request.");
e.printStackTrace();
}
return httpResponse;
}
/*
* Takes a uri and a secret key and returns a base64-encoded
* SHA-1 HMAC.
*/
public String base64Sha1Hmac(String uri, String secretKey) {
try {
byte[] keyBytes = secretKey.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(uri.getBytes("UTF-8"));
Encoder base64 = Base64.getEncoder();
return base64.encodeToString(rawHmac);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
The subsequent CephAdminAPIClient example illustrates how to instantiate the
CephAdminAPI class, build a map of request parameters, and use the
execute() method to create, get, update and delete a user.
Example
import java.io.IOException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import java.util.*;
public class CephAdminAPIClient {
public static void main (String[] args){
CephAdminAPI adminApi = new CephAdminAPI ("FFC6ZQ6EMIF64194158N",
"Xac39eCAhlTGcCAUreuwe1ZuH5oVQFa51lbEMVoT",
"ceph-client");
/*
* Create a user
*/
Map requestArgs = new HashMap();
requestArgs.put("access", "usage=read, write; users=read, write");
requestArgs.put("display-name", "New User");
requestArgs.put("email", "new-user@email.com");
requestArgs.put("format", "json");
requestArgs.put("uid", "new-user");
CloseableHttpResponse response =
adminApi.execute("PUT", "/admin/user", null, requestArgs);
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
try {
System.out.println("\nResponse Content is: "
+ EntityUtils.toString(entity, "UTF-8") + "\n");
response.close();
} catch (IOException e){
System.err.println ("Encountered an I/O exception.");
e.printStackTrace();
}
/*
* Get a user
*/
requestArgs = new HashMap();
requestArgs.put("format", "json");
requestArgs.put("uid", "new-user");
response = adminApi.execute("GET", "/admin/user", null, requestArgs);
System.out.println(response.getStatusLine());
entity = response.getEntity();
try {
System.out.println("\nResponse Content is: "
+ EntityUtils.toString(entity, "UTF-8") + "\n");
response.close();
} catch (IOException e){
System.err.println ("Encountered an I/O exception.");
e.printStackTrace();
}
/*
* Modify a user
*/
requestArgs = new HashMap();
requestArgs.put("display-name", "John Doe");
requestArgs.put("email", "johndoe@email.com");
requestArgs.put("format", "json");
requestArgs.put("uid", "new-user");
requestArgs.put("max-buckets", "100");
response = adminApi.execute("POST", "/admin/user", null, requestArgs);
System.out.println(response.getStatusLine());
entity = response.getEntity();
try {
System.out.println("\nResponse Content is: "
+ EntityUtils.toString(entity, "UTF-8") + "\n");
response.close();
} catch (IOException e){
System.err.println ("Encountered an I/O exception.");
e.printStackTrace();
}
/*
* Create a subuser
*/
requestArgs = new HashMap();
requestArgs.put("format", "json");
requestArgs.put("uid", "new-user");
requestArgs.put("subuser", "foobar");
response = adminApi.execute("PUT", "/admin/user", "subuser", requestArgs);
System.out.println(response.getStatusLine());
entity = response.getEntity();
try {
System.out.println("\nResponse Content is: "
+ EntityUtils.toString(entity, "UTF-8") + "\n");
response.close();
} catch (IOException e){
System.err.println ("Encountered an I/O exception.");
e.printStackTrace();
}
/*
* Delete a user
*/
requestArgs = new HashMap();
requestArgs.put("format", "json");
requestArgs.put("uid", "new-user");
response = adminApi.execute("DELETE", "/admin/user", null, requestArgs);
System.out.println(response.getStatusLine());
entity = response.getEntity();
try {
System.out.println("\nResponse Content is: "
+ EntityUtils.toString(entity, "UTF-8") + "\n");
response.close();
} catch (IOException e){
System.err.println ("Encountered an I/O exception.");
e.printStackTrace();
}
}
}
Reference
-
For a more extensive explanation of the Amazon S3 authentication procedure, consult the Signing and Authenticating REST Requests section of Amazon Simple Storage Service documentation.
-
For more information, see S3 Authentication.