Permalink
Browse files

Merge pull request #2 from m0she/master

Add beforeSave upload verification
  • Loading branch information...
2 parents b5afed7 + 55c8b8f commit 15a6517a1b2d637081855225f55ced9ce9420bfb @TalLevAmi TalLevAmi committed Jun 27, 2013
Showing with 138 additions and 20 deletions.
  1. +6 −3 README.md
  2. +29 −5 cloud/cloudinary/sign.coffee
  3. +55 −12 cloud/cloudinary/sign.js
  4. +48 −0 cloud/main.js
View
@@ -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`
@@ -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"
Oops, something went wrong.
View
@@ -1,4 +1,5 @@
cloudinary = require("cloud/cloudinary/all");
+_ = require('cloud/cloudinary/lib/underscore')
/* Configuration sample:
cloudinary.config({
@@ -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");
@@ -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 15a6517

Please sign in to comment.