X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/14f6c56b96fb8b7ccd104951f9e8374540f16fa5..da58ec28659f5167f9658be5714731acee57dfb1:/services/keepstore/perms.go?ds=sidebyside diff --git a/services/keepstore/perms.go b/services/keepstore/perms.go index 143815576b..7205a4594d 100644 --- a/services/keepstore/perms.go +++ b/services/keepstore/perms.go @@ -1,109 +1,33 @@ -/* -Permissions management on Arvados locator hashes. +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 -The permissions structure for Arvados is as follows (from -https://arvados.org/issues/2328) - -A Keep locator string has the following format: - - [hash]+[size]+A[signature]@[timestamp] - -The "signature" string here is a cryptographic hash, expressed as a -string of hexadecimal digits, and timestamp is a 32-bit Unix timestamp -expressed as a hexadecimal number. e.g.: - - acbd18db4cc2f85cedef654fccc4a4d8+3+A257f3f5f5f0a4e4626a18fc74bd42ec34dcb228a@7fffffff - -The signature represents a guarantee that this locator was generated -by either Keep or the API server for use with the supplied API token. -If a request to Keep includes a locator with a valid signature and is -accompanied by the proper API token, the user has permission to GET -that object. - -The signature may be generated either by Keep (after the user writes a -block) or by the API server (if the user has can_read permission on -the specified object). Keep and API server share a secret that is used -to generate signatures. - -To verify a permission hint, Keep generates a new hint for the -requested object (using the locator string, the timestamp, the -permission secret and the user's API token, which must appear in the -request headers) and compares it against the hint included in the -request. If the permissions do not match, or if the API token is not -present, Keep returns a 401 error. -*/ - -package main +package keepstore import ( - "crypto/hmac" - "crypto/sha1" - "fmt" - "regexp" - "strconv" - "strings" "time" -) - -// The PermissionSecret is the secret key used to generate SHA1 -// digests for permission hints. apiserver and Keep must use the same -// key. -var PermissionSecret []byte -// MakePermSignature returns a string representing the signed permission -// hint for the blob identified by blob_hash, api_token and expiration timestamp. -func MakePermSignature(blob_hash string, api_token string, expiry string) string { - hmac := hmac.New(sha1.New, PermissionSecret) - hmac.Write([]byte(blob_hash)) - hmac.Write([]byte("@")) - hmac.Write([]byte(api_token)) - hmac.Write([]byte("@")) - hmac.Write([]byte(expiry)) - digest := hmac.Sum(nil) - return fmt.Sprintf("%x", digest) -} + "git.arvados.org/arvados.git/sdk/go/arvados" + "git.arvados.org/arvados.git/sdk/go/keepclient" +) -// SignLocator takes a blob_locator, an api_token and an expiry time, and +// SignLocator takes a blobLocator, an apiToken and an expiry time, and // returns a signed locator string. -func SignLocator(blob_locator string, api_token string, expiry time.Time) string { - // If no permission secret or API token is available, - // return an unsigned locator. - if PermissionSecret == nil || api_token == "" { - return blob_locator - } - // Extract the hash from the blob locator, omitting any size hint that may be present. - blob_hash := strings.Split(blob_locator, "+")[0] - // Return the signed locator string. - timestamp_hex := fmt.Sprintf("%08x", expiry.Unix()) - return blob_locator + - "+A" + MakePermSignature(blob_hash, api_token, timestamp_hex) + - "@" + timestamp_hex -} - -// VerifySignature returns true if the signature on the signed_locator -// can be verified using the given api_token. -func VerifySignature(signed_locator string, api_token string) bool { - if re, err := regexp.Compile(`^([a-f0-9]{32}(\+[0-9]+)?).*\+A[[:xdigit:]]+@([[:xdigit:]]{8})`); err == nil { - if matches := re.FindStringSubmatch(signed_locator); matches != nil { - blob_locator := matches[1] - timestamp_hex := matches[3] - if expire_ts, err := ParseHexTimestamp(timestamp_hex); err == nil { - // Fail signatures with expired timestamps. - if expire_ts.Before(time.Now()) { - return false - } - return signed_locator == SignLocator(blob_locator, api_token, expire_ts) - } - } - } - return false +func SignLocator(cluster *arvados.Cluster, blobLocator, apiToken string, expiry time.Time) string { + return keepclient.SignLocator(blobLocator, apiToken, expiry, cluster.Collections.BlobSigningTTL.Duration(), []byte(cluster.Collections.BlobSigningKey)) } -func ParseHexTimestamp(timestamp_hex string) (ts time.Time, err error) { - if ts_int, e := strconv.ParseInt(timestamp_hex, 16, 0); e == nil { - ts = time.Unix(ts_int, 0) - } else { - err = e +// VerifySignature returns nil if the signature on the signedLocator +// can be verified using the given apiToken. Otherwise it returns +// either ExpiredError (if the timestamp has expired, which is +// something the client could have figured out independently) or +// PermissionError. +func VerifySignature(cluster *arvados.Cluster, signedLocator, apiToken string) error { + err := keepclient.VerifySignature(signedLocator, apiToken, cluster.Collections.BlobSigningTTL.Duration(), []byte(cluster.Collections.BlobSigningKey)) + if err == keepclient.ErrSignatureExpired { + return ExpiredError + } else if err != nil { + return PermissionError } - return ts, err + return nil }