I'm struggling to authenticate with MS SharePoint to use it's API. I've been googling and playing around with that problem for a while but somehow I can't figure out a solution. The most promising solution so far is based on this answer.
The dependencies I'm using are:
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
This is my code:
public static void callRestEasyService() throws Exception {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(AuthScope.ANY),
new NTCredentials("user", "password", "https://workstation.de", "domain.de"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
try {
HttpGet httpget = new HttpGet("https://adress/_api/web/lists");
System.out.println("Executing request " + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
The code is quite self-explanatory I think, it is basically just what the linked answer suggests. My investigation showed that the best way to go for this problem are the NTCredentials. I also tried some other alternatives like the Basic Authentication but in all cases I receive:
HTTP/1.1 401 Unauthorized
I also tried using Samba JCIFS as an alternative NTLM engine.
Furthermore I'm a little bit scared that maybe the parameter for workstation (3rd parameter) or domain is filled in incorrectly by me. The documentation says:
workstation - The workstation the authentication request is originating from. Essentially, the computer name for this machine.
So I tried filling in the name of my machine but many examples on the web suggest that you use an URL that you are trying to authenticate with. This caused a little confusion for me but with none of the two options I could get it working.
Does anyone know why that is or has a possible solution or a workaround for that problem? Is it maybe possible that the SharePoint restricts the access via a programmed client? As far as I know it's at least not possible to disable the API from the SharePoint. Any ideas / thoughts to this?
I did not manage to get it going with the apache http client instead of this I tried running a cURL request from my java code to approach the SharePoint API, this worked fine instead. In case any one else has a similiar problem and wants to try a workaround this is my source code to run the cURL request:
ProcessBuilder pb = new ProcessBuilder(
"curl",
"https://mysharepoint/_api/web/lists/getbytitle('listName')/items",
"-v",
"--ntlm",
"--negotiate",
"-u",
"user:password"
);
Process p = pb.start();
InputStream is = p.getInputStream();
return createHashMapForStaff(convertStreamToString(is));
The return line (createHashMapForStaff(convertStreamToString(is)) is just adapting the retrieved .XML from the SharePoint to my needs. The InputStream from the Process p is basically what you need.
Related
I have been successfully able to perform CURL commands from the CMD in my Windows PC by installing curl for Windows. Similarly, I have been able to get them working in my JAVA application by using ProcessBuilder and Process to create Operating System process. In particular, I need to execute the REST API end point commands used in KissFlow given here: https://support.kissflow.com/support/solutions/articles/179582-understanding-the-rest-api-end-points
Question: I have been able to able to execute the commands with the GET method like so.
ProcessBuilder pb = new ProcessBuilder("curl","-H","api_key:<XXXX>","-X","GET","http://<XXXX>.appspot.com/api/1/Employee/list/p1/50
However, when using the commands with POST like
ProcessBuilder pb = new ProcessBuilder("curl","-H","api_key:<XXXX>","-X ","POST","--data-urlencode","First Name=XXXX","http://<XXXX>.appspot.com/api/1/Employee/submit");, I get an error:
curl: (6) Could not resolve host: POST
with the input stream of the process returning
<html><title>Error 400 (Bad Request)!!1</title></html>
This in fact works perfectly when executed from CMD.
I have tried suggestions of all related questions here. Any help is appreciated. Thanks.
EDIT: Any alternate method from CURL to do the same will be acceptable as well.
According #GyroGearless's idea, try to use the sample code below to retrieve responses from URLs through methods GET and POST using Apache's HttpClient class. I think that with HttpClient you'll have much more "power" than with CURL.
You'll need commons-httpclient.jar and its dependencies: commons-codec and commons-logging. You'll find these jars at http://commons.apache.org/downloads/
(...)
String url = "http://<XXXX>.appspot.com/api/1/Employee/list/p1/50";
HttpClient client = new HttpClient();
GetMethod get = new GetMethod(url);
client.executeMethod(get);
System.out.println(new String(get.getResponseBody()));
get.releaseConnection();
url = "http://<XXXX>.appspot.com/api/1/Employee/submit";
PostMethod post = new PostMethod(url);
post.addParameter("id", "10");
client.executeMethod(post);
System.out.println(new String(post.getResponseBody()));
post.releaseConnection();
(...)
As you can see, in the PostMethod part we're sending parameters within request. Maybe you don't need this....
You can also use Visual studio, we also created a post method where we reject requests in kissflow. Just letting you know that you can also do that in visual studio.
I'm starting an e-commerce site and one of the drop shippers that I plan on using offers an api that can be used to automate the placing and tracking of orders. Even thought they have clients in several languages (PHP, Python, Ruby,Node), they don't have one in Java yet so I decided to write my own. I'm trying to test it, but I keep getting this error
Hostname in certificate didn't match: <api.theprintful.com> != <behappy.me> OR <behappy.me> OR <www.behappy.me>
I've followed the directions that they have for their documentation:
Some API requests (like the Product catalog and Country codes) are available without an
API key, but for majority of requests you will need to authenticate your store.
...
To perform authorization, you need to add the Authorization header with a Base64 encoded
API key when performing a request. So if the API key is vv0gtldg-1d7v-qclq:e2vv-
lmhg676ak0z1, then you need to add this header to the API request:
Authorization: Basic dnYwZ3RsZGctMWQ3di1xY2xxOmUydnYtbG1oZzY3NmFrMHox
I've followed the directions
//constructor that is used to make the object
public ProductsRequest(String apiKey)
{
super();
this.apiKey = apiKey;
this.encodedApiKey = codec.encodeBase64String(apiKey.getBytes());
}
//function that is used to call the api
public List<Product> getAllProductList(String path)
{
//the list of products that will be returned
List<Product> returnedProducts = null;
//set the endpoint that will be used to get the list of products
httpGet = new HttpGet(path);
String basicString = "Basic "+this.encodedApiKey;
httpGet.addHeader("Authorization",basicString.substring(0,basicString.length()-2));
try
{
response = httpClient.execute(httpGet);
if(response.getStatusLine().getStatusCode() == 200)
{
entity = response.getEntity();
jsonResponse = EntityUtils.toString(entity);
Type listType = new TypeToken<List<Product>>(){}.getType();
JsonNode root = mapper.readTree(jsonResponse);
ArrayNode products = (ArrayNode) root.path("result");
returnedProducts = (List<Product>) gson.fromJson(products.toString(),listType);
}
}
catch (IOException e)
{
e.printStackTrace();
}
return returnedProducts;
}
I can't figure out why I'm getting this error. I tried emailing the dev team at the company and they suggested that I try creating a fake store that isn't connected to any ecommerce software that I can use just for testing but they said that everything looked good on their end. I tried following that suggestion but I still get the same error.
I found this thread on Stackoverflow where it looks like somebody had a similar problem. I'm just wondering why would I have to do this in Java when none of the other client libraries in other languages have to go through this process?
As far as I can tell I'm encoding everything the way the documentation says I should and I'm setting the headers correctly. Do I need to use different libraries?
I'm currently using the apache commons-codec
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
</dependency>
and apache http-components
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
I've tried running the examples from one of the client libraries that the company has in another language and everything is working correctly. I know that it is something wrong with my code and not their SSL certificates, but I can't figure out what it is.
Your server is using Server Name Indication to present a different certificate depending on the name you ask for (which is necessary to host multiple certificate with SSL/TLS on the same IP address and port).
You can check this with:
openssl s_client -connect api.theprintful.com:443 -servername api.theprintful.com
and
openssl s_client -connect api.theprintful.com:443
SNI is currently only available since Java 7 (and only on the client side, server-side support being planned for Java 8).
An application doing Java 7 should do this automatically for you when using HttpsURLConnection.
The fix for this in Apache HTTP Client is recent. You should make sure you are indeed using version 4.3.2 and Java 7. The following would fail with version 4.3.1 (even with Java 7), or with 4.3.2 on Java 6:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("https://sni.velox.ch/");
HttpResponse response = httpClient.execute(httpGet);
Are you connecting via SSL? i.e. does your path begin with 'https://www.behappy.me'? If so, then i think what's happening is that their SSL certificate is issued for 'api.theprintful.com'. To prevent man-in-the-middle attacks, SSL clients are supposed to check whether the certificate they got from the server was really issued for the server in question, see RFC 6125. In your case, this check fails and results in the exception you see. This has nothing to do with your side of the authentication, you are probably doing everything right, they see the correct request coming in, but your side croaks on the wrong certificate.
What can you do to get around this?
Ask them to provide the correct certificate. That's the easiest and best solution. Unfortunately, it requires cooperation from their side, which you may or may not get.
Fake you own DNS, that is, put an entry in your hosts file pointing api.theprintful.com to the IP address of www.behappy.me. This is the suggestion in this thread.
Suppress the hostname check by supplying an empty HostnameVeriier. That's indicated in the same thread above, and can be done without messing with any external settings. But make sure, i mean really absolutely sure that you remove that workaround before moving the code to production, or else goto fail.
Context
I have a desktop JAVA application I use to upload files (blobs) to a google app blobstore.
Everything works fine with a direct connection to the Internet but it doesn't when connecting through an HTTP proxy (Squid) with authentication.
I am using httpClient 4.2.3 and I don't get any error or response. It just gets stuck when calling httpClient.execute(post).
Code
I added these lines to handle the proxy authentication and it works well when using URL to get a page:
System.setProperty("http.proxyUser", username);
System.setProperty("http.proxyPassword", password);
I tried those as well:
Authenticator.setDefault(
new Authenticator() {
#Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
username, password.toCharArray());
}
}
);
And from now on this is the same code that works when not using a proxy.
First of all I download a page where I get the url to use to post a file to the blobstore:
URL url = new URL("http://www.example.com/get-upload-url.jsp");
String urlWhereToPost=IOUtils.toString(url.openStream());
DefaultHttpClient client = new DefaultHttpClient ();
Here we prepare the multipart post:
HttpPost post
= new HttpPost( urlWhereToPost.trim() );
MultipartEntity entity
= new MultipartEntity( HttpMultipartMode.BROWSER_COMPATIBLE);
entity.addPart( "key"
, new FileBody(new File(jpgFilePath)
, "image/jpeg" )
);
post.setEntity((HttpEntity)entity);
And it is when calling execute that nothing happens (and it never get's to the next instruction):
HttpResponse execute = client.execute( post );
Tests
I have been trying several things but nothing worked:
In the beginning I thought the problem was using POST because GET works fine using URL()
but I tried using HttpClient to execute a GET and it gets stuck as well.
I used Wireshark to check the packets send to the proxy and I saw that when using URL() Wireshark recognizes the calls to the proxy as requests to execute a GET from the proxy. But when using httpClient it looks like the request is not well built because Wireshark shows a packet but doesn't recognize the inner request.
Then I tried building the POST using HttpURLConnection and it gets through the proxy and I get the answer from the server but it looks like I am not building it well because appengine doesn't find the file I send (but this would be another question...).
Conclusion
Anyone with the same problem? Any idea?
Your proxy settings are for the Java system classes. Apache HttpClient is supposed to be configured in a different way.
This link may help: Proxy authentication
I have been using HTTPClient version 4.1.2 to try to access a REST over HTTP API that requires Basic Authentication. Here is client code:
DefaultHttpClient httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager());
// Enable HTTP Basic Auth
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
new UsernamePasswordCredentials(this.username, this.password));
HttpHost proxy = new HttpHost(this.proxyURI.getHost(), this.proxyURI.getPort());
httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, proxy);
When I construct a POST request, like this:
HttpPost request = new HttpPost("http://my/url");
request.addHeader(new BasicHeader("Content-type", "application/atom+xml; type=entry")); // required by vendor
request.setEntity(new StringEntity("My content"));
HttpResponse response = client.execute(request);
I see in Charles Proxy that there are two requests being sent. One without the Authorization: Basic ... header and one with it. The first one fails with a 401, as you would expect, but the second goes through just fine with a 201.
Does anyone know why this happens? Thanks!
EDIT:
I should make clear that I have already looked at this question, but as you can see I set the AuthScope the same way and it didn't solve my problem. Also, I am creating a new HttpClient every time I made a request (though I use the same ConnectionManager), but even if I use the same HttpClient for multiple requests, the problem still persists.
EDIT 2:
So it looks like what #LastCoder was suggesting is the way to do. See this answer to another question. The problem stems from my lack of knowledge around the HTTP spec. What I'm looking to do is called "preemptive authentication" and the HttpClient docs mention it here. Thankfully, the answer linked to above is a much shorter and cleaner way to do it.
Rather than using .setCredentials() why don't you just encode USERNAME:PASSWORD and add the authentication header with .addHeader()
This means that your server/target endpoint is creating a new session for every client request. This forces every request of yours to go through a hand-shake, which means the clients first makes the call and realizes that it needs authorization, then it follows with the authorization. What you need to do is send the authorization preemptively as follows:
httpClient.getParams().setAuthenticationPreemptive(true);
Just to understand the process you may log your client request headers, to give you an idea of what your client is sending and receiving:
See if this works.
I'm working on building an android application which requires different levels of authentication, and I would like to do so using Active Directory.
From what I've read, using Kerberos is the way Microsoft suggests. How do I do this for Android? I see the javax.security.auth doc, but it doesn't tell me too much.
I also saw a note somewhere that Kerberos does not contain user groups - is this true? In that case, would I have to somehow combine LDAP as well?
EDIT
The main goal here is achieving an LDAP connection to the active directory in order to authenticate and give the user correct permissions for the enterprise Android application. The real barrier here is the fact that Google left out many of the Java Web Services API from it's port to android. (i.e. javax.naming) Also, many of the connection mechanisms in the Android jar seem to be only included as legacy code, and they in fact actually do nothing.
For that you might be better off just staying completely within LDAP and don't venture into the kerberos. Kerberos gives you advantage of Single Sign On, but since your android app doesn't have any credentials already in place it doesn't really help you. I guess google had their own reasons not to include the javax.naming into the distro. It is pretty heavy stuff.
You might be able to either port the stuff yourself from java runtime library sources, or might be better off using native LDAP library. For example this one.
Just remember to use secure LDAP connection or at least secure authentication method. More info about this is here.
I found the documentation here to be really useful when I was writing my code to authenticate with my Kerberos server. Here's how I authenticate with my kerberos server, but you might need to tweak it for yours (hence me including the link):
public static final int REGISTRATION_TIMEOUT = 30 * 1000; // ms
private static DefaultHttpClient httpClient;
private static final AuthScope SERVER_AUTH_SCOPE =
new AuthScope("urls to kerberos server", AuthScope.ANY_PORT);
public static DefaultHttpClient getHttpClient(){
if(httpClient == null){
httpClient = new DefaultHttpClient();
final HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, REGISTRATION_TIMEOUT);
ConnManagerParams.setTimeout(params, REGISTRATION_TIMEOUT);
}
return httpClient;
}
public static boolean authenticate(String username, String password)
{
UsernamePasswordCredentials creds =
new UsernamePasswordCredentials(username, password);
DefaultHttpClient client = getHttpClient();
client.getCredentialsProvider().setCredentials(SERVER_AUTH_SCOPE, creds);
boolean authWorked = false;
try{
HttpGet get = new HttpGet(AUTH_URI);
HttpResponse resp = client.execute(get);
authWorked = resp.getStatusLine().getStatusCode() != 403
}
catch(IOException e){
Log.e("TAG", "IOException exceptions");
//TODO maybe do something?
}
return authWorked;
}
Have you looked at using JCIFS? Based on these questions [1] [2] and this site, JCIFS works under Android. The JCIFS site has a simple NTLM Authenticator example that could help get you started. However, based on this Samba list message, you will need to use LDAP and custom code to get the user's groups.
Try this tutorial from Oracle. My code likes a charm. Hopefully everything is included in Android's VM distro.