parse.com: cloud code to return a PFFile - parse-platform

Background...
I'm exploring Parse.com as a back end for an iOS app that also has an html/web browser interface for some users (either via javascript client or asp.net client - to be determined). The web users are 'an audience' for the data/files the app users prepare in the app. They are not the same people.
I need to lock down access to objects in the database (no public access read or write) so I plan to set up an admin user belonging to an administrators role and create an app_users role applying class-level permissions to the various classes accordingly.
Then for my iOS app, using the anonymous users add them to the app_Users role, setting up a default ACL for object level permissions and interact with the data model accordingly.
The app creates PDF files and stores as PFFile objects and I want these to have no public read or write access too. these docs are what will be accessible via the web client.
So...
I don't think i want to use PFUsers for each potential user accessing via a web client -don't want it to over engineered. So I figured send params to Cloud Code (with useMasterKey()) to first return a list of file meta data to present to the user - this works well - I can return the PFFile url or objectId, doc name, file type and size...
The challenge...
Next I'd need to build a Cloud Code function which given objectId or a url will fetch the PDF file and return it in a way my web page can display it to the user.
I've seen a few examples in the Networking section of the docs looks like it might be possible but I can seem to join the dots.
Hope that makes sense - any thoughts?
Edit: Added Code
The code I've been looking at works for text/html - is it possible to response a PDF or binary
Parse.Cloud.httpRequest({
url:'example.com/file.pdf',
 success: function(httpResponse) {
console.log(httpResponse.text);
},
error: function(httpResponse) {
console.error('Request failed: ' + httpResponse.status);
});

Related

How to access private files in s3 storage by laravel api for android application

Each user has a number of private files (photos, videos, etc.) stored on the s3 disk.
From the mobile application side, I send a request to Laravel web service to get the list of files and show it to the user on the client side.
I use the resource collection in Laravel to send responses and send my list to the mobile application.
My question is, how can I access the file itself using the file path on the client side?
Do I need to send a request to Laravel for each file to request a download type response for each file?
Considering that the number of files is more than one file and I want to show a list view inside the mobile application and I don't want to send a request to the server for each photo and download the photo.
I want the accessible links to be returned as soon as I get the list from the laravel app so that I can display them on the app side.
Laravel Side:
Route::get('api/user/{user}/files', function (User $user){
$files = $user->files();
return new FileCollection($files);
});
Route::get('api/download/{path}', function (string $path){
return Storage::disk('s3')->download($path);
});
Client Side:
What do I do here?
You can call Storage::disk('s3')->temporaryUrl($path, now()->addMinute()) to generate publicly accessible links for private files (links are going to expire in 1 minute in this example).

The requested application with ID xxxxxx was not found

I 'm trying to post/get score from google play leaderboards
I met all the meta-tag from the documentation including my client id
<meta name="google-signin-client_id" content="XXXXXX-YYYYYYYYYYY.apps.googleusercontent.com" />
I also have setup the google sign-in system and all is fine, however when I try to call the leaderboards API I get the message error: The requested application with ID xxxxxx was not found
I am calling the API like the mentioned in the doc
gapi.client.request({
path: '/games/v1/leaderboards/LEADERBOARD-ID',
params: { maxResults: 3 },
callback: function(response) {
console.log(response);
}
});
I am not sure if the problem is the missing argument to execute a request.
Try to use this API requests method.
gapi.client.Request
An object encapsulating an HTTP request. This object is not
instantiated directly, rather it is returned by gapi.client.request.
There are two ways to execute a request. We recommend that you treat
the object as a promise and use the then method, but you can also use
the execute method and pass in a callback.
You can refer to this Github post for additional reference.
This message: W/AchievementAgent( 3558):
{"code":404,"errors":[{"message":"The requested application with ID
571707973781 was not found.","domain":"global","reason":"notFound "}]}
is a little cryptic but points to a mismatch with the auth
configuration on the console and the application.
You'll want to double check the keystore SHA1 fingerprint of the
keystore you signed the app with and the one configured in the dev
console.
It could also be the bundle ID, but that is hard to mess up since it
is part of the resource data used when running Setup for the plugin.
Also, it could be that the player is not a tester for this game.
For anyone having the same issue, you need to publish the beta version of the game to be able to interact with the game scoreboard.
Note: In the beta version, only tester accounts added to the game can access the scoreboard.

Running Google Picker with offline access oAuth token

What I am doing:
I am integrating Google Picker on my page. This will allow users to select files from their Google Drive to be used in the web app. In the app, people in a group share a common google drive (i.e. they all can select files from account example#email.com) which was created by group admin by his email address. When the admin signs-up for the account we do OAuth and get access_token with refresh_token against our app on google (with offline access enabled). I plan to use the access_token and refresh-token of the admin, on other group user's account when they try to use picker to select files.
What I have done:
I have integrated the Google Picker successfully in my app using the basic code provided in docs. Then to achieve what I wanted, I removed following code from the example code:
gapi.load('auth', {'callback': onAuthApiLoad});
and
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
and
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
createPicker();
}
}
and instead of .setOAuthToken(oauthToken) I pass refreshed access_token directly as string (I get that from my server with an ajax call).
.setOAuthToken("<access_token>")
But every time I call picker.setVisible(true); I get a screen in an iframe saying In order to select an item from your online storage, please sign in.
Problem:
Try to add sign in listener. Listeners provide a way to automatically respond to changes in the current user's Sign-In session. For example, after your startup method initializes the Google Sign-In auth2 object, you can set up listeners to respond to events like auth2.isSignedIn state changes, or changes in auth2.currentUser.
Validating the token might be a possibility before using the token each time but that might add a lot of extra overhead for a rare use-case each time we load the picker and when calling the API endpoints with a token after the re-authentication issue, there was no key about the token being invalid. You can validate a token by making a web service request to an endpoint on the Google Authorization Server and performing a string match on the results of that web service request.

Parse Server Migration request.installationId

Am i migrating a working Parse app.
I use request.installationId on both before and after save events to load the installation object (session) of the acting user as we have some metadata attached to it.
On my migrated app. This seems to be causing an issue. The request.installationId is returning a different uuid on the before and after event. The after event is correct the before is incorrect.
Is this no longer reliable?
It seems the error was caused by not properly passing the users session token with each API call. When migrating to Parse Server from the old hosted parse platform now closed. You need to adapt all of your cloud based api calls for querys, stored procedure calls to include either a users session or the flag to run the call with the master key.
// calling a stored function, note the session token is passed in;
return Parse.Cloud.run('getUserRole', data, {
sessionToken: request.user.getSessionToken()
});
// If you want to run using the master key
return Parse.Cloud.run('getUserRole', data, {
useMasterKey: true
});

Hot Towel/Durandal/Breeze.js: how to vertically secure data calls?

I'm new to the whole client-side SPA world. I'm using the above technologies, which seem quite promising. However, one huge snag that I can't get over easily is the lack of built-in security. I had to manually roll out the user authorization, which IMHO should be part of the framework.
Now that I have that sorted, I'm getting major headaches with vertical security: where one user is logged in but can easily access other users' info by changing a few parameters in the browser console. I could pass the userId with every call and then compare it with the one on the server, but I was hoping that there would be an overarching solution that doesn't pollute the breeze data calls with user ids.
For example, let's say there's a call from the data service like this:
function getItems(){
var query = breeze.EntityQuery.from('Items').expand("Person");
return manager.executeQuery(query);
}
this will get all the items, not good. So let's limit by userId:
function getItems(userId){
var query = breeze.EntityQuery.from('Items').where("userId", "==", authentication.userId).expand("Person");
return manager.executeQuery(query);
}
in the second example, we get the userId from the authentication service, which stored the userId when the user was logged in. However, a malicious user can easily go the browser console and change that value.
Of course, I could pass the userId using withParameters(...) and compare it with the current one on the server, but I'd have to do that for every call, which doesn't seem right. Is there a better way to secure calls with the trusted user id?
#Ali - I understand your pain and your concern. You are right to fear any form of so-called security that relies on information passed in the URL. Fortunately there are excellent answers to your concerns and Breeze apps work well with them.
For example, have you studied the ASP.NET Breeze/Knockout Template? It uses Forms Auth for authentication and guards the Web API controller with an [Authorize] attribute. Only logged-in users can access any of the controller methods.
That authentication also sets the IPrincipal which the Web API controller makes available through its User property. You'll see User passed to the constructor of the TodoRepository. In that repository you'll find guard logic to restrict query and saves to just the Todo information belonging to the requesting user.
Look at the network traffic. You won't find any user identifying information in the URL or the request/response bodies. You will see an encrypted authentication cookie in a header.
An obvious flaw in the example is that the client/server traffic takes place in the clear. You must add transport level security (HTTPS) before you go to production. But this is a demo after all.
Why not just do this in the controller?
If the Web Api is secured with [Authorize] then you can get the users ID in the controller and make sure that the data returned is for the currently logged in user.

Resources