| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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 ( | 
					
						
							| 
									
										
										
										
											2016-08-24 07:26:42 +08:00
										 |  |  | 	"net/rpc" | 
					
						
							| 
									
										
										
										
											2016-11-11 16:14:32 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-31 09:08:02 +08:00
										 |  |  | // Attempt to retry only this many number of times before
 | 
					
						
							|  |  |  | // giving up on the remote RPC entirely.
 | 
					
						
							|  |  |  | const globalAuthRPCRetryThreshold = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // authConfig requires to make new AuthRPCClient.
 | 
					
						
							| 
									
										
										
										
											2016-08-25 01:14:14 +08:00
										 |  |  | type authConfig struct { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	accessKey        string // Access key (like username) for authentication.
 | 
					
						
							|  |  |  | 	secretKey        string // Secret key (like Password) for authentication.
 | 
					
						
							|  |  |  | 	serverAddr       string // RPC server address.
 | 
					
						
							|  |  |  | 	serviceEndpoint  string // Endpoint on the server to make any RPC call.
 | 
					
						
							|  |  |  | 	secureConn       bool   // Make TLS connection to RPC server or not.
 | 
					
						
							|  |  |  | 	serviceName      string // Service name of auth server.
 | 
					
						
							|  |  |  | 	disableReconnect bool   // Disable reconnect on failure or not.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/// Retry configurable values.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Each retry unit multiplicative, measured in time.Duration.
 | 
					
						
							|  |  |  | 	// This is the basic unit used for calculating backoffs.
 | 
					
						
							|  |  |  | 	retryUnit time.Duration | 
					
						
							|  |  |  | 	// Maximum retry duration i.e A caller would wait no more than this
 | 
					
						
							|  |  |  | 	// duration to continue their loop.
 | 
					
						
							|  |  |  | 	retryCap time.Duration | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Maximum retries an call authRPC client would do for a failed
 | 
					
						
							|  |  |  | 	// RPC call.
 | 
					
						
							|  |  |  | 	retryAttemptThreshold int | 
					
						
							| 
									
										
										
										
											2016-08-25 01:14:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // AuthRPCClient is a authenticated RPC client which does authentication before doing Call().
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | type AuthRPCClient struct { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	sync.Mutex            // Mutex to lock this object.
 | 
					
						
							|  |  |  | 	rpcClient  *RPCClient // Reconnectable RPC client to make any RPC call.
 | 
					
						
							|  |  |  | 	config     authConfig // Authentication configuration information.
 | 
					
						
							|  |  |  | 	authToken  string     // Authentication token.
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // newAuthRPCClient - returns a JWT based authenticated (go) rpc client, which does automatic reconnect.
 | 
					
						
							|  |  |  | func newAuthRPCClient(config authConfig) *AuthRPCClient { | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 	// Check if retry params are set properly if not default them.
 | 
					
						
							|  |  |  | 	emptyDuration := time.Duration(int64(0)) | 
					
						
							|  |  |  | 	if config.retryUnit == emptyDuration { | 
					
						
							|  |  |  | 		config.retryUnit = defaultRetryUnit | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if config.retryCap == emptyDuration { | 
					
						
							|  |  |  | 		config.retryCap = defaultRetryCap | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if config.retryAttemptThreshold == 0 { | 
					
						
							|  |  |  | 		config.retryAttemptThreshold = globalAuthRPCRetryThreshold | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	return &AuthRPCClient{ | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		rpcClient: newRPCClient(config.serverAddr, config.serviceEndpoint, config.secureConn), | 
					
						
							|  |  |  | 		config:    config, | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Login - a jwt based authentication is performed with rpc server.
 | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | func (authClient *AuthRPCClient) Login() (err error) { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	authClient.Lock() | 
					
						
							|  |  |  | 	defer authClient.Unlock() | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	// Return if already logged in.
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	if authClient.authToken != "" { | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	// Call login.
 | 
					
						
							|  |  |  | 	args := LoginRPCArgs{ | 
					
						
							|  |  |  | 		Username:    authClient.config.accessKey, | 
					
						
							|  |  |  | 		Password:    authClient.config.secretKey, | 
					
						
							|  |  |  | 		Version:     Version, | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 		RequestTime: UTCNow(), | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	reply := LoginRPCReply{} | 
					
						
							|  |  |  | 	serviceMethod := authClient.config.serviceName + loginMethodName | 
					
						
							|  |  |  | 	if err = authClient.rpcClient.Call(serviceMethod, &args, &reply); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-09-24 18:34:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	// Logged in successfully.
 | 
					
						
							|  |  |  | 	authClient.authToken = reply.AuthToken | 
					
						
							| 
									
										
										
										
											2016-11-21 08:57:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-24 10:19:24 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // call makes a RPC call after logs into the server.
 | 
					
						
							|  |  |  | func (authClient *AuthRPCClient) call(serviceMethod string, args interface { | 
					
						
							|  |  |  | 	SetAuthToken(authToken string) | 
					
						
							| 
									
										
										
										
											2016-08-25 01:14:14 +08:00
										 |  |  | }, reply interface{}) (err error) { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	// On successful login, execute RPC call.
 | 
					
						
							|  |  |  | 	if err = authClient.Login(); err == nil { | 
					
						
							| 
									
										
										
										
											2017-02-18 19:15:42 +08:00
										 |  |  | 		authClient.Lock() | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		// Set token and timestamp before the rpc call.
 | 
					
						
							|  |  |  | 		args.SetAuthToken(authClient.authToken) | 
					
						
							| 
									
										
										
										
											2017-02-18 19:15:42 +08:00
										 |  |  | 		authClient.Unlock() | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Do RPC call.
 | 
					
						
							|  |  |  | 		err = authClient.rpcClient.Call(serviceMethod, args, reply) | 
					
						
							| 
									
										
										
										
											2016-12-30 11:42:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Call executes RPC call till success or globalAuthRPCRetryThreshold on ErrShutdown.
 | 
					
						
							|  |  |  | func (authClient *AuthRPCClient) Call(serviceMethod string, args interface { | 
					
						
							|  |  |  | 	SetAuthToken(authToken string) | 
					
						
							|  |  |  | }, reply interface{}) (err error) { | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Done channel is used to close any lingering retry routine, as soon
 | 
					
						
							|  |  |  | 	// as this function returns.
 | 
					
						
							| 
									
										
										
										
											2016-12-30 11:42:02 +08:00
										 |  |  | 	doneCh := make(chan struct{}) | 
					
						
							|  |  |  | 	defer close(doneCh) | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i := range newRetryTimer(authClient.config.retryUnit, authClient.config.retryCap, doneCh) { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		if err = authClient.call(serviceMethod, args, reply); err == rpc.ErrShutdown { | 
					
						
							|  |  |  | 			// As connection at server side is closed, close the rpc client.
 | 
					
						
							| 
									
										
										
										
											2016-12-30 11:42:02 +08:00
										 |  |  | 			authClient.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 			// Retry if reconnect is not disabled.
 | 
					
						
							|  |  |  | 			if !authClient.config.disableReconnect { | 
					
						
							|  |  |  | 				// Retry until threshold reaches.
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 				if i < authClient.config.retryAttemptThreshold { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-12-30 11:42:02 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break | 
					
						
							| 
									
										
										
										
											2016-08-24 07:26:42 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2016-08-23 02:01:21 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-16 15:30:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // Close closes underlying RPC Client.
 | 
					
						
							|  |  |  | func (authClient *AuthRPCClient) Close() error { | 
					
						
							|  |  |  | 	authClient.Lock() | 
					
						
							|  |  |  | 	defer authClient.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	authClient.authToken = "" | 
					
						
							|  |  |  | 	return authClient.rpcClient.Close() | 
					
						
							| 
									
										
										
										
											2016-09-16 15:30:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // ServerAddr returns the serverAddr (network address) of the connection.
 | 
					
						
							|  |  |  | func (authClient *AuthRPCClient) ServerAddr() string { | 
					
						
							|  |  |  | 	return authClient.config.serverAddr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServiceEndpoint returns the RPC service endpoint of the connection.
 | 
					
						
							|  |  |  | func (authClient *AuthRPCClient) ServiceEndpoint() string { | 
					
						
							|  |  |  | 	return authClient.config.serviceEndpoint | 
					
						
							| 
									
										
										
										
											2016-09-16 15:30:55 +08:00
										 |  |  | } |