Community

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
holmer
Community Member

Error 403 using Upload API (Java)

I am working through the Canvas documentation for doing file uploads. My institution is interested in using this to automatically place our generated PDF report cards into user's files. I am testing by trying to upload a file called test.txt into my own files. Step 1 from the documentation works perfectly. However in Step 2 I keep getting a 403 error. Here is my code. The error happens at the last line of this snippet. Any help is greatly appreciated.

URL url = new URL(urlString);
Map<String,Object> parameters = new LinkedHashMap<>();

for(int i = 0; i < params.length; i++) {
parameters.put(paramNames[i], params[i]);
}
parameters.put("file", new FileReader("U:/Canvas/test.txt"));

StringBuilder postData = new StringBuilder();
for (Map.Entry<String,Object> param : parameters.entrySet()) {
if (postData.length() != 0) postData.append('&');
postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");

HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
conn.setDoOutput(true);
conn.getOutputStream().write(postDataBytes);

Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

26 Replies
Stef_retired
Community Team
Community Team

 @holmer  Welcome to the Canvas Community! Because of the technical nature of your question, I've shared it with the https://community.canvaslms.com/groups/canvas-developers?sr=search&searchId=cd71b04a-225c-4d04-8f43-... group. Their replies will be visible under this question, but if you'd also like to join the group to participate in the discussions there, you can do so by clicking on the link to the group and selecting Join Group from the Actions dropdown at the upper right of the group home page.

holmer
Community Member

Still hoping to get some answers to this question. I'm at a loss now.

cesbrandt
Community Champion

Without the full code, or at least API URLs to see, my best guess is that you are using the incorrect URL or that you lack the permissions to do so.

  1. Do you have an authorization token setup?
  2. What API URL is giving you the forbidden response?
  3. Have you tested with another account with elevated permissions?
holmer
Community Member

Yes my auth token is highest admin rights. I have made other successful calls with it to find and manipulate data for other users. 

I am getting the forbidden response during step 2 when sending Canvas back all of the parameters and the file. URL is https://instructure-uploads.s3.amazonaws.com 

I have not bothered testing with another account because mine is highest level. I feel there is some error with how I am attaching the file in Java.

cesbrandt
Community Champion

As far as I'm aware, calling directly to AWS is forbidden, even by the highest admin rights, though I honestly can't recall if I had ever tried it. I've only ever done file uploading via the Canvas API. This would make the API call something like:

curl 'https://<domain>/api/v1/users/123456/files' -F 'name=<filename>' -F 'size=<filesize>' -F 'content_type=<filetype>' -F 'parent_folder_id=<foldertocontainfile>' -F 'parent_folder_path=<pathtofile>' -F 'onduplicate=<duplicatehandling>' -H 'Authorization: Bearer <token>'‍‍

Note: You need parent_folder_id OR parent_folder_path OR neither, you can't have both. In the event that neither is supplied, it will default to the target files area root.

Note: You do not need to specify on_duplicate if you want the files to be overwritten, which is the default value. The only valid settings are overwrite and rename.

Here's an example of a completed cURL:

curl 'https://canvas.instructure.com/api/v1/users/123456/files' -F 'name=test.txt' -F 'size=1024' -F 'content_type=text/plain' -F 'parent_folder_path=test_file_area' -H 'Authorization: Bearer ...'‍
holmer
Community Member

Step 2 of the documentation says "Depending on how Canvas is configured, this upload URL might be another URL in the same domain, or a Amazon S3 bucket, or some other URL. In order to work with all Canvas installations, applications should be very careful to follow this documentation and not make any undocumented assumptions about the upload workflow."

So why do you say that calling AWS is forbidden? AWS is the URL I am receiving from step 1.

cesbrandt
Community Champion

My apologies, I have difficulty connecting dots of information and misunderstood. ^^'

It is strange. I'm actually getting the same results with my root_admin account, too. I can upload to /users/self/files/users/:my_id/files, and /courses/:any_id/files without issue, but /users/:someone_elses_id/files is giving me the unauthorized error, also. Just to be sure, I actually tested the cURL from two locations using 3 different languages for execution.

I'm going to look into this more, but thus far I've been unable to locate any reason for this error in the documentation.

cesbrandt
Community Champion

 @holmer ‌, with the help ToeBee on IRC, we successfully tested uploading files to another users' files area by using masquerading. So when you run the first cURL, do so while masquerading as the user whose files area you are trying to load the file to. The rest should work fine without it, at least, it worked when I tested it.

We're still working on making it work for uploading via URL.

cesbrandt
Community Champion

I stand corrected regarding loading from a URL. When I pulled up my code this morning, I spotted the typo I had. Masquerading is confirmed to work for uploading to another users' files area from upload and URL.

I've no idea why it requires masquerading to work, but that's what seems to do the trick. I'd suggest submitting a ticket to Instructure about it if you'd like to know whether it is intentional that it is needed or not.