[ARVADOS] created: 4faccebcdd7872eca76b183951a2850518d35916
git at public.curoverse.com
git at public.curoverse.com
Wed Nov 26 00:47:27 EST 2014
at 4faccebcdd7872eca76b183951a2850518d35916 (commit)
commit 4faccebcdd7872eca76b183951a2850518d35916
Author: Tom Clegg <tom at curoverse.com>
Date: Sun Nov 23 02:09:11 2014 -0500
3781: Add allowed headers. Respond to OPTIONS at any path.
diff --git a/services/keepproxy/keepproxy.go b/services/keepproxy/keepproxy.go
index 620434f..e547efd 100644
--- a/services/keepproxy/keepproxy.go
+++ b/services/keepproxy/keepproxy.go
@@ -248,7 +248,7 @@ func MakeRESTRouter(
rest.Handle(`/{hash:[0-9a-f]{32}}+{hints}`, PutBlockHandler{kc, t}).Methods("PUT")
rest.Handle(`/{hash:[0-9a-f]{32}}`, PutBlockHandler{kc, t}).Methods("PUT")
rest.Handle(`/`, PutBlockHandler{kc, t}).Methods("POST")
- rest.Handle(`/{hash:[0-9a-f]{32}}{ignore}`, OptionsHandler{}).Methods("OPTIONS")
+ rest.Handle(`/`, OptionsHandler{}).Methods("OPTIONS")
}
rest.NotFoundHandler = InvalidPathHandler{}
@@ -256,6 +256,13 @@ func MakeRESTRouter(
return rest
}
+func SetCorsHeaders(resp http.ResponseWriter) {
+ resp.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, OPTIONS")
+ resp.Header().Set("Access-Control-Allow-Origin", "*")
+ resp.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, X-Keep-Desired-Replicas")
+ resp.Header().Set("Access-Control-Max-Age", "86486400")
+}
+
func (this InvalidPathHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
log.Printf("%s: %s %s unroutable", GetRemoteAddress(req), req.Method, req.URL.Path)
http.Error(resp, "Bad request", http.StatusBadRequest)
@@ -263,15 +270,11 @@ func (this InvalidPathHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
func (this OptionsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
log.Printf("%s: %s %s", GetRemoteAddress(req), req.Method, req.URL.Path)
- resp.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, OPTIONS")
- resp.Header().Set("Access-Control-Allow-Origin", "*")
- resp.Header().Set("Access-Control-Allow-Headers", "Authorization, X-Keep-Desired-Replicas")
- resp.Header().Set("Access-Control-Max-Age", "86486400")
+ SetCorsHeaders(resp)
}
func (this GetBlockHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
- resp.Header().Set("Access-Control-Allow-Origin", "*")
- resp.Header().Set("Access-Control-Allow-Headers", "Authorization")
+ SetCorsHeaders(resp)
kc := *this.KeepClient
@@ -335,6 +338,7 @@ func (this GetBlockHandler) ServeHTTP(resp http.ResponseWriter, req *http.Reques
}
func (this PutBlockHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ SetCorsHeaders(resp)
kc := *this.KeepClient
diff --git a/services/keepproxy/keepproxy_test.go b/services/keepproxy/keepproxy_test.go
index 667a0b2..0ac843f 100644
--- a/services/keepproxy/keepproxy_test.go
+++ b/services/keepproxy/keepproxy_test.go
@@ -349,7 +349,7 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
fmt.Sprintf("http://localhost:29954/%x+3",
md5.Sum([]byte("foo"))))
c.Check(err, Equals, nil)
- c.Check(resp.Header.Get("Access-Control-Allow-Headers"), Equals, "Authorization")
+ c.Check(resp.Header.Get("Access-Control-Allow-Headers"), Equals, "Authorization, Content-Length, Content-Type, X-Keep-Desired-Replicas")
c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*")
}
}
commit 500fb090fb7c295000c485ca7e2214ad448c40dc
Author: Tom Clegg <tom at curoverse.com>
Date: Sat Nov 22 15:45:14 2014 -0500
3781: Add POST method for writing without knowing MD5.
diff --git a/services/keepproxy/keepproxy.go b/services/keepproxy/keepproxy.go
index b74a982..620434f 100644
--- a/services/keepproxy/keepproxy.go
+++ b/services/keepproxy/keepproxy.go
@@ -7,6 +7,7 @@ import (
"fmt"
"github.com/gorilla/mux"
"io"
+ "io/ioutil"
"log"
"net"
"net/http"
@@ -246,6 +247,7 @@ func MakeRESTRouter(
if enable_put {
rest.Handle(`/{hash:[0-9a-f]{32}}+{hints}`, PutBlockHandler{kc, t}).Methods("PUT")
rest.Handle(`/{hash:[0-9a-f]{32}}`, PutBlockHandler{kc, t}).Methods("PUT")
+ rest.Handle(`/`, PutBlockHandler{kc, t}).Methods("POST")
rest.Handle(`/{hash:[0-9a-f]{32}}{ignore}`, OptionsHandler{}).Methods("OPTIONS")
}
@@ -261,7 +263,7 @@ func (this InvalidPathHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
func (this OptionsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
log.Printf("%s: %s %s", GetRemoteAddress(req), req.Method, req.URL.Path)
- resp.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, PUT, OPTIONS")
+ resp.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, OPTIONS")
resp.Header().Set("Access-Control-Allow-Origin", "*")
resp.Header().Set("Access-Control-Allow-Headers", "Authorization, X-Keep-Desired-Replicas")
resp.Header().Set("Access-Control-Max-Age", "86486400")
@@ -384,7 +386,20 @@ func (this PutBlockHandler) ServeHTTP(resp http.ResponseWriter, req *http.Reques
}
// Now try to put the block through
- hash, replicas, err := kc.PutHR(hash, req.Body, contentLength)
+ var replicas int
+ var err error
+ if hash == "" {
+ if bytes, err := ioutil.ReadAll(req.Body); err != nil {
+ msg := fmt.Sprintf("Error reading request body: %s", err)
+ log.Printf(msg)
+ http.Error(resp, msg, http.StatusInternalServerError)
+ return
+ } else {
+ hash, replicas, err = kc.PutB(bytes)
+ }
+ } else {
+ hash, replicas, err = kc.PutHR(hash, req.Body, contentLength)
+ }
// Tell the client how many successful PUTs we accomplished
resp.Header().Set(keepclient.X_Keep_Replicas_Stored, fmt.Sprintf("%d", replicas))
diff --git a/services/keepproxy/keepproxy_test.go b/services/keepproxy/keepproxy_test.go
index ba9793d..667a0b2 100644
--- a/services/keepproxy/keepproxy_test.go
+++ b/services/keepproxy/keepproxy_test.go
@@ -14,6 +14,7 @@ import (
"net/url"
"os"
"os/exec"
+ "strings"
"testing"
"time"
)
@@ -339,7 +340,7 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
c.Check(resp.StatusCode, Equals, 200)
body, err := ioutil.ReadAll(resp.Body)
c.Check(string(body), Equals, "")
- c.Check(resp.Header.Get("Access-Control-Allow-Methods"), Equals, "GET, HEAD, PUT, OPTIONS")
+ c.Check(resp.Header.Get("Access-Control-Allow-Methods"), Equals, "GET, HEAD, POST, PUT, OPTIONS")
c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*")
}
@@ -352,3 +353,24 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*")
}
}
+
+func (s *ServerRequiredSuite) TestPostWithoutHash(c *C) {
+ runProxy(c, []string{"keepproxy"}, "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h", 29955)
+ waitForListener()
+ defer closeListener()
+
+ {
+ client := http.Client{}
+ req, err := http.NewRequest("POST",
+ "http://localhost:29955/",
+ strings.NewReader("qux"))
+ req.Header.Add("Authorization", "OAuth2 4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h")
+ req.Header.Add("Content-Type", "application/octet-stream")
+ resp, err := client.Do(req)
+ c.Check(err, Equals, nil)
+ body, err := ioutil.ReadAll(resp.Body)
+ c.Check(err, Equals, nil)
+ c.Check(string(body), Equals,
+ fmt.Sprintf("%x+%d", md5.Sum([]byte("qux")), 3))
+ }
+}
commit 3ae43002e9762e60844723edc5096c7282fb978d
Author: Tom Clegg <tom at curoverse.com>
Date: Sat Nov 22 05:01:13 2014 -0500
3781: Set CORS headers in keepproxy responses.
diff --git a/services/keepproxy/keepproxy.go b/services/keepproxy/keepproxy.go
index de4ccaf..b74a982 100644
--- a/services/keepproxy/keepproxy.go
+++ b/services/keepproxy/keepproxy.go
@@ -222,6 +222,8 @@ type PutBlockHandler struct {
type InvalidPathHandler struct{}
+type OptionsHandler struct{}
+
// MakeRESTRouter
// Returns a mux.Router that passes GET and PUT requests to the
// appropriate handlers.
@@ -244,6 +246,7 @@ func MakeRESTRouter(
if enable_put {
rest.Handle(`/{hash:[0-9a-f]{32}}+{hints}`, PutBlockHandler{kc, t}).Methods("PUT")
rest.Handle(`/{hash:[0-9a-f]{32}}`, PutBlockHandler{kc, t}).Methods("PUT")
+ rest.Handle(`/{hash:[0-9a-f]{32}}{ignore}`, OptionsHandler{}).Methods("OPTIONS")
}
rest.NotFoundHandler = InvalidPathHandler{}
@@ -256,7 +259,17 @@ func (this InvalidPathHandler) ServeHTTP(resp http.ResponseWriter, req *http.Req
http.Error(resp, "Bad request", http.StatusBadRequest)
}
+func (this OptionsHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ log.Printf("%s: %s %s", GetRemoteAddress(req), req.Method, req.URL.Path)
+ resp.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, PUT, OPTIONS")
+ resp.Header().Set("Access-Control-Allow-Origin", "*")
+ resp.Header().Set("Access-Control-Allow-Headers", "Authorization, X-Keep-Desired-Replicas")
+ resp.Header().Set("Access-Control-Max-Age", "86486400")
+}
+
func (this GetBlockHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
+ resp.Header().Set("Access-Control-Allow-Origin", "*")
+ resp.Header().Set("Access-Control-Allow-Headers", "Authorization")
kc := *this.KeepClient
diff --git a/services/keepproxy/keepproxy_test.go b/services/keepproxy/keepproxy_test.go
index 88ac8a6..ba9793d 100644
--- a/services/keepproxy/keepproxy_test.go
+++ b/services/keepproxy/keepproxy_test.go
@@ -222,7 +222,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
}
func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
- log.Print("TestPutAndGet start")
+ log.Print("TestPutAskGetForbidden start")
kc := runProxy(c, []string{"keepproxy"}, "123abc", 29951)
waitForListener()
@@ -260,7 +260,7 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
log.Print("Get")
}
- log.Print("TestPutAndGetForbidden done")
+ log.Print("TestPutAskGetForbidden done")
}
func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
@@ -320,3 +320,35 @@ func (s *ServerRequiredSuite) TestPutDisabled(c *C) {
log.Print("TestPutDisabled done")
}
+
+func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
+ runProxy(c, []string{"keepproxy"}, "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h", 29954)
+ waitForListener()
+ defer closeListener()
+
+ {
+ client := http.Client{}
+ req, err := http.NewRequest("OPTIONS",
+ fmt.Sprintf("http://localhost:29954/%x+3",
+ md5.Sum([]byte("foo"))),
+ nil)
+ req.Header.Add("Access-Control-Request-Method", "PUT")
+ req.Header.Add("Access-Control-Request-Headers", "Authorization, X-Keep-Desired-Replicas")
+ resp, err := client.Do(req)
+ c.Check(err, Equals, nil)
+ c.Check(resp.StatusCode, Equals, 200)
+ body, err := ioutil.ReadAll(resp.Body)
+ c.Check(string(body), Equals, "")
+ c.Check(resp.Header.Get("Access-Control-Allow-Methods"), Equals, "GET, HEAD, PUT, OPTIONS")
+ c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*")
+ }
+
+ {
+ resp, err := http.Get(
+ fmt.Sprintf("http://localhost:29954/%x+3",
+ md5.Sum([]byte("foo"))))
+ c.Check(err, Equals, nil)
+ c.Check(resp.Header.Get("Access-Control-Allow-Headers"), Equals, "Authorization")
+ c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*")
+ }
+}
commit afef51be31f166fda47481e558881de09e97a983
Author: Tom Clegg <tom at curoverse.com>
Date: Wed Nov 26 00:40:49 2014 -0500
3781: Add tests for CORS headers. Turn off CORS headers at OPTIONS /login etc.
diff --git a/services/api/app/controllers/user_sessions_controller.rb b/services/api/app/controllers/user_sessions_controller.rb
index cdcb720..f6efda1 100644
--- a/services/api/app/controllers/user_sessions_controller.rb
+++ b/services/api/app/controllers/user_sessions_controller.rb
@@ -140,4 +140,8 @@ class UserSessionsController < ApplicationController
callback_url += 'api_token=' + api_client_auth.api_token
redirect_to callback_url
end
+
+ def cross_origin_forbidden
+ send_error 'Forbidden', status: 403
+ end
end
diff --git a/services/api/config/routes.rb b/services/api/config/routes.rb
index 096a0a5..c2cffd4 100644
--- a/services/api/config/routes.rb
+++ b/services/api/config/routes.rb
@@ -3,8 +3,13 @@ Server::Application.routes.draw do
# See http://guides.rubyonrails.org/routing.html
- # OPTIONS requests just get an empty response with CORS headers.
- match '*a', :to => 'static#empty', :via => 'OPTIONS'
+ # OPTIONS requests are not allowed at routes that use cookies.
+ ['/auth/*a', '/login', '/logout'].each do |nono|
+ match nono, :to => 'user_sessions#cross_origin_forbidden', :via => 'OPTIONS'
+ end
+ # OPTIONS at discovery and API paths get an empty response with CORS headers.
+ match '/discovery/v1/*a', :to => 'static#empty', :via => 'OPTIONS'
+ match '/arvados/v1/*a', :to => 'static#empty', :via => 'OPTIONS'
namespace :arvados do
namespace :v1 do
diff --git a/services/api/test/integration/cross_origin_test.rb b/services/api/test/integration/cross_origin_test.rb
new file mode 100644
index 0000000..ebe7ce7
--- /dev/null
+++ b/services/api/test/integration/cross_origin_test.rb
@@ -0,0 +1,76 @@
+require 'test_helper'
+
+class CrossOriginTest < ActionDispatch::IntegrationTest
+ def options *args
+ # Rails doesn't support OPTIONS the same way as GET, POST, etc.
+ reset! unless integration_session
+ integration_session.__send__(:process, :options, *args).tap do
+ copy_session_variables!
+ end
+ end
+
+ %w(/login /logout /auth/example/callback /auth/joshid).each do |path|
+ test "OPTIONS requests are refused at #{path}" do
+ options path, {}, {}
+ assert_no_cors_headers
+ end
+
+ test "CORS headers do not exist at GET #{path}" do
+ get path, {}, {}
+ assert_no_cors_headers
+ end
+ end
+
+ %w(/discovery/v1/apis/arvados/v1/rest).each do |path|
+ test "CORS headers are set at GET #{path}" do
+ get path, {}, {}
+ assert_response :success
+ assert_cors_headers
+ end
+ end
+
+ ['/arvados/v1/collections',
+ '/arvados/v1/users',
+ '/arvados/v1/api_client_authorizations'].each do |path|
+ test "CORS headers are set and body is stub at OPTIONS #{path}" do
+ options path, {}, {}
+ assert_response :success
+ assert_cors_headers
+ assert_equal '-', response.body
+ end
+
+ test "CORS headers are set at authenticated GET #{path}" do
+ get path, {}, auth(:active_trustedclient)
+ assert_response :success
+ assert_cors_headers
+ end
+
+ # CORS headers are OK only if cookies are *not* used to determine
+ # whether a transaction is allowed. The following is a (far from
+ # perfect) test that the usual Rails cookie->session mechanism
+ # does not grant access to any resources.
+ ['GET', 'POST'].each do |method|
+ test "Session does not work at #{method} #{path}" do
+ send method.downcase, path, {format: 'json'}, {user_id: 1}
+ assert_response 401
+ assert_cors_headers
+ end
+ end
+ end
+
+ protected
+ def assert_cors_headers
+ assert_equal '*', response.headers['Access-Control-Allow-Origin']
+ allowed = response.headers['Access-Control-Allow-Methods'].split(', ')
+ %w(GET HEAD POST PUT DELETE).each do |m|
+ assert_includes allowed, m, "A-C-A-Methods should include #{m}"
+ end
+ assert_equal 'Authorization', response.headers['Access-Control-Allow-Headers']
+ end
+
+ def assert_no_cors_headers
+ response.headers.keys.each do |h|
+ assert_no_match /^Access-Control-/i, h
+ end
+ end
+end
commit a25c9cb6721e61afe433a238b2e2c580adf97f31
Author: Tom Clegg <tom at curoverse.com>
Date: Sat Nov 22 04:49:53 2014 -0500
3781: Set CORS headers in API responses.
diff --git a/services/api/app/controllers/application_controller.rb b/services/api/app/controllers/application_controller.rb
index 4f0364f..eacd5f2 100644
--- a/services/api/app/controllers/application_controller.rb
+++ b/services/api/app/controllers/application_controller.rb
@@ -27,6 +27,7 @@ class ApplicationController < ActionController::Base
ERROR_ACTIONS = [:render_error, :render_not_found]
+ before_filter :set_cors_headers
before_filter :respond_with_json_by_default
before_filter :remote_ip
before_filter :load_read_auths
@@ -345,6 +346,13 @@ class ApplicationController < ActionController::Base
end
end
+ def set_cors_headers
+ response.headers['Access-Control-Allow-Origin'] = '*'
+ response.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, PUT, POST, DELETE'
+ response.headers['Access-Control-Allow-Headers'] = 'Authorization'
+ response.headers['Access-Control-Max-Age'] = '86486400'
+ end
+
def respond_with_json_by_default
html_index = request.accepts.index(Mime::HTML)
if html_index.nil? or request.accepts[0...html_index].include?(Mime::JSON)
diff --git a/services/api/app/controllers/static_controller.rb b/services/api/app/controllers/static_controller.rb
index d624ea8..9c66f01 100644
--- a/services/api/app/controllers/static_controller.rb
+++ b/services/api/app/controllers/static_controller.rb
@@ -3,7 +3,7 @@ class StaticController < ApplicationController
skip_before_filter :find_object_by_uuid
skip_before_filter :render_404_if_no_object
- skip_before_filter :require_auth_scope, :only => [ :home, :login_failure ]
+ skip_before_filter :require_auth_scope, only: [:home, :empty, :login_failure]
def home
respond_to do |f|
@@ -20,4 +20,8 @@ class StaticController < ApplicationController
end
end
+ def empty
+ render text: "-"
+ end
+
end
diff --git a/services/api/app/controllers/user_sessions_controller.rb b/services/api/app/controllers/user_sessions_controller.rb
index 3e79915..cdcb720 100644
--- a/services/api/app/controllers/user_sessions_controller.rb
+++ b/services/api/app/controllers/user_sessions_controller.rb
@@ -1,6 +1,7 @@
class UserSessionsController < ApplicationController
before_filter :require_auth_scope, :only => [ :destroy ]
+ skip_before_filter :set_cors_headers
skip_before_filter :find_object_by_uuid
skip_before_filter :render_404_if_no_object
diff --git a/services/api/config/routes.rb b/services/api/config/routes.rb
index 705822a..096a0a5 100644
--- a/services/api/config/routes.rb
+++ b/services/api/config/routes.rb
@@ -3,6 +3,9 @@ Server::Application.routes.draw do
# See http://guides.rubyonrails.org/routing.html
+ # OPTIONS requests just get an empty response with CORS headers.
+ match '*a', :to => 'static#empty', :via => 'OPTIONS'
+
namespace :arvados do
namespace :v1 do
resources :api_client_authorizations do
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list