[ARVADOS] updated: 297f48f2e6233da1e61cda57af30c9c6b64f0908

git at public.curoverse.com git at public.curoverse.com
Tue May 6 23:44:36 EDT 2014


Summary of changes:
 services/keep/src/keep/keep.go      |   10 ++-
 services/keep/src/keep/keep_test.go |  145 ++++++++++++++++++++++++++++++++++-
 services/keep/src/keep/perms.go     |    5 +-
 3 files changed, 155 insertions(+), 5 deletions(-)

       via  297f48f2e6233da1e61cda57af30c9c6b64f0908 (commit)
      from  b63b4b51a7bdaa38960e0b4428141c16c0a07d7a (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 297f48f2e6233da1e61cda57af30c9c6b64f0908
Author: Tim Pierce <twp at curoverse.com>
Date:   Tue May 6 23:40:59 2014 -0400

    Require enforce_permissions and API token as necessary.
    
    Unqualified /index requests require enforce_permissions to be enabled
    and an API token to be supplied with the request.
    
    SignLocator should return an unsigned locator if no API token was
    supplied.
    
    Refs #2328

diff --git a/services/keep/src/keep/keep.go b/services/keep/src/keep/keep.go
index 278023b..8cb45cc 100644
--- a/services/keep/src/keep/keep.go
+++ b/services/keep/src/keep/keep.go
@@ -312,9 +312,15 @@ func PutBlockHandler(w http.ResponseWriter, req *http.Request) {
 func IndexHandler(w http.ResponseWriter, req *http.Request) {
 	prefix := mux.Vars(req)["prefix"]
 
-	// Only the data manager may issue unqualified "GET /index" requests.
+	// Only the data manager may issue unqualified "GET /index" requests,
+	// and only if enforce_permissions is enabled.
+	// If the request is unauthenticated, or does not match the data manager's
+	// API token, return 403 Permission denied.
 	if prefix == "" {
-		if data_manager_token != GetApiToken(req) {
+		api_token := GetApiToken(req)
+		if !enforce_permissions ||
+			api_token == "" ||
+			data_manager_token != GetApiToken(req) {
 			http.Error(w, PermissionError.Error(), PermissionError.HTTPCode)
 			return
 		}
diff --git a/services/keep/src/keep/keep_test.go b/services/keep/src/keep/keep_test.go
index 77cf3b5..d3536d6 100644
--- a/services/keep/src/keep/keep_test.go
+++ b/services/keep/src/keep/keep_test.go
@@ -351,7 +351,7 @@ func TestIndex(t *testing.T) {
 	match, err := regexp.MatchString(expected, index)
 	if err == nil {
 		if !match {
-			t.Errorf("IndexLocators returned:\n-----\n%s-----\n", index)
+			t.Errorf("IndexLocators returned:\n%s", index)
 		}
 	} else {
 		t.Errorf("regexp.MatchString: %s", err)
@@ -481,6 +481,148 @@ func TestGetHandler(t *testing.T) {
 	}
 }
 
+func TestPutHandler(t *testing.T) {
+	defer teardown()
+
+	// Prepare two test Keep volumes.
+	KeepVM = MakeTestVolumeManager(2)
+	defer func() { KeepVM.Quit() }()
+
+	// Set up a REST router for testing the handlers.
+	rest := NewRESTRouter()
+
+	// Execute a PUT request.
+	test_url := "http://localhost:25107/" + TEST_HASH
+	test_body := bytes.NewReader(TEST_BLOCK)
+	req, _ := http.NewRequest("PUT", test_url, test_body)
+	resp := httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != 200 {
+		t.Errorf("bad response code: %v", resp)
+	}
+	if resp.Body.String() != TEST_HASH {
+		t.Errorf("bad response body: %v", resp)
+	}
+
+	// Add a permission key.
+	// When a permission key is available, the locator returned
+	// from a PUT request will be signed.
+	PermissionSecret = []byte(known_key)
+
+	// An authenticated PUT request returns a signed locator.
+	test_url = "http://localhost:25107/" + TEST_HASH
+	test_body = bytes.NewReader(TEST_BLOCK)
+	req, _ = http.NewRequest("PUT", test_url, test_body)
+	req.Header.Set("Authorization", "OAuth "+known_token)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != 200 {
+		t.Errorf("bad response code: %v", resp)
+	}
+	if !VerifySignature(resp.Body.String(), known_token) {
+		t.Errorf("bad response body: %v", resp)
+	}
+
+	// An unauthenticated PUT request returns an unsigned locator
+	// even when a permission key is available.
+	test_url = "http://localhost:25107/" + TEST_HASH
+	test_body = bytes.NewReader(TEST_BLOCK)
+	req, _ = http.NewRequest("PUT", test_url, test_body)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != 200 {
+		t.Errorf("bad response code: %v", resp)
+	}
+	if resp.Body.String() != TEST_HASH {
+		t.Errorf("bad response body: %v", resp)
+	}
+}
+
+func TestIndexHandler(t *testing.T) {
+	defer teardown()
+
+	// Set up Keep volumes and populate them.
+	// Include multiple blocks on different volumes, and
+	// some metadata files.
+	KeepVM = MakeTestVolumeManager(2)
+	defer func() { KeepVM.Quit() }()
+
+	vols := KeepVM.Volumes()
+	vols[0].Put(TEST_HASH, TEST_BLOCK)
+	vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
+
+	// Set up a REST router for testing the handlers.
+	rest := NewRESTRouter()
+
+	// Requests for /index with a prefix are okay even if unauthenticated.
+	test_url := "http://localhost:25107/index/" + TEST_HASH[0:5]
+	req, _ := http.NewRequest("GET", test_url, nil)
+	resp := httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	expected := `^` + TEST_HASH + `\+\d+ \d+\n$`
+	match, _ := regexp.MatchString(expected, resp.Body.String())
+	if !match {
+		t.Errorf("IndexHandler returned:\n%s", resp.Body.String())
+	}
+
+	// Unauthenticated /index requests: fail.
+	test_url = "http://localhost:25107/index"
+	req, _ = http.NewRequest("GET", test_url, nil)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != PermissionError.HTTPCode {
+		t.Errorf("unauthenticated /index: %+v", resp)
+	}
+
+	// Authenticated /index requests by a non-superuser: also fail.
+	test_url = "http://localhost:25107/index"
+	req, _ = http.NewRequest("GET", test_url, nil)
+	req.Header.Set("Authorization", "OAuth "+known_token)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != PermissionError.HTTPCode {
+		t.Errorf("authenticated /index: %+v", resp)
+	}
+
+	// Even superuser /index requests fail if enforce_permissions is off!
+	enforce_permissions = false
+	data_manager_token = "DATA MANAGER TOKEN"
+	test_url = "http://localhost:25107/index"
+	req, _ = http.NewRequest("GET", test_url, nil)
+	req.Header.Set("Authorization", "OAuth "+data_manager_token)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != PermissionError.HTTPCode {
+		t.Errorf("superuser /index (permissions off): %+v", resp)
+	}
+
+	// Superuser /index requests with enforce_permissions set: succeed!
+	enforce_permissions = true
+	data_manager_token = "DATA MANAGER TOKEN"
+	test_url = "http://localhost:25107/index"
+	req, _ = http.NewRequest("GET", test_url, nil)
+	req.Header.Set("Authorization", "OAuth "+data_manager_token)
+	resp = httptest.NewRecorder()
+	rest.ServeHTTP(resp, req)
+
+	if resp.Code != http.StatusOK {
+		t.Errorf("superuser /index: %+v", resp)
+	}
+	expected = `^` + TEST_HASH + `\+\d+ \d+\n` +
+		TEST_HASH_2 + `\+\d+ \d+\n$`
+	match, _ = regexp.MatchString(expected, resp.Body.String())
+	if !match {
+		t.Errorf("superuser /index:\n%s", resp.Body.String())
+	}
+}
+
 // ========================================
 // Helper functions for unit tests.
 // ========================================
@@ -501,6 +643,7 @@ func MakeTestVolumeManager(num_volumes int) VolumeManager {
 //     Cleanup to perform after each test.
 //
 func teardown() {
+	data_manager_token = ""
 	enforce_permissions = false
 	PermissionSecret = nil
 	KeepVM = nil
diff --git a/services/keep/src/keep/perms.go b/services/keep/src/keep/perms.go
index 3ad7bb0..0d1b091 100644
--- a/services/keep/src/keep/perms.go
+++ b/services/keep/src/keep/perms.go
@@ -66,8 +66,9 @@ func MakePermSignature(blob_hash string, api_token string, expiry string) string
 // SignLocator takes a blob_locator, an api_token and an expiry time, and
 // returns a signed locator string.
 func SignLocator(blob_locator string, api_token string, expiry time.Time) string {
-	// If the permission secret has not been set, return an unsigned locator.
-	if PermissionSecret == nil {
+	// 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.

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list