Skip to content

Commit

Permalink
Merge pull request #2069 from dubek/fix-rest-json-part-2
Browse files Browse the repository at this point in the history
core/rest: Proper JSON encoding in responses to /registrar/* endpoints
  • Loading branch information
srderson committed Jul 6, 2016
2 parents 94b567b + bdc611d commit 15d0620
Show file tree
Hide file tree
Showing 4 changed files with 728 additions and 82 deletions.
13 changes: 5 additions & 8 deletions core/rest/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ package rest
import (
"bytes"
"fmt"
"google/protobuf"
"os"
"testing"

"google/protobuf"

"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/util"
"github.com/hyperledger/fabric/protos"
Expand All @@ -37,12 +36,10 @@ func TestMain(m *testing.M) {
}

func setupTestConfig() {
viper.SetConfigName("core") // name of config file (without extension)
viper.AddConfigPath("./") // path to look for the config file in
viper.AddConfigPath("./..") // path to look for the config file in
viper.AddConfigPath("./../../peer") // path to look for the config file in
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
viper.SetConfigName("rest_test") // name of config file (without extension)
viper.AddConfigPath(".") // path to look for the config file in
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
}
Expand Down
124 changes: 60 additions & 64 deletions core/rest/rest_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type restResult struct {
Error string `json:",omitempty"`
}

// tcertsResult defines the response payload for the GetTransactionCert REST
// interface request.
type tcertsResult struct {
OK []string
}

// rpcRequest defines the JSON RPC 2.0 request payload for the /chaincode endpoint.
type rpcRequest struct {
Jsonrpc *string `json:"jsonrpc,omitempty"`
Expand Down Expand Up @@ -218,27 +224,23 @@ func validateEnrollmentIDParameter(rw web.ResponseWriter, enrollmentID string) b
// CA and stores the enrollment certificate and key in the Devops server.
func (s *ServerOpenchainREST) Register(rw web.ResponseWriter, req *web.Request) {
restLogger.Info("REST client login...")
encoder := json.NewEncoder(rw)

// Decode the incoming JSON payload
var loginSpec pb.Secret
err := jsonpb.Unmarshal(req.Body, &loginSpec)

// Check for proper JSON syntax
if err != nil {
// Unmarshall returns a " character around unrecognized fields in the case
// of a schema validation failure. These must be replaced with a ' character.
// Otherwise, the returned JSON is invalid.
errVal := strings.Replace(err.Error(), "\"", "'", -1)

// Client must supply payload
if err == io.EOF {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"Payload must contain object Secret with enrollId and enrollSecret fields.\"}")
restLogger.Error("{\"Error\": \"Payload must contain object Secret with enrollId and enrollSecret fields.\"}")
encoder.Encode(restResult{Error: "Payload must contain object Secret with enrollId and enrollSecret fields."})
restLogger.Error("Error: Payload must contain object Secret with enrollId and enrollSecret fields.")
} else {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", errVal)
restLogger.Errorf("{\"Error\": \"%s\"}", errVal)
encoder.Encode(restResult{Error: err.Error()})
restLogger.Errorf("Error: %s", err)
}

return
Expand All @@ -247,8 +249,8 @@ func (s *ServerOpenchainREST) Register(rw web.ResponseWriter, req *web.Request)
// Check that the enrollId and enrollSecret are not left blank.
if (loginSpec.EnrollId == "") || (loginSpec.EnrollSecret == "") {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"enrollId and enrollSecret may not be blank.\"}")
restLogger.Error("{\"Error\": \"enrollId and enrollSecret may not be blank.\"}")
encoder.Encode(restResult{Error: "enrollId and enrollSecret may not be blank."})
restLogger.Error("Error: enrollId and enrollSecret may not be blank.")

return
}
Expand All @@ -265,7 +267,7 @@ func (s *ServerOpenchainREST) Register(rw web.ResponseWriter, req *web.Request)
// If the user is already logged in, return
if _, err := os.Stat(localStore + "loginToken_" + loginSpec.EnrollId); err == nil {
rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": \"User %s is already logged in.\"}", loginSpec.EnrollId)
encoder.Encode(restResult{OK: fmt.Sprintf("User %s is already logged in.", loginSpec.EnrollId)})
restLogger.Infof("User '%s' is already logged in.\n", loginSpec.EnrollId)

return
Expand All @@ -284,13 +286,13 @@ func (s *ServerOpenchainREST) Register(rw web.ResponseWriter, req *web.Request)
// Directory does not exist, create it
if err := os.Mkdir(localStore, 0755); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Fatal error -- %s\"}", err)
encoder.Encode(restResult{Error: fmt.Sprintf("Fatal error -- %s", err)})
panic(fmt.Errorf("Fatal error when creating %s directory: %s\n", localStore, err))
}
} else {
// Unexpected error
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Fatal error -- %s\"}", err)
encoder.Encode(restResult{Error: fmt.Sprintf("Fatal error -- %s", err)})
panic(fmt.Errorf("Fatal error on os.Stat of %s directory: %s\n", localStore, err))
}
}
Expand All @@ -300,19 +302,17 @@ func (s *ServerOpenchainREST) Register(rw web.ResponseWriter, req *web.Request)
err = ioutil.WriteFile(localStore+"loginToken_"+loginSpec.EnrollId, []byte(loginSpec.EnrollId), 0755)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Fatal error -- %s\"}", err)
encoder.Encode(restResult{Error: fmt.Sprintf("Fatal error -- %s", err)})
panic(fmt.Errorf("Fatal error when storing client login token: %s\n", err))
}

rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": \"Login successful for user '%s'.\"}", loginSpec.EnrollId)
encoder.Encode(restResult{OK: fmt.Sprintf("Login successful for user '%s'.", loginSpec.EnrollId)})
restLogger.Infof("Login successful for user '%s'.\n", loginSpec.EnrollId)
} else {
loginErr := strings.Replace(string(loginResult.Msg), "\"", "'", -1)

rw.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", loginErr)
restLogger.Errorf("Error on client login: %s", loginErr)
encoder.Encode(restResult{Error: string(loginResult.Msg)})
restLogger.Errorf("Error on client login: %s", string(loginResult.Msg))
}

return
Expand Down Expand Up @@ -373,10 +373,12 @@ func (s *ServerOpenchainREST) DeleteEnrollmentID(rw web.ResponseWriter, req *web
_, err1 := os.Stat(loginTok)
_, err2 := os.Stat(cryptoDir)

encoder := json.NewEncoder(rw)

// If the user is not logged in, nothing to delete. Return OK.
if os.IsNotExist(err1) && os.IsNotExist(err2) {
rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": \"User %s is not logged in.\"}", enrollmentID)
encoder.Encode(restResult{OK: fmt.Sprintf("User %s is not logged in.", enrollmentID)})
restLogger.Infof("User '%s' is not logged in.\n", enrollmentID)

return
Expand All @@ -385,23 +387,23 @@ func (s *ServerOpenchainREST) DeleteEnrollmentID(rw web.ResponseWriter, req *web
// The user is logged in, delete the user's login token
if err := os.RemoveAll(loginTok); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Error trying to delete login token for user %s: %s\"}", enrollmentID, err)
restLogger.Errorf("{\"Error\": \"Error trying to delete login token for user %s: %s\"}", enrollmentID, err)
encoder.Encode(restResult{Error: fmt.Sprintf("Error trying to delete login token for user %s: %s", enrollmentID, err)})
restLogger.Errorf("Error: Error trying to delete login token for user %s: %s", enrollmentID, err)

return
}

// The user is logged in, delete the user's cert and key directory
if err := os.RemoveAll(cryptoDir); err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Error trying to delete login directory for user %s: %s\"}", enrollmentID, err)
restLogger.Errorf("{\"Error\": \"Error trying to delete login directory for user %s: %s\"}", enrollmentID, err)
encoder.Encode(restResult{Error: fmt.Sprintf("Error trying to delete login directory for user %s: %s", enrollmentID, err)})
restLogger.Errorf("Error: Error trying to delete login directory for user %s: %s", enrollmentID, err)

return
}

rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": \"Deleted login token and directory for user %s.\"}", enrollmentID)
encoder.Encode(restResult{OK: fmt.Sprintf("Deleted login token and directory for user %s.", enrollmentID)})
restLogger.Infof("Deleted login token and directory for user %s.\n", enrollmentID)

return
Expand All @@ -418,6 +420,8 @@ func (s *ServerOpenchainREST) GetEnrollmentCert(rw web.ResponseWriter, req *web.

restLogger.Debugf("REST received enrollment certificate retrieval request for registrationID '%s'", enrollmentID)

encoder := json.NewEncoder(rw)

// If security is enabled, initialize the crypto client
if core.SecurityEnabled() {
if restLogger.IsEnabledFor(logging.DEBUG) {
Expand All @@ -428,8 +432,8 @@ func (s *ServerOpenchainREST) GetEnrollmentCert(rw web.ResponseWriter, req *web.
sec, err := crypto.InitClient(enrollmentID, nil)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", err)
restLogger.Errorf("{\"Error\": \"%s\"}", err)
encoder.Encode(restResult{Error: err.Error()})
restLogger.Errorf("Error: %s", err)

return
}
Expand All @@ -438,17 +442,17 @@ func (s *ServerOpenchainREST) GetEnrollmentCert(rw web.ResponseWriter, req *web.
handler, err := sec.GetEnrollmentCertificateHandler()
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", err)
restLogger.Errorf("{\"Error\": \"%s\"}", err)
encoder.Encode(restResult{Error: err.Error()})
restLogger.Errorf("Error: %s", err)

return
}

// Certificate handler can not be hil
if handler == nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Error retrieving certificate handler.\"}")
restLogger.Error("{\"Error\": \"Error retrieving certificate handler.\"}")
encoder.Encode(restResult{Error: "Error retrieving certificate handler."})
restLogger.Errorf("Error: Error retrieving certificate handler.")

return
}
Expand All @@ -459,17 +463,17 @@ func (s *ServerOpenchainREST) GetEnrollmentCert(rw web.ResponseWriter, req *web.
// Confirm the retrieved enrollment certificate is not nil
if certDER == nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Enrollment certificate is nil.\"}")
restLogger.Error("{\"Error\": \"Enrollment certificate is nil.\"}")
encoder.Encode(restResult{Error: "Enrollment certificate is nil."})
restLogger.Errorf("Error: Enrollment certificate is nil.")

return
}

// Confirm the retrieved enrollment certificate has non-zero length
if len(certDER) == 0 {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Enrollment certificate length is 0.\"}")
restLogger.Error("{\"Error\": \"Enrollment certificate length is 0.\"}")
encoder.Encode(restResult{Error: "Enrollment certificate length is 0."})
restLogger.Errorf("Error: Enrollment certificate length is 0.")

return
}
Expand All @@ -484,13 +488,13 @@ func (s *ServerOpenchainREST) GetEnrollmentCert(rw web.ResponseWriter, req *web.
crypto.CloseClient(sec)

rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": \"%s\"}", urlEncodedCert)
encoder.Encode(restResult{OK: urlEncodedCert})
restLogger.Debugf("Successfully retrieved enrollment certificate for secure context '%s'", enrollmentID)
} else {
// Security must be enabled to request enrollment certificates
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"Security functionality must be enabled before requesting client certificates.\"}")
restLogger.Error("{\"Error\": \"Security functionality must be enabled before requesting client certificates.\"}")
encoder.Encode(restResult{Error: "Security functionality must be enabled before requesting client certificates."})
restLogger.Errorf("Error: Security functionality must be enabled before requesting client certificates.")

return
}
Expand All @@ -507,6 +511,8 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web

restLogger.Debugf("REST received transaction certificate retrieval request for registrationID '%s'", enrollmentID)

encoder := json.NewEncoder(rw)

// Parse out the count query parameter
req.ParseForm()
queryParams := req.Form
Expand All @@ -523,8 +529,8 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web
// Check for count parameter being a non-negative integer
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"Count query parameter must be a non-negative integer.\"}")
restLogger.Error("{\"Error\": \"Count query parameter must be a non-negative integer.\"}")
encoder.Encode(restResult{Error: "Count query parameter must be a non-negative integer."})
restLogger.Errorf("Error: Count query parameter must be a non-negative integer.")

return
}
Expand All @@ -550,8 +556,8 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web
sec, err := crypto.InitClient(enrollmentID, nil)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", err)
restLogger.Errorf("{\"Error\": \"%s\"}", err)
encoder.Encode(restResult{Error: err.Error()})
restLogger.Errorf("Error: %s", err)

return
}
Expand All @@ -562,17 +568,17 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web
handler, err := sec.GetTCertificateHandlerNext(attributes...)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", err)
restLogger.Errorf("{\"Error\": \"%s\"}", err)
encoder.Encode(restResult{Error: err.Error()})
restLogger.Errorf("Error: %s", err)

return
}

// Certificate handler can not be hil
if handler == nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Error retrieving certificate handler.\"}")
restLogger.Error("{\"Error\": \"Error retrieving certificate handler.\"}")
encoder.Encode(restResult{Error: "Error retrieving certificate handler."})
restLogger.Errorf("Error: Error retrieving certificate handler.")

return
}
Expand All @@ -587,17 +593,17 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web
// Confirm the retrieved enrollment certificate is not nil
if certDER == nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Transaction certificate is nil.\"}")
restLogger.Error("{\"Error\": \"Transaction certificate is nil.\"}")
encoder.Encode(restResult{Error: "Transaction certificate is nil."})
restLogger.Errorf("Error: Transaction certificate is nil.")

return
}

// Confirm the retrieved enrollment certificate has non-zero length
if len(certDER) == 0 {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"Transaction certificate length is 0.\"}")
restLogger.Error("{\"Error\": \"Transaction certificate length is 0.\"}")
encoder.Encode(restResult{Error: "Transaction certificate length is 0."})
restLogger.Errorf("Error: Transaction certificate length is 0.")

return
}
Expand All @@ -615,24 +621,14 @@ func (s *ServerOpenchainREST) GetTransactionCert(rw web.ResponseWriter, req *web
// Close the security client
crypto.CloseClient(sec)

// Construct a JSON formatted response
jsonResponse, err := json.Marshal(tcertArray)
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(rw, "{\"Error\": \"%s\"}", err)
restLogger.Errorf("{\"Error marshalling TCert array\": \"%s\"}", err)

return
}

rw.WriteHeader(http.StatusOK)
fmt.Fprintf(rw, "{\"OK\": %s}", string(jsonResponse))
encoder.Encode(tcertsResult{OK: tcertArray})
restLogger.Debugf("Successfully retrieved transaction certificates for secure context '%s'", enrollmentID)
} else {
// Security must be enabled to request transaction certificates
rw.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(rw, "{\"Error\": \"Security functionality must be enabled before requesting client certificates.\"}")
restLogger.Error("{\"Error\": \"Security functionality must be enabled before requesting client certificates.\"}")
encoder.Encode(restResult{Error: "Security functionality must be enabled before requesting client certificates."})
restLogger.Errorf("Error: Security functionality must be enabled before requesting client certificates.")

return
}
Expand Down

0 comments on commit 15d0620

Please sign in to comment.