diff --git a/datastore/mock_database.go b/datastore/mock_database.go index a5154b02..ea60b12b 100644 --- a/datastore/mock_database.go +++ b/datastore/mock_database.go @@ -154,6 +154,9 @@ func (mdb *MockDB) SyncAccount(account Account) error { // FindModel mocks the database response for finding a model func (mdb *MockDB) FindModel(brandID, modelName, apiKey string) (Model, error) { model := Model{ID: 1, BrandID: "system", Name: "alder", KeypairID: 1, AuthorityID: "system", KeyID: "UytTqTvREVhx0tSfYC6KkFHmLWllIIZbQ3NsEG7OARrWuaXSRJyey0vjIQkTEvMO", KeyActive: true, SealedKey: ""} + if modelName == "lost" { + model = Model{ID: 999, BrandID: "system", Name: "lost", KeypairID: 1, AuthorityID: "system", KeyID: "UytTqTvREVhx0tSfYC6KkFHmLWllIIZbQ3NsEG7OARrWuaXSRJyey0vjIQkTEvMO", KeyActive: true, SealedKey: ""} + } if modelName == "ash" { model = Model{ID: 2, BrandID: "system", Name: "ash", KeypairID: 1, AuthorityID: "system", KeyID: "UytTqTvREVhx0tSfYC6KkFHmLWllIIZbQ3NsEG7OARrWuaXSRJyey0vjIQkTEvMO", KeyActive: true, SealedKey: ""} } @@ -725,6 +728,9 @@ func (mdb *MockDB) UpdateModelAssert(m ModelAssertion) error { // GetModelAssert mock for updating model assertion record func (mdb *MockDB) GetModelAssert(modelID int) (ModelAssertion, error) { + if modelID == 999 { + return ModelAssertion{}, errors.New("Cannot find the model assertion record") + } if modelID == 2 { return ModelAssertion{ ID: 1, diff --git a/service/assertion/actions.go b/service/assertion/actions.go index 167156cb..057af2fd 100644 --- a/service/assertion/actions.go +++ b/service/assertion/actions.go @@ -46,8 +46,7 @@ func modelAssertionHandler(w http.ResponseWriter, apiKey string, request ModelAs // Validate the model by checking that it exists on the database model, err := datastore.Environ.DB.FindModel(request.BrandID, request.Name, apiKey) if err != nil { - log.Message("MODEL", response.ErrorInvalidModel.Code, response.ErrorInvalidModel.Message) - return response.ErrorInvalidModel + return response.ErrorInvalidModel("MODEL", request.Name, request.BrandID, apiKey) } assertions := []asserts.Assertion{} diff --git a/service/assertion/actions_user.go b/service/assertion/actions_user.go index 76117c73..a38d2659 100644 --- a/service/assertion/actions_user.go +++ b/service/assertion/actions_user.go @@ -52,8 +52,8 @@ func systemUserAssertionAction(w http.ResponseWriter, authUser datastore.User, a model, err := datastore.Environ.DB.GetAllowedModel(user.ModelID, datastore.User{}) if err != nil { log.Println(err) - svlog.Message("USER", response.ErrorInvalidModelID.Code, response.ErrorInvalidModelID.Message) - response.FormatStandardResponse(false, response.ErrorInvalidModelID.Code, "", response.ErrorInvalidModelID.Message, w) + resp := response.ErrorInvalidModelID("USER", user.ModelID) + response.FormatErrorResponse(resp, w) return } diff --git a/service/assertion/handlers_api_test.go b/service/assertion/handlers_api_test.go index 34ed8ce2..68647fd2 100644 --- a/service/assertion/handlers_api_test.go +++ b/service/assertion/handlers_api_test.go @@ -83,6 +83,7 @@ func (s *AssertionSuite) TestAssertionHandler(c *check.C) { {invalidModel(), 400, response.JSONHeader, "ValidAPIKey"}, {unauthBrand(), 400, response.JSONHeader, "ValidAPIKey"}, {unknownBrand(), 400, response.JSONHeader, "ValidAPIKey"}, + {unknownModel(), 400, response.JSONHeader, "ValidAPIKey"}, } for _, t := range tests { @@ -146,6 +147,15 @@ func unknownBrand() []byte { return d } +func unknownModel() []byte { + a := assertion.ModelAssertionRequest{ + BrandID: "system", + Name: "lost", + } + d, _ := json.Marshal(a) + return d +} + func classicModel() []byte { a := assertion.ModelAssertionRequest{ BrandID: "system", diff --git a/service/assertion/handlers_user_test.go b/service/assertion/handlers_user_test.go index 3fa4c04b..1b04713a 100644 --- a/service/assertion/handlers_user_test.go +++ b/service/assertion/handlers_user_test.go @@ -46,6 +46,7 @@ func (s *AssertionSuite) TestSystemUserAssertionHandler(c *check.C) { {generateSystemUserRequestInactiveModel(), 400, false}, {generateSystemUserRequestInvalidAssertion(), 400, false}, {generateSystemUserRequestInvalidSince(), 200, true}, + {generateSystemUserRequestEmptyCredentials(), 400, false}, } for _, test := range tests { @@ -103,3 +104,18 @@ func generateSystemUserRequestInvalidAssertion() string { return string(req) } + +func generateSystemUserRequestEmptyCredentials() string { + request := assertion.SystemUserRequest{ + Email: "test@example.com", + Name: "John Doe", + Username: "jdoe", + Password: "", + ModelID: 1, + Since: "2017-03-24T12:34:00Z", + SSHKeys: []string{}, + } + req, _ := json.Marshal(request) + + return string(req) +} diff --git a/service/pivot/handlers_pivot.go b/service/pivot/handlers_pivot.go index 5f4643ec..b5c8e1ff 100644 --- a/service/pivot/handlers_pivot.go +++ b/service/pivot/handlers_pivot.go @@ -205,8 +205,7 @@ func findModelPivot(brand, modelName, serial, apiKey string) (datastore.Substore // Validate the model by checking that it exists on the database model, err := datastore.Environ.DB.FindModel(brand, modelName, apiKey) if err != nil { - svlog.Message("PIVOT", "invalid-model", "Cannot find model with the matching brand and model") - return datastore.Substore{}, response.ErrorInvalidModel + return datastore.Substore{}, response.ErrorInvalidModel("PIVOT", modelName, brand, apiKey) } // Check for a sub-store model for the pivot diff --git a/service/response/errors.go b/service/response/errors.go index 927e4c02..28b3f444 100644 --- a/service/response/errors.go +++ b/service/response/errors.go @@ -20,7 +20,12 @@ package response -import "net/http" +import ( + "fmt" + "net/http" + + svlog "github.com/CanonicalLtd/serial-vault/service/log" +) // ErrorResponse is a generic JSON error response structure from an API method type ErrorResponse struct { @@ -44,9 +49,6 @@ var ( ErrorInvalidType = ErrorResponse{false, "invalid-type", "", "The assertion type must be 'serial'", http.StatusBadRequest} ErrorInvalidSecondType = ErrorResponse{false, "invalid-second-type", "", "The 2nd assertion type must be 'model'", http.StatusBadRequest} ErrorInvalidNonce = ErrorResponse{false, "invalid-nonce", "", "Nonce is invalid or expired", http.StatusBadRequest} - ErrorInvalidModel = ErrorResponse{false, "invalid-model", "", "Cannot find model with the matching brand and model", http.StatusBadRequest} - ErrorInvalidModelID = ErrorResponse{false, "invalid-model", "", "Cannot find model with the selected ID", http.StatusBadRequest} - ErrorInvalidModelSubstore = ErrorResponse{false, "invalid-model", "", "Cannot find a matching model or sub-store model", http.StatusBadRequest} ErrorInvalidSubstore = ErrorResponse{false, "invalid-substore", "", "Cannot find sub-store mapping for the model", http.StatusBadRequest} ErrorInactiveModel = ErrorResponse{false, "invalid-model", "", "The model is linked with an inactive signing-key", http.StatusBadRequest} ErrorInvalidAccount = ErrorResponse{false, "invalid-account", "", "The account cannot be found", http.StatusBadRequest} @@ -66,3 +68,26 @@ var ( ErrorSignAssertion = ErrorResponse{false, "signing-assertion", "", "Error signing the assertion", http.StatusBadRequest} ErrorGenerateNonce = ErrorResponse{false, "generate-nonce", "", "Error generating a nonce. Please try again later", http.StatusBadRequest} ) + +// ErrorInvalidModel returns error message about wrong model +func ErrorInvalidModel(from, modelName, brand, apiKey string) ErrorResponse { + msg := fmt.Sprintf("Cannot find model %s with the matching brand %s and apiKey %s", modelName, brand, apiKey) + svlog.Message(from, "invalid-model", msg) + return ErrorResponse{false, "invalid-model", "", msg, http.StatusBadRequest} +} + +// ErrorInvalidModelID returns InvalidModelID error message +func ErrorInvalidModelID(from string, ModelID int) ErrorResponse { + msg := fmt.Sprintf("Cannot find model with the selected ID %d", ModelID) + svlog.Message(from, "invalid-model", msg) + + return ErrorResponse{false, "invalid-model", "", msg, http.StatusBadRequest} +} + +// ErrorInvalidModelSubstore returns invalid model substore error +func ErrorInvalidModelSubstore(from, brandID, modelName, apiKey, serialNumer string) ErrorResponse { + msg := fmt.Sprintf("Cannot find a matching model or sub-store model %s with the matching brand %s apiKey %s and serialNumer %s", + modelName, brandID, apiKey, serialNumer) + svlog.Message(from, "invalid-model", msg) + return ErrorResponse{false, "invalid-model", "", msg, http.StatusBadRequest} +} diff --git a/service/response/response.go b/service/response/response.go index 8c2677e3..bbade48b 100644 --- a/service/response/response.go +++ b/service/response/response.go @@ -48,7 +48,20 @@ func FormatStandardResponse(success bool, errorCode, errorSubcode, message strin // Encode the response as JSON if err := json.NewEncoder(w).Encode(response); err != nil { - log.Printf("Error forming the boolean response (%v)\n. %v", response, err) + log.Printf("Error forming the standard response (%v)\n. %v", response, err) + return err + } + return nil +} + +// FormatErrorResponse returns a JSON response from an API method, indicating failure. +func FormatErrorResponse(response ErrorResponse, w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(response.StatusCode) + + // Encode the response as JSON + if err := json.NewEncoder(w).Encode(response); err != nil { + log.Printf("Error forming the error response (%v)\n. %v", response, err) return err } return nil diff --git a/service/sign/handlers_sign.go b/service/sign/handlers_sign.go index 13771d26..2cc8265e 100644 --- a/service/sign/handlers_sign.go +++ b/service/sign/handlers_sign.go @@ -346,7 +346,8 @@ func findModel(brandID, modelName, serialNumer, apiKey string) (datastore.Model, // Validate the model by checking that it exists on the database model, err := datastore.Environ.DB.FindModel(brandID, modelName, apiKey) if err != nil { - svlog.Message("SIGN", response.ErrorInvalidModel.Code, response.ErrorInvalidModel.Message) + msg := fmt.Sprintf("Cannot find model %s with the matching brand %s and apiKey %s", modelName, brandID, apiKey) + svlog.Message("SIGN", "invalid-model", msg) } else { // Found the model, so return it return model, response.ErrorResponse{Success: true} @@ -357,12 +358,11 @@ func findModel(brandID, modelName, serialNumer, apiKey string) (datastore.Model, substore, err := datastore.Environ.DB.GetSubstoreModel(brandID, modelName, serialNumer) if err != nil { log.Println(err) - svlog.Message("CHECK", response.ErrorInvalidModelSubstore.Code, response.ErrorInvalidModelSubstore.Message) - return model, response.ErrorInvalidModelSubstore + return model, response.ErrorInvalidModelSubstore("CHECK", brandID, modelName, apiKey, serialNumer) } if substore.FromModel.APIKey != apiKey { - return substore.FromModel, response.ErrorInvalidModelSubstore + return substore.FromModel, response.ErrorInvalidModelSubstore("CHECK", brandID, modelName, apiKey, serialNumer) } return substore.FromModel, response.ErrorResponse{Success: true}