Skip to content
This repository has been archived by the owner on Dec 7, 2017. It is now read-only.

Commit

Permalink
Add beforeSave upload verification
Browse files Browse the repository at this point in the history
  • Loading branch information
m0she committed Jun 26, 2013
1 parent b5afed7 commit 55c8b8f
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 20 deletions.
9 changes: 6 additions & 3 deletions README.md
Expand Up @@ -5,9 +5,12 @@ Also provided in this package is sample code of a simple Cloud Function that sig

## Files

* `cloud/main.js` - The main cloud code file loaded by parse. Contains a sample cloud function (`sign_upload_request`) that returns an object with all required parameters to initiate a direct upload to cloudinary.
The sample function requires an authenticated Parse user and embeds the username into the tags field.
The returned data can be used to construct an HTML form or passed to cloudinary front-end libraries to initiate an image upload.
* `cloud/main.js` - The main cloud code file loaded by parse. Contains:
* A sample cloud function (`sign_upload_request`) that returns an object with all required parameters to initiate a direct upload to cloudinary.
The function requires an authenticated Parse user and embeds the username into the tags field.
The returned data can be used to construct an HTML form or passed to cloudinary front-end libraries to initiate an image upload.
* A sample beforeSave factory (`beforeSaveFactory`). When given an `object_name` and a `field_name`, creates a beforeSave function that verifies updates to `field_name` are only done with a signed cloudinary identifier.
The beforeSave function also removes the signatures when saving to the database.
* `cloud/cloudinary/all.js` - The cloudinary library entrypoint. In order to load cloudinary library you must `require('cloud/cloudinary/all')` the result of this expression is the cloudinary namespace. See `cloud/main.js` for sample usage.
* `cloud/cloudinary_config.js` holds cloudinary configuration as demonstrated in `cloud/cloudinary_config.js.sample`

Expand Down
34 changes: 29 additions & 5 deletions cloud/cloudinary/sign.coffee
Expand Up @@ -4,18 +4,42 @@ config = require('cloud/cloudinary/config.js')

exports.sign_upload_request = (params) ->
params = build_upload_params(params)
params.signature = sign_request(params)
params.api_key = config().api_key
if !params.api_key?
throw "Must supply api_key"
params

identifier_pattern = ///
^(?:([^/]+)/)??
(?:([^/]+)/)??
(?:v(\d+)/)?
(?:([^#/]+?)
(?:\.([^.#/]+))?)
(?:#([^/]+))?$
///
exports.verify_upload = (identifier) ->
[match, resource_type, image_type, version, public_id, format, signature] =
identifier.match(identifier_pattern) || []
expected_signature = sign_request(public_id: public_id, version: version)
console.log 'Signing '
console.log public_id: public_id, version: version, expected: expected_signature, given: signature
expected_signature == signature
exports.remove_signature = (identifier) ->
identifier.replace /#.*$/, ''
sign_request = (params) ->
for k, v of params when not present(v)
delete params[k]
api_secret = config().api_secret
if !api_secret?
throw "Must supply api_secret"
params.signature = api_sign_request(params, api_secret)
params.api_key = config().api_key
if !params.api_key?
throw "Must supply api_key"
return params
api_sign_request(params, api_secret)
get_api_url = (action = 'upload', options = {}) ->
cloudinary = options["upload_prefix"] ? config().upload_prefix ? "https://api.cloudinary.com"
Expand Down
67 changes: 55 additions & 12 deletions cloud/cloudinary/sign.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions cloud/main.js
@@ -1,4 +1,5 @@
cloudinary = require("cloud/cloudinary/all");
_ = require('cloud/cloudinary/lib/underscore')

/* Configuration sample:
cloudinary.config({
Expand All @@ -7,6 +8,14 @@ cloudinary = require("cloud/cloudinary/all");
});
/**/



/**
* The following declaration exposes a cloud code function that enables you
* to sign a direct-upload request from your app.
* @note This function assumes no extra parameters are needed for the upload.
* @note This function embeds the username in the cloudinary tags field.
*/
Parse.Cloud.define("sign_upload_request", function(request, response) {
if (!request.user || !request.user.authenticated()) {
response.error("Needs an authenticated user");
Expand All @@ -16,3 +25,42 @@ Parse.Cloud.define("sign_upload_request", function(request, response) {
cloudinary.sign_upload_request({tags: request.user.getUsername()})
);
});

/**
* This factory creates a beforeSave filter that verifies that a given
* cloudinary-identifier field in your object is a valid (has correct signature)
*
* @note This function allows changing of other fields without validation
*/
function beforeSaveFactory(object_name, field_name) {
Parse.Cloud.beforeSave(object_name, function(request, response) {
var identifier = request.object.get(field_name);
function verify_upload(previous_value) {
if (identifier === previous_value || cloudinary.verify_upload(identifier)) {
// Remove signature and store
request.object.set(field_name, cloudinary.remove_signature(identifier));
response.success();
} else {
response.error("Bad signature");
}
}

(new Parse.Query(object_name)).get(request.object.id, {
success: function(previous) {
verify_upload(previous.get(field_name));
},
error: function(object, error) {
if (error.code == Parse.Error.OBJECT_NOT_FOUND) {
return verify_upload(new Parse.Object);
}
response.error(error);
}
});
});
}

/// The following lines install a beforeSave filter for the given field within the given object
var object_name = "Photo";
var field_name = "cloudinary_identifier";
beforeSaveFactory(object_name, field_name);

0 comments on commit 55c8b8f

Please sign in to comment.