Unable to download Azure blob from Java - java

This is a rather strange issue. I need to generate a signed url that can be shared with others for downloading the file within a certain time limit. Obviously, since I am saying signed, you should not require any prior permissions to download. Following is the code I am using to upload and download
private final BlobSasPermission blobSasPermission = new BlobSasPermission()
.setReadPermission(true);
public String uploadAndGenerateSignedUrl(String filePath, String uploadPath) {
BlobClient blobClient = containerClient.getBlobClient(uploadPath);
blobClient.uploadFromFile(filePath);
BlockBlobClient blockBlobClient = blobClient.getBlockBlobClient();
BlobServiceSasSignatureValues blobServiceSasSignatureValues = new BlobServiceSasSignatureValues(
OffsetDateTime.now().plusMinutes(azureConfiguration.getExpiryMin()), blobSasPermission);
return blockBlobClient.getBlobUrl() + "?" + blockBlobClient
.generateSas(blobServiceSasSignatureValues);
}
public void downloadFromUrl(String signedUrl, File file) throws IOException {
OkHttpClient client = SharedClient.getNewSharedClientBuilder().build();
Request request = new Request.Builder()
.url(signedUrl)
.build();
okhttp3.Response response = client.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
FileUtils.copyInputStreamToFile(inputStream, file);
}
Now, when I open the generated url in the browser, the file is successfully downloaded. Similarly, running the GET in PostMan for this url, works fine. However, when I send the same url to the download method, it fails with error 403 stating Server failed to authenticate the request. I am clueless about the reason for this. I tried adding both http and https protocols to signed url but that also did not help.
Edit:
Adding the error message
Response{protocol=http/1.1, code=403, message=Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature., url=https://{account}.blob.core.windows.net/{container}/MDConnector/MDTest/Test_16269425/2021-07-24/1627125196450/NormalFile.md?sv=2019-07-07&se=2021-07-24T12:13:18Z&sr=b&sp=r&sig=BoTaZ9iEA8Cdcbscf6zpWTol32+52rfVyLNDMBlLr1Q=}

I'm able to reproduce this issue. Essentially the issue is coming because of + sign in your SAS token signature. When a URL is created, + sign is interpreted as a space and because of that your authorization is failing.
What you have to do is URL encode your SAS token. Once you do that, your request should work just fine. Please see the sample code below:
package com.company;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws IOException {
String url = "https://account.blob.core.windows.net/container/blob.png";
String sasToken = "sv=2020-04-08&st=2021-07-24T14:33:27Z&se=2021-07-31T14:33:00Z&sr=b&sp=rac&sig=QytPc/+0z/eHd+u4WO0HGOFDOZjVfB+vbQdbR6FFrl4=";//Notice the "+" sign in SAS token.
sasToken = URLEncoder.encode(sasToken, StandardCharsets.UTF_8.toString());
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url+"?"+ sasToken).build();
okhttp3.Response response = client.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
}
}

Related

Request MultiPart/Form-Data with ByteArray

i want to send a request includes bytearray as form-data. Everyone using "File" but i have just the "bytearray" and i don't want to use any path. My request in curl:
curl -F file=#file server
In java what i tried:
byte[] fileByte = Base64.decodeBase64(parameter);
ByteArrayInputStream myFile = new ByteArrayInputStream(fileByte);
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("server");
multipartEntityBuilder.addBinaryBody("file", myFile, ContentType.APPLICATION_OCTET_STREAM, "filename");
HttpEntity multipart = multipartEntityBuilder.build();
httpPost.setEntity(multipart);
and i tried
multipartEntityBuilder.addBinaryBody("file", fileByte, ContentType.DEFAULT_BINARY, "filename");
//OR FileBody, ByteArrayEntity, InputStream or like thats
So, that methods not worked for me. How i can send the request successfully ?
EDIT: i used the ptsv2 with postman and result ->
when i send the same request and file with java the result ->
i think issue is certainly related to the ByteArray or InputStream. I must find the another Type for the my byte[] or right method for post in java with using the File type but without path.
I don't know what "server" is, but maybe the problem is with the server you are sending to. I ran your code, writing to a public HTTP POST test server available on the internet, and it works fine for me. Here's the code I ran:
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
class ListsTest {
public static void main(String[] args) {
byte[] fileByte = "Sample string data".getBytes(StandardCharsets.UTF_8);
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("https://ptsv2.com/t/r7ypk-1613291354/post");
multipartEntityBuilder.addBinaryBody("file", fileByte, ContentType.DEFAULT_BINARY, "filename");
HttpEntity multipart = multipartEntityBuilder.build();
httpPost.setEntity(multipart);
try {
client.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
}
}
If you go to the target test server bucket:
https://ptsv2.com/t/r7ypk-1613291354
you can see what the full request looks like as received by the server, and see that the payload makes it over just fine as a file with the appropriate filename and contents. The file content portion of the request display looks like this:
Your alternate method worked equally well for me. I chose to post this version as it is simpler, not requiring you to wrap your byte array in an InputStream object.

Kerberos authentication for web request in Java?

I'm trying to implement exactly this: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/lab/part6.html
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
public class RunHttpSpnego {
static final String kuser = "username"; // your account name
static final String kpass = "password"; // your password for the account
static class MyAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
// I haven't checked getRequestingScheme() here, since for NTLM
// and Negotiate, the usrname and password are all the same.
System.err.println("Feeding username and password for "
+ getRequestingScheme());
return (new PasswordAuthentication(kuser, kpass.toCharArray()));
}
}
public static void main(String[] args) throws Exception {
Authenticator.setDefault(new MyAuthenticator());
URL url = new URL(args[0]);
InputStream ins = url.openConnection().getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
String str;
while((str = reader.readLine()) != null)
System.out.println(str);
}
}
I've already reduced to trying just to execute the sample code snippet, but I don't know how to overcome this issue I'm facing.
As I understand, the flow for such requests should look like, as detailed in this document as well:
1. The clients sends a HTTP GET request to the server
2. The server responds with a HTTP 401, and a WWW Authenticate: Negotiate header
3. Then the client sends another HTTP GET request, this time with the proper Negotiate header in the request.
My problem is, when I try to run that sample, the request fails with an IOException obviously, because of the server responding with a 401, and then I don't know how to issue the second request with the header. I was trying to add a header as with Basic Authentication, but in that case I don't know where to get the Negotiate value for that from.
I did a packet capture, I can see the TGS ticket being requested from the KDC, so I think that part is OK, I just don't know how it should be applied to my request. I did the same packet capture with other scripts connecting to the same web service, and I can see the exact same flow what I've described above.
So, to summarize my question: how should I add the authentication header to my request? Do I need to do it explicitely in my code, if so, how do I do it?

How we can create the folder in google drive by using java

I need to create the folder in Google drive by using Java. Does any one tell me the example or how to create the folder in Google drive. Thanks in advance...!!!
My program
package net.sf.dynamicreports.examples;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import com.google.gson.JsonObject;
public class SourceCodeProgram {
public static void main(String argv[]) throws Exception {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost post = new HttpPost(
"https://www.googleapis.com/drive/v2/files");
post.addHeader("Content-Type", "application/json");
post.addHeader("Authorization",
"Bearer XXXXXXXXXXXXXXXXXXXXXXXXX ");
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("title", "Test folder");
jsonObject
.addProperty("mimeType", "application/vnd.google-apps.folder");
post.setEntity(new StringEntity(jsonObject.toString()));
httpClient.execute(post);
}
}
In above program everything is going fine just want to know about the
post.addHeader("Authorization",
"Bearer XXXXXXXXXXXXXXXXXXXXXXXXX ");
What should i place at
XXXXXXXXXXXXXXXXXXXXXXXXX
is it some kind of key which will i get from google?
Google uses OAuth 2.0 authentication, you need to add OAuth Token in place of xxxxxxxx for OAuth 2.0 authentication (Authorization Bearer header is used for OAuth authentication purpose). This may be useful for you : https://developers.google.com/identity/protocols/OAuth2
After successful authentication with Google, you can use following code for creating the folder in Google Drive:
File fileMetadata = new File();
fileMetadata.setName("Invoices");
fileMetadata.setMimeType("application/vnd.google-apps.folder");
File file = driveService.files().create(fileMetadata).setFields("id").execute();
System.out.println("Folder ID: " + file.getId());
P.S. - This is referenced from Google Drive Help Link
Use this Drive API JAVA Quickstart instead and start from there. The 'xxxxxxxx' is not something you can copy and paste from somewhere. It is an access token generated by Google API. But for testing purposes, you can generate it from OAuthplayground, copy and paste in place of xxxxx. It will only last an hour so you also need to implement refresh tokens. Again that's for testing purposes only.
To give you an idea of the access token being generated, you can see a Javascript implementation of this in Picker API:
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token; //<-- access token
createPicker();
}
}

JBPM Console Remote authetication through java

I have deployed jbpm-cosole on my localhost, and I have created a java application to make REST calls for process transaction. So, my question is.
As jbpm-console uses spring-security, how to send authentication request to JBPM?
After successful authentication I want to store authentication token as a java object to make further transactions without login.
or is there any another way to do that please let me know.
This is a sample code which returns the tasks list of a user. You just need to set the authentication details in request header as shown in this code. This works with jbpm-6.1.0.Final.
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
public class JBPMRest {
public static void getTaskSummaryList() throws Exception {
String status = "Reserved";
String actor = "krisv";
String addr = "http://localhost:8080/jbpm-console/rest/task/query?status=" + status + "&potentialOwner=" + actor;
try {
HttpClient client = HttpClientBuilder.create().build();
HttpGet get = new HttpGet(addr);
String authData = "krisv" + ":" + "krisv";
String encoded = new sun.misc.BASE64Encoder().encode(authData.getBytes());
get.setHeader("Content-Type", "application/json");
get.setHeader("Authorization", "Basic " + encoded);
get.setHeader("ACCEPT", "application/xml");
HttpResponse cgResponse = client.execute(get);
String content = EntityUtils.toString(cgResponse.getEntity());
System.out.println(content);
} catch (Exception e) {
throw new Exception("Error consuming service.", e);
}
}
}
You can send basic authentication information (username + password) in the header of your REST call. The remote Java client implementation for example does this as well. If you use the remote Java client, you can reuse the same engine for subsequent calls as well.

Using SAXBuilder With a Proxy URL

How does one handle parsing an XML file that is stored behind a proxy server? I have the following code:
import java.io.IOException;
import java.net.URL;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
public class FileParsing {
public static void parseXMLFile(String xmlFilePath) throws IOException, JDOMException {
URL xmlFileURL = new URL(xmlFilePath);
SAXBuilder builder = new SAXBuilder();
Document xmlDoc = builder.build(xmlFileURL);
// File parsing code...
}
}
Right now I get this exception:
java.io.IOException: Server returned HTTP response code: 502 for URL: https://proxyserver.com/xmlFileOfInterest.xml
I'm assuming this is because the file is on a proxy server. Is this the cause of my problem? And if so, what is the proper way to handle files that are on proxy servers?
you can use apache webclient to parse it
WebClient client = WebClient.create(xmlFilePath
,String.class,null);
client = client.type(MediaType.APPLICATION_XML);
HTTPConduit httpConduit = (HTTPConduit)WebClient.getConfig(client).getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
policy.setProxyServer(proxyurl);
policy.setProxyServerPort(8080);
httpConduit.setClient(policy);
Response r = client.post();
InputStream response= (InputStream)r.getEntity();

Categories