diff --git a/cmd/web-peer-rpc.go b/cmd/browser-peer-rpc.go similarity index 83% rename from cmd/web-peer-rpc.go rename to cmd/browser-peer-rpc.go index 5908ab46d..240eb8d29 100644 --- a/cmd/web-peer-rpc.go +++ b/cmd/browser-peer-rpc.go @@ -1,5 +1,5 @@ /* - * Minio Cloud Storage, (C) 2014-2016 Minio, Inc. + * Minio Cloud Storage, (C) 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,9 @@ import ( "time" ) -func (br *browserAPIHandlers) LoginHandler(args *RPCLoginArgs, reply *RPCLoginReply) error { +// Login handler implements JWT login token generator, which upon login request +// along with username and password is generated. +func (br *browserPeerAPIHandlers) LoginHandler(args *RPCLoginArgs, reply *RPCLoginReply) error { jwt, err := newJWT(defaultInterNodeJWTExpiry) if err != nil { return err @@ -56,7 +58,7 @@ type SetAuthPeerArgs struct { // will be forced to re-establish connections. Connections will be // re-established only when the sending client has also updated its // credentials. -func (br *browserAPIHandlers) SetAuthPeer(args SetAuthPeerArgs, reply *GenericReply) error { +func (br *browserPeerAPIHandlers) SetAuthPeer(args SetAuthPeerArgs, reply *GenericReply) error { // Check auth if !isRPCTokenValid(args.Token) { return errInvalidToken @@ -83,8 +85,7 @@ func updateCredsOnPeers(creds credential) map[string]error { errs := make([]error, len(peers)) var wg sync.WaitGroup - // Launch go routines to send request to each peer in - // parallel. + // Launch go routines to send request to each peer in parallel. for ix := range peers { wg.Add(1) go func(ix int) { @@ -103,7 +104,7 @@ func updateCredsOnPeers(creds credential) map[string]error { secretKey: serverConfig.GetCredential().SecretAccessKey, address: peers[ix], secureConn: isSSL(), - path: path.Join(reservedBucket, browserPath), + path: path.Join(reservedBucket, browserPeerPath), loginMethod: "Browser.LoginHandler", }) @@ -114,12 +115,11 @@ func updateCredsOnPeers(creds credential) map[string]error { // response and not the reply. err := client.Call("Browser.SetAuthPeer", &args, &GenericReply{}) - // we try a bit hard (3 attempts with 1 second - // delay) to set creds on peers in case of - // failure. + // We try a bit hard (3 attempts with 1 second delay) + // to set creds on peers in case of failure. if err != nil { for i := 0; i < 2; i++ { - time.Sleep(1 * time.Second) + time.Sleep(1 * time.Second) // 1 second delay. err = client.Call("Browser.SetAuthPeer", &args, &GenericReply{}) if err == nil { break diff --git a/cmd/browser-peer-rpc_test.go b/cmd/browser-peer-rpc_test.go new file mode 100644 index 000000000..0f6cc74f8 --- /dev/null +++ b/cmd/browser-peer-rpc_test.go @@ -0,0 +1,123 @@ +/* + * Minio Cloud Storage, (C) 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cmd + +import ( + "path" + "testing" +) + +// API suite container common to both FS and XL. +type TestRPCBrowserPeerSuite struct { + serverType string + testServer TestServer + testAuthConf *authConfig +} + +// Setting up the test suite and starting the Test server. +func (s *TestRPCBrowserPeerSuite) SetUpSuite(c *testing.T) { + s.testServer = StartTestBrowserPeerRPCServer(c, s.serverType) + s.testAuthConf = &authConfig{ + address: s.testServer.Server.Listener.Addr().String(), + accessKey: s.testServer.AccessKey, + secretKey: s.testServer.SecretKey, + path: path.Join(reservedBucket, browserPeerPath), + loginMethod: "BrowserPeer.LoginHandler", + } +} + +// No longer used with gocheck, but used in explicit teardown code in +// each test function. // Called implicitly by "gopkg.in/check.v1" +// after all tests are run. +func (s *TestRPCBrowserPeerSuite) TearDownSuite(c *testing.T) { + s.testServer.Stop() +} + +func TestBrowserPeerRPC(t *testing.T) { + // setup code + s := &TestRPCBrowserPeerSuite{serverType: "XL"} + s.SetUpSuite(t) + + // run test + s.testBrowserPeerRPC(t) + + // teardown code + s.TearDownSuite(t) +} + +// Tests for browser peer rpc. +func (s *TestRPCBrowserPeerSuite) testBrowserPeerRPC(t *testing.T) { + // Construct RPC call arguments. + creds := credential{ + AccessKeyID: "abcd1", + SecretAccessKey: "abcd1234", + } + + // Validate for invalid token. + args := SetAuthPeerArgs{Creds: creds} + args.Token = "garbage" + rclient := newClient(s.testAuthConf.address, s.testAuthConf.path, false) + defer rclient.Close() + err := rclient.Call("BrowserPeer.SetAuthPeer", &args, &GenericReply{}) + if err != nil { + if err.Error() != errInvalidToken.Error() { + t.Fatal(err) + } + } + + // Validate for successful Peer update. + args = SetAuthPeerArgs{Creds: creds} + client := newAuthClient(s.testAuthConf) + defer client.Close() + err = client.Call("BrowserPeer.SetAuthPeer", &args, &GenericReply{}) + if err != nil { + t.Fatal(err) + } + + // Validate for failure in login handler with previous credentials. + rclient = newClient(s.testAuthConf.address, s.testAuthConf.path, false) + defer rclient.Close() + rargs := &RPCLoginArgs{ + Username: s.testAuthConf.accessKey, + Password: s.testAuthConf.secretKey, + } + rreply := &RPCLoginReply{} + err = rclient.Call("BrowserPeer.LoginHandler", rargs, rreply) + if err != nil { + if err.Error() != errInvalidAccessKeyID.Error() { + t.Fatal(err) + } + } + + // Validate for success in loing handled with valid credetnails. + rargs = &RPCLoginArgs{ + Username: creds.AccessKeyID, + Password: creds.SecretAccessKey, + } + rreply = &RPCLoginReply{} + err = rclient.Call("BrowserPeer.LoginHandler", rargs, rreply) + if err != nil { + t.Fatal(err) + } + // Validate all the replied fields after successful login. + if rreply.Token == "" { + t.Fatalf("Generated token cannot be empty %s", errInvalidToken) + } + if rreply.Timestamp.IsZero() { + t.Fatal("Time stamp returned cannot be zero") + } +} diff --git a/cmd/web-rpc-router.go b/cmd/browser-rpc-router.go similarity index 71% rename from cmd/web-rpc-router.go rename to cmd/browser-rpc-router.go index 0e901e041..74007f92e 100644 --- a/cmd/web-rpc-router.go +++ b/cmd/browser-rpc-router.go @@ -27,24 +27,23 @@ import ( // throughout Minio cluster, initiated from a Minio browser session. const ( - browserPath = "/browser/setauth" + browserPeerPath = "/browser/setauth" ) // The Type exporting methods exposed for RPC calls. -type browserAPIHandlers struct { -} +type browserPeerAPIHandlers struct{} // Register RPC router -func registerBrowserRPCRouter(mux *router.Router) error { - browserHandlers := &browserAPIHandlers{} +func registerBrowserPeerRPCRouter(mux *router.Router) error { + bpHandlers := &browserPeerAPIHandlers{} - browserRPCServer := rpc.NewServer() - err := browserRPCServer.RegisterName("Browser", browserHandlers) + bpRPCServer := rpc.NewServer() + err := bpRPCServer.RegisterName("BrowserPeer", bpHandlers) if err != nil { return traceError(err) } - browserRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter() - browserRouter.Path(browserPath).Handler(browserRPCServer) + bpRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter() + bpRouter.Path(browserPeerPath).Handler(bpRPCServer) return nil } diff --git a/cmd/routers.go b/cmd/routers.go index 81a45ede7..5d4585eeb 100644 --- a/cmd/routers.go +++ b/cmd/routers.go @@ -113,7 +113,7 @@ func configureServerHandler(srvCmdConfig serverCmdConfig) (http.Handler, error) // By default minio web browser is enabled. if !strings.EqualFold(os.Getenv("MINIO_BROWSER"), "off") { // Register RPC router for web related calls. - if err = registerBrowserRPCRouter(mux); err != nil { + if err = registerBrowserPeerRPCRouter(mux); err != nil { return nil, err } diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index e4344064e..bb00a945e 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -2056,3 +2056,33 @@ func initTestWebRPCEndPoint(objLayer ObjectLayer) http.Handler { registerWebRouter(muxRouter) return muxRouter } + +// Initialize browser RPC endpoint. +func initTestBrowserPeerRPCEndPoint() http.Handler { + // Initialize router. + muxRouter := router.NewRouter() + registerBrowserPeerRPCRouter(muxRouter) + return muxRouter +} + +func StartTestBrowserPeerRPCServer(t TestErrHandler, instanceType string) TestServer { + root, err := newTestConfig("us-east-1") + if err != nil { + t.Fatalf("%s", err) + } + + // Create an instance of TestServer. + testRPCServer := TestServer{} + + // Fetch credentials for the test server. + credentials := serverConfig.GetCredential() + + testRPCServer.Root = root + testRPCServer.AccessKey = credentials.AccessKeyID + testRPCServer.SecretKey = credentials.SecretAccessKey + + // Initialize and run the TestServer. + testRPCServer.Server = httptest.NewServer(initTestBrowserPeerRPCEndPoint()) + return testRPCServer + +} diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 7e6eaded0..7a83da50a 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -383,8 +383,7 @@ func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *Se reply.PeerErrMsgs[svr] = errVal.Error() } - // If we were unable to update locally, we return an error to - // the user/browser. + // If we were unable to update locally, we return an error to the user/browser. if errsMap[globalMinioAddr] != nil { // Since the error message may be very long to display // on the browser, we tell the user to check the diff --git a/cmd/web-handlers_test.go b/cmd/web-handlers_test.go index bce8eb36a..11074313b 100644 --- a/cmd/web-handlers_test.go +++ b/cmd/web-handlers_test.go @@ -590,7 +590,6 @@ func testSetAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandle // Iterating over the test cases, calling the function under test and asserting the response. for i, testCase := range testCases { - setAuthRequest := SetAuthArgs{AccessKey: testCase.username, SecretKey: testCase.password} setAuthReply := &SetAuthReply{} req, err := newTestWebRPCRequest("Web.SetAuth", authorization, setAuthRequest)