Code review for 1844-blob-signature: documentation, slight refactoring (refs #1844)
authorTim Pierce <twp@curoverse.com>
Fri, 7 Mar 2014 19:35:49 +0000 (14:35 -0500)
committerTim Pierce <twp@curoverse.com>
Fri, 7 Mar 2014 19:35:49 +0000 (14:35 -0500)
services/api/app/models/blob.rb

index 0ba299a45bd29e38758f12902dc7abcfc327e012..11fab9fb59b6c0369c0519d9553e798665ae8933 100644 (file)
@@ -1,7 +1,28 @@
 class Blob
+
+  # In order to get a Blob from Keep, you have to prove either
+  # [a] you have recently written it to Keep yourself, or
+  # [b] apiserver has recently decided that you should be able to read it
+  #
+  # To ensure that the requestor of a blob is authorized to read it,
+  # Keep requires clients to timestamp the blob locator with an expiry
+  # time, and to sign the timestamped locator with their API token.
+  #
+  # A signed blob locator has the form:
+  #     locator_hash +A blob_signature @ timestamp
+  # where the timestamp is a Unix time expressed as a hexadecimal value,
+  # and the blob_signature is the signed locator_hash + API token + timestamp.
+  # 
   class InvalidSignatureError < StandardError
   end
 
+  # Blob.sign_locator: return a signed and timestamped blob locator.
+  #
+  # The 'opts' argument should include:
+  #   [required] :key       - the Arvados server-side blobstore key
+  #   [required] :api_token - user's API token
+  #   [optional] :ttl       - number of seconds before this request expires
+  #
   def self.sign_locator blob_locator, opts
     # We only use the hash portion for signatures.
     blob_hash = blob_locator.split('+').first
@@ -12,14 +33,16 @@ class Blob
 
     # Generate a signature.
     signature =
-      OpenSSL::HMAC.hexdigest('sha1', opts[:key],
-                              [blob_hash,
-                               opts[:api_token],
-                               timestamp].join('@'))
+      generate_signature opts[:key], blob_hash, opts[:api_token], timestamp
 
     blob_locator + '+A' + signature + '@' + timestamp
   end
 
+  # Blob.verify_signature
+  #   Safely verify the signature on a blob locator.
+  #   Return value: true if the locator has a valid signature, false otherwise
+  #   Arguments: signed_blob_locator, opts
+  #
   def self.verify_signature *args
     begin
       self.verify_signature! *args
@@ -29,6 +52,14 @@ class Blob
     end
   end
 
+  # Blob.verify_signature!
+  #   Verify the signature on a blob locator.
+  #   Return value: true if the locator has a valid signature
+  #   Arguments: signed_blob_locator, opts
+  #   Exceptions:
+  #     Blob::InvalidSignatureError if the blob locator does not include a
+  #     valid signature
+  #
   def self.verify_signature! signed_blob_locator, opts
     blob_hash = signed_blob_locator.split('+').first
     given_signature, timestamp = signed_blob_locator.
@@ -47,14 +78,19 @@ class Blob
     end
 
     my_signature =
-      OpenSSL::HMAC.hexdigest('sha1', opts[:key],
-                              [blob_hash,
-                               opts[:api_token],
-                               timestamp].join('@'))
+      generate_signature opts[:key], blob_hash, opts[:api_token], timestamp
+
     if my_signature != given_signature
       raise Blob::InvalidSignatureError.new 'Signature is invalid.'
     end
 
     true
   end
+
+  def self.generate_signature key, blob_hash, api_token, timestamp
+    OpenSSL::HMAC.hexdigest('sha1', key,
+                            [blob_hash,
+                             api_token,
+                             timestamp].join('@'))
+  end
 end