Cyrusc
Cyrusc

Reputation: 163

HP ALM Rest API QCSession 411 Authentication

I am using HP-ALM 12.01 which seems to be stock full of issues. I cannot update to another version at this time.

I am trying to get access to the rest api to upload test results automatically from JUnit. I am using the infrastructure shown here (example application -> Infrastructure). From which, my connection scripts passes base64 encoded login info to authentication-point/authenticate and I am retrieving a valid LWSSO cookie. However, when I use this cookie to connect to rest/site-session to receive my QCSession cookies, I am receiving a 411 Length Required error. I have attempted to hard code the Content-Length into the headers as shown here

public void GetQCSession(){
    String qcsessionurl = con.buildUrl("rest/site-session");
    Map<String, String> requestHeaders = new HashMap<String, String>();
    requestHeaders.put("Content-Type", "application/xml");
    requestHeaders.put("Accept", "application/xml");
    requestHeaders.put("Content-Length", "0");
    try {
        Response resp = con.httpPost(qcsessionurl, null, requestHeaders);
        con.updateCookies(resp);
        System.out.println(resp.getStatusCode());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

This did not work. I have also tried modifying the infrastructure to automatically inject the Content-Length header, as shown here

    private void prepareHttpRequest(
        HttpURLConnection con,
        Map<String, String> headers,
        byte[] bytes,
        String cookieString) throws IOException {

    String contentType = null;

    //attach cookie information if such exists
    if ((cookieString != null) && !cookieString.isEmpty()) {

        con.setRequestProperty("Cookie", cookieString);
    }

    //send data from headers
    if (headers != null) {

        //Skip the content-type header - should only be sent
        //if you actually have any content to send. see below.
        contentType = headers.remove("Content-Type");

        Iterator<Entry<String, String>>
                headersIterator = headers.entrySet().iterator();
        while (headersIterator.hasNext()) {
            Entry<String, String> header = headersIterator.next();
            con.setRequestProperty(header.getKey(), header.getValue());
        }
    }

    // If there's data to attach to the request, it's handled here.
    // Note that if data exists, we take into account previously removed
    // content-type.
    if ((bytes != null) && (bytes.length > 0)) {

        con.setDoOutput(true);

        //warning: if you add content-type header then you MUST send
        // information or receive error.
        //so only do so if you're writing information...
        if (contentType != null) {
            con.setRequestProperty("Content-Type", contentType);
        }

        OutputStream out = con.getOutputStream();
        out.write(bytes);
        out.flush();
        out.close();
        con.setRequestProperty("Content-Length", Integer.toString(bytes.length));
    } else {
        con.setRequestProperty("Content-Length", "0");
    }
}

which also does not work. note that setRequestProperty simply does a .set(key, value) to a MessageHeader

Has anyone dealt with this issue before or know how to resolve it?

Note that none of these issues occurs with postman. All 4 cookies are generated after a site-session post.

Upvotes: 3

Views: 2330

Answers (3)

sebastian
sebastian

Reputation: 21

The code Example from Barney was slightly expanded since it was not adapted for ALM 12.5 setting. Main difference is, that there are more cookies and cookies are attached to header

  Config config = new Config(dataService);
  String almURL = "https://" + config.host() + "/qcbin";

  client = ClientBuilder.newBuilder().build();
  target = client.target(almURL).path("api/authentication/sign-in");
  invocationBuilder = target
        .request(new String[] {"application/xml"})
        .accept(new String[] {"application/xml"});
  invocationBuilder.header("Authorization", getEncodedAuthString(config.username(), config.password()));

  res = invocationBuilder.post(null);

  String qcsessioncookie = res.getCookies().get("QCSession").getValue();
  String almusercookie = res.getCookies().get("ALM_USER").getValue();
  String xsrftokencookie = res.getCookies().get("XSRF-TOKEN").getValue();
  String lswoocookie = res.getCookies().get("LWSSO_COOKIE_KEY").getValue();

  /* Get the test-Set Data defect */
  String midPoint = "rest/domains/" + config.domain() + "/projects/" + config.project();
  target = client.target(almURL).path(midPoint).path("test-sets/1");
  invocationBuilder = target
        .request(new String[] {"application/xml"})
        .accept(new String[] {"application/xml"});

  concatenatedHeaderCookieString = "QCSession=" + qcsessioncookie + ";" + "ALM_USER=" + ";" + almusercookie + ";" + "XSRF-TOKEN=" + xsrftokencookie + ";"
        + "LWSSO_COOKIE_KEY=" + lswoocookie;
  invocationBuilder.header("Cookie", concatenatedHeaderCookieString);

  res = invocationBuilder.get();

Upvotes: 2

Barney
Barney

Reputation: 1895

POM.xml Dependency

<dependency>
    <groupId>org.glassfish.jersey.bundles</groupId>
    <artifactId>jaxrs-ri</artifactId>
    <version>2.0</version>
</dependency>

Java Code: Login, Get the first defect, Logout

import java.util.Base64;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class App {

    private static final String almURL = "https://abc.hp.com/qcbin";
    private static final String isAuthenticatedPath = "authentication-point/authenticate";
    private static final String qcSiteSession = "rest/site-session";
    private static final String logoutPath = "authentication-point/logout";
    private static String lswoocookie;
    private static String qcsessioncookie;

    public static String strDomain = "domain";
    public static String strProject = "project";
    public static String strUserName = "username";
    public static String strPassword = "password";

    public static Client client;
    public static WebTarget target;
    public static Invocation.Builder invocationBuilder;
    public static Response res;


    private static String getEncodedAuthString() {
        String auth = strUserName + ":" + strPassword;
        byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
        String authHeader = "Basic " + new String(encodedAuth);

        return authHeader;
    }

    public static void main(String args[]){
        client = ClientBuilder.newBuilder().build();

        /* Get LWSSO Cookie */
        target = client.target(almURL).path(
                isAuthenticatedPath);
        invocationBuilder = target.request(new String[] { "application/xml" });
        invocationBuilder.header("Authorization", getEncodedAuthString());
        res = invocationBuilder.get();
        lswoocookie = res.getCookies().get("LWSSO_COOKIE_KEY").getValue();

        /* Get QCSession Cookie */
        target = client.target(almURL).path(qcSiteSession);
        invocationBuilder = target
                .request();
        invocationBuilder.cookie("LWSSO_COOKIE_KEY", lswoocookie);
        res = invocationBuilder.post(null);
        qcsessioncookie = res.getCookies().get("QCSession").getValue();

        /* Get the first defect */
        String midPoint = "rest/domains/" + strDomain + "/projects/" + strProject;
        target = client.target(almURL).path(midPoint).path("defects/1");
        invocationBuilder = target
                .request(new String[] { "application/json" });
        invocationBuilder.cookie("LWSSO_COOKIE_KEY", lswoocookie);
        invocationBuilder.cookie("QCSession", qcsessioncookie);
        res = invocationBuilder.get();

        /* Logout */
        target = client.target(almURL).path(logoutPath);
        invocationBuilder = target
                .request();
        invocationBuilder.cookie("LWSSO_COOKIE_KEY", lswoocookie);
        invocationBuilder.cookie("QCSession", qcsessioncookie);
        res = invocationBuilder.post(null);
    }   
}

Upvotes: 1

Cyrusc
Cyrusc

Reputation: 163

The issue is that Java's HttpURLConnection ignores certain properties when manually set. One of these is Content-Length. This is because it automatically sets it itself. However, if you're not sending any data it simply doesn't send it, which ALM is not accepting due its outdated http protocols, as it expects to receive a Content-Length of 0.

To work around this you have to tell java to allow restrticted headers. This is done by running

System.setProperty("sun.net.http.allowRestrictedHeaders", "true");

for more information, look here Why does Content-Length HTTP header field use a value other than the one given in Java code?

Upvotes: 1

Related Questions