[XAPID-1037] refactor and add tests
diff --git a/api.go b/api.go index d8cbe34..220da6e 100644 --- a/api.go +++ b/api.go
@@ -15,7 +15,6 @@ package apidVerifyApiKey import ( - "database/sql" "encoding/json" "errors" "io" @@ -47,14 +46,14 @@ } // handle client API -func (apiManager *apiManager) handleRequest(w http.ResponseWriter, r *http.Request) { +func (a *apiManager) handleRequest(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") verifyApiKeyReq, err := validateRequest(r.Body, w) if err != nil { return } - b, err := apiManager.verifyAPIKey(verifyApiKeyReq) + b, err := a.verifyAPIKey(verifyApiKeyReq) if err != nil { respStatusCode, atoierr := strconv.Atoi(err.Error()) @@ -91,9 +90,10 @@ log.Debug(verifyApiKeyReq) // 2. verify params + // TODO : make this method of verifyApiKeyReq struct if verifyApiKeyReq.Action == "" || verifyApiKeyReq.ApiProxyName == "" || verifyApiKeyReq.OrganizationName == "" || verifyApiKeyReq.EnvironmentName == "" || verifyApiKeyReq.Key == "" { - // TODO : set correct fields in error response - errorResponse := errorResponse("Bad_REQUEST", "Missing element") + // TODO : set correct missing fields in error response + errorResponse, _ := json.Marshal(errorResponse("Bad_REQUEST", "Missing element", http.StatusBadRequest)) w.WriteHeader(http.StatusBadRequest) w.Write(errorResponse) return verifyApiKeyReq, errors.New("Bad_REQUEST") @@ -112,25 +112,26 @@ err := apiM.dbMan.getApiKeyDetails(&dataWrapper) switch { - case err == sql.ErrNoRows: + case err != nil && err.Error() == "InvalidApiKey": reason := "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" errorCode := "oauth.v2.InvalidApiKey" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + errResponse := errorResponse(reason, errorCode, http.StatusOK) + return json.Marshal(errResponse) case err != nil: reason := err.Error() errorCode := "SEARCH_INTERNAL_ERROR" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusInternalServerError)) + errResponse := errorResponse(reason, errorCode, http.StatusInternalServerError) + return json.Marshal(errResponse) } - apiProduct := shortListApiProduct(dataWrapper.apiProducts, verifyApiKeyReq) - dataWrapper.verifyApiKeySuccessResponse.ApiProduct = apiProduct + dataWrapper.verifyApiKeySuccessResponse.ApiProduct = shortListApiProduct(dataWrapper.apiProducts, verifyApiKeyReq) /* * Perform all validations */ - errResponse, err := apiM.performValidations(dataWrapper) + errResponse := apiM.performValidations(dataWrapper) if errResponse != nil { - return errResponse, err + return json.Marshal(&errResponse) } apiM.enrichAttributes(&dataWrapper) @@ -183,79 +184,78 @@ } } - // TODO : remove this check from here and let performValidations take care of checking this - if !verifyApiKeyReq.ValidateAgainstApiProxiesAndEnvs { - if len(rankedProducts[2]) > 0 { - return rankedProducts[2][0] - } else if len(rankedProducts[3]) > 0 { - return rankedProducts[3][0] - } + if len(rankedProducts[2]) > 0 { + return rankedProducts[2][0] + } else if len(rankedProducts[3]) > 0 { + return rankedProducts[3][0] } return bestMathcedProduct } -func (apiM apiManager) performValidations(dataWrapper VerifyApiKeyRequestResponseDataWrapper) ([]byte, error) { +func (apiM apiManager) performValidations(dataWrapper VerifyApiKeyRequestResponseDataWrapper) *ErrorResponse { clientIdDetails := dataWrapper.verifyApiKeySuccessResponse.ClientId verifyApiKeyReq := dataWrapper.verifyApiKeyRequest appDetails := dataWrapper.verifyApiKeySuccessResponse.App tempDeveloperDetails := dataWrapper.tempDeveloperDetails cType := dataWrapper.ctype apiProductDetails := dataWrapper.verifyApiKeySuccessResponse.ApiProduct + var reason, errorCode string if !strings.EqualFold("APPROVED", clientIdDetails.Status) { - reason := "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" - errorCode := "oauth.v2.ApiKeyNotApproved" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + reason = "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" + errorCode = "oauth.v2.ApiKeyNotApproved" } if !strings.EqualFold("APPROVED", appDetails.Status) { - reason := "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" - errorCode := "keymanagement.service.invalid_client-app_not_approved" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + reason = "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" + errorCode = "keymanagement.service.invalid_client-app_not_approved" } if !strings.EqualFold("ACTIVE", tempDeveloperDetails.Status) { - reason := "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" - errorCode := "keymanagement.service.DeveloperStatusNotActive" + reason = "API Key verify failed for (" + verifyApiKeyReq.Key + ", " + verifyApiKeyReq.OrganizationName + ")" + errorCode = "keymanagement.service.DeveloperStatusNotActive" if cType == "company" { errorCode = "keymanagement.service.CompanyStatusNotActive" } - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) } if dataWrapper.verifyApiKeySuccessResponse.ApiProduct.Id == "" { - reason := "Path Validation Failed. Product not resolved" - errorCode := "oauth.v2.InvalidApiKeyForGivenResource" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + reason = "Path Validation Failed. Product not resolved" + errorCode = "oauth.v2.InvalidApiKeyForGivenResource" } - result := validatePath(apiProductDetails.Resources, verifyApiKeyReq.UriPath) - if result == false { - reason := "Path Validation Failed (" + strings.Join(apiProductDetails.Resources, ", ") + " vs " + verifyApiKeyReq.UriPath + ")" - errorCode := "oauth.v2.InvalidApiKeyForGivenResource" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + result := len(apiProductDetails.Resources) == 0 || validatePath(apiProductDetails.Resources, verifyApiKeyReq.UriPath) + if !result { + reason = "Path Validation Failed (" + strings.Join(apiProductDetails.Resources, ", ") + " vs " + verifyApiKeyReq.UriPath + ")" + errorCode = "oauth.v2.InvalidApiKeyForGivenResource" } // TODO : empty check - if verifyApiKeyReq.ValidateAgainstApiProxiesAndEnvs && !contains(apiProductDetails.Apiproxies, verifyApiKeyReq.ApiProxyName) { - reason := "Proxy Validation Failed (" + strings.Join(apiProductDetails.Apiproxies, ", ") + " vs " + verifyApiKeyReq.ApiProxyName + ")" - errorCode := "oauth.v2.InvalidApiKeyForGivenResource" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + if verifyApiKeyReq.ValidateAgainstApiProxiesAndEnvs && (len(apiProductDetails.Apiproxies) > 0 && !contains(apiProductDetails.Apiproxies, verifyApiKeyReq.ApiProxyName)) { + reason = "Proxy Validation Failed (" + strings.Join(apiProductDetails.Apiproxies, ", ") + " vs " + verifyApiKeyReq.ApiProxyName + ")" + errorCode = "oauth.v2.InvalidApiKeyForGivenResource" } /* Verify if the ENV matches */ - if verifyApiKeyReq.ValidateAgainstApiProxiesAndEnvs && !contains(apiProductDetails.Environments, verifyApiKeyReq.EnvironmentName) { - reason := "ENV Validation Failed (" + strings.Join(apiProductDetails.Environments, ", ") + " vs " + verifyApiKeyReq.EnvironmentName + ")" - errorCode := "oauth.v2.InvalidApiKeyForGivenResource" - return errorResponse(reason, errorCode), errors.New(strconv.Itoa(http.StatusOK)) + if verifyApiKeyReq.ValidateAgainstApiProxiesAndEnvs && (len(apiProductDetails.Environments) > 0 && !contains(apiProductDetails.Environments, verifyApiKeyReq.EnvironmentName)) { + reason = "ENV Validation Failed (" + strings.Join(apiProductDetails.Environments, ", ") + " vs " + verifyApiKeyReq.EnvironmentName + ")" + errorCode = "oauth.v2.InvalidApiKeyForGivenResource" + } - return nil, nil + if errorCode != "" { + log.Debug("Validation error occoured ", errorCode, " ", reason) + ee := errorResponse(reason, errorCode, http.StatusOK) + return &ee + } + + return nil } func (a *apiManager) enrichAttributes(dataWrapper *VerifyApiKeyRequestResponseDataWrapper) { + attributeMap := a.dbMan.getKmsAttributes(dataWrapper.tenant_id, dataWrapper.verifyApiKeySuccessResponse.ClientId.ClientId, dataWrapper.tempDeveloperDetails.Id, dataWrapper.verifyApiKeySuccessResponse.ApiProduct.Id, dataWrapper.verifyApiKeySuccessResponse.App.Id) clientIdAttributes := attributeMap[dataWrapper.verifyApiKeySuccessResponse.ClientId.ClientId] @@ -269,7 +269,7 @@ dataWrapper.tempDeveloperDetails.Attributes = developerAttributes } -func errorResponse(reason, errorCode string) []byte { +func errorResponse(reason, errorCode string, statusCode int) ErrorResponse { if errorCode == "SEARCH_INTERNAL_ERROR" { log.Error(reason) } else { @@ -278,7 +278,7 @@ resp := ErrorResponse{ ResponseCode: errorCode, ResponseMessage: reason, + StatusCode: statusCode, } - ret, _ := json.Marshal(resp) - return ret + return resp }
diff --git a/api_ShortListApiProduct_test.go b/api_ShortListApiProduct_test.go index f604b83..f41e118 100644 --- a/api_ShortListApiProduct_test.go +++ b/api_ShortListApiProduct_test.go
@@ -130,13 +130,3 @@ } } } - -func TestShortListApiProductValidateProxyEnv(t *testing.T) { - for _, td := range shortListApiProductTestData { - td.req.ValidateAgainstApiProxiesAndEnvs = true - actual := shortListApiProduct(td.dbData, td.req) - if actual.Id != td.expectedWhenValidateProxyEnvIsTrue { - t.Errorf("TestData (%s) ValidateProxyEnv (%t) : expected (%s), actual (%s)", td.testDesc, td.req.ValidateAgainstApiProxiesAndEnvs, td.expectedResult, actual.Id) - } - } -}
diff --git a/api_performValidations_test.go b/api_performValidations_test.go index 1054308..c6e015d 100644 --- a/api_performValidations_test.go +++ b/api_performValidations_test.go
@@ -15,6 +15,7 @@ package apidVerifyApiKey import ( + "encoding/json" "github.com/30x/apid-core" "github.com/30x/apid-core/factory" "testing" @@ -212,6 +213,102 @@ }, }, }, + { + testDesc: "resources not configured in db", + expectedResult: "", + expectedWhenValidateProxyEnvIsTrue: "", + dataWrapper: VerifyApiKeyRequestResponseDataWrapper{ + verifyApiKeyRequest: VerifyApiKeyRequest{ + Key: "test-key", + OrganizationName: "test-org", + UriPath: "/test", + ApiProxyName: "test-proxy-name", + EnvironmentName: "test-env-name", + }, + tempDeveloperDetails: DeveloperDetails{ + Status: "ACTIVE", + }, + verifyApiKeySuccessResponse: VerifyApiKeySuccessResponse{ + ApiProduct: ApiProductDetails{ + Id: "test-api-product", + Resources: []string{}, + Apiproxies: []string{"test-proxy-name"}, + Environments: []string{"test-env-name"}, + Status: "APPROVED", + }, + App: AppDetails{ + Status: "APPROVED", + }, + ClientId: ClientIdDetails{ + Status: "APPROVED", + }, + }, + }, + }, + { + testDesc: "proxies not configured in db", + expectedResult: "", + expectedWhenValidateProxyEnvIsTrue: "", + dataWrapper: VerifyApiKeyRequestResponseDataWrapper{ + verifyApiKeyRequest: VerifyApiKeyRequest{ + Key: "test-key", + OrganizationName: "test-org", + UriPath: "/test", + ApiProxyName: "test-proxy-name", + EnvironmentName: "test-env-name", + }, + tempDeveloperDetails: DeveloperDetails{ + Status: "ACTIVE", + }, + verifyApiKeySuccessResponse: VerifyApiKeySuccessResponse{ + ApiProduct: ApiProductDetails{ + Id: "test-api-product", + Resources: []string{"/test"}, + Apiproxies: []string{}, + Environments: []string{"test-env-name"}, + Status: "APPROVED", + }, + App: AppDetails{ + Status: "APPROVED", + }, + ClientId: ClientIdDetails{ + Status: "APPROVED", + }, + }, + }, + }, + { + testDesc: "environments not configured in db", + expectedResult: "", + expectedWhenValidateProxyEnvIsTrue: "", + dataWrapper: VerifyApiKeyRequestResponseDataWrapper{ + verifyApiKeyRequest: VerifyApiKeyRequest{ + Key: "test-key", + OrganizationName: "test-org", + UriPath: "/test", + ApiProxyName: "test-proxy-name", + EnvironmentName: "test-env-name", + }, + tempDeveloperDetails: DeveloperDetails{ + Status: "ACTIVE", + }, + verifyApiKeySuccessResponse: VerifyApiKeySuccessResponse{ + ApiProduct: ApiProductDetails{ + Id: "test-api-product", + Resources: []string{"/test"}, + Apiproxies: []string{"test-proxy-name"}, + Environments: []string{}, + Status: "APPROVED", + }, + App: AppDetails{ + Status: "APPROVED", + }, + ClientId: ClientIdDetails{ + Status: "APPROVED", + }, + }, + }, + }, } func TestPerformValidation(t *testing.T) { @@ -221,12 +318,16 @@ log = factory.DefaultServicesFactory().Log() a := apiManager{} for _, td := range performValidationsTestData { - actual, err := a.performValidations(td.dataWrapper) - if string(actual) != td.expectedResult { - t.Errorf("TestData (%s) ValidateProxyEnv (%t) : expected (%s), actual (%s)", td.testDesc, td.dataWrapper.verifyApiKeyRequest.ValidateAgainstApiProxiesAndEnvs, td.expectedResult, string(actual)) + actualObject := a.performValidations(td.dataWrapper) + var actual string + if actualObject != nil { + a, _ := json.Marshal(&actualObject) + actual = string(a) + } else { + actual = "" } - if err != nil && err.Error() != "200" { - t.Error("Expected to return 200 status code") + if string(actual) != td.expectedResult { + t.Errorf("TestData (%s) ValidateProxyEnv (%t) : expected (%s), actual (%s)", td.testDesc, td.dataWrapper.verifyApiKeyRequest.ValidateAgainstApiProxiesAndEnvs, td.expectedResult, actual) } } } @@ -239,12 +340,17 @@ a := apiManager{} for _, td := range performValidationsTestData { td.dataWrapper.verifyApiKeyRequest.ValidateAgainstApiProxiesAndEnvs = true - actual, err := a.performValidations(td.dataWrapper) - if string(actual) != td.expectedWhenValidateProxyEnvIsTrue { - t.Errorf("TestData (%s) ValidateProxyEnv (%t) : expected (%s), actual (%s)", td.testDesc, td.dataWrapper.verifyApiKeyRequest.ValidateAgainstApiProxiesAndEnvs, td.expectedWhenValidateProxyEnvIsTrue, string(actual)) + actualObject := a.performValidations(td.dataWrapper) + var actual string + if actualObject != nil { + a, _ := json.Marshal(&actualObject) + actual = string(a) + } else { + actual = "" } - if err != nil && err.Error() != "200" { - t.Error("Expected to return 200 status code") + if string(actual) != td.expectedWhenValidateProxyEnvIsTrue { + + t.Errorf("TestData (%s) ValidateProxyEnv (%t) : expected (%s), actual (%s)", td.testDesc, td.dataWrapper.verifyApiKeyRequest.ValidateAgainstApiProxiesAndEnvs, td.expectedWhenValidateProxyEnvIsTrue, actual) } } }
diff --git a/data.go b/data.go index 2674a92..695ef8e 100644 --- a/data.go +++ b/data.go
@@ -254,7 +254,7 @@ if err != nil { log.Error("error fetching verify apikey details ", err) - return err + return errors.New("InvalidApiKey") } if dataWrapper.verifyApiKeySuccessResponse.App.CallbackUrl != "" {
diff --git a/verifyAPIKey_suite_test_old.go.bak b/verifyAPIKey_suite_test_old.go.bak deleted file mode 100644 index ff656a3..0000000 --- a/verifyAPIKey_suite_test_old.go.bak +++ /dev/null
@@ -1,142 +0,0 @@ -// Copyright 2017 Google 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 apidVerifyApiKey - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "github.com/30x/apid-core" - "github.com/30x/apid-core/factory" - "io/ioutil" - //"net/http" - "net/http/httptest" - "os" - "testing" -) - -var ( - testTempDir string - testServer *httptest.Server -) - -var _ = BeforeSuite(func() { - apid.Initialize(factory.DefaultServicesFactory()) - - config := apid.Config() - - var err error - testTempDir, err = ioutil.TempDir("", "api_test") - Expect(err).NotTo(HaveOccurred()) - - config.Set("data_path", testTempDir) - - apid.InitializePlugins("") - - db, err := apid.Data().DB() - Expect(err).NotTo(HaveOccurred()) - //setDB(db) - //createTables(db) - //createApidClusterTables(db) - //addScopes(db) - //testServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - // if req.URL.Path == apiPath { - // handleRequest(w, req) - // } - //})) - - createTestData(db) -}) - -var _ = AfterSuite(func() { - apid.Events().Close() - if testServer != nil { - testServer.Close() - } - os.RemoveAll(testTempDir) -}) - -func TestVerifyAPIKey(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "VerifyAPIKey Suite") -} - -func createTestData(db apid.DB) { - txn, err := db.Begin() - Expect(err).ShouldNot(HaveOccurred()) - // api products - for i := 0; i < 10; i++ { - generateTestApiProduct(i, txn) - } - // developers - for i := 0; i < 10; i++ { - generateTestDeveloper(i, txn) - } - - // application - var j, k int - for i := 0; i < 10; i++ { - for j = k; j < 10+k; j++ { - generateTestApp(j, i, txn) - } - k = j - } - // app credentials - for i := 0; i < 10; i++ { - generateTestAppCreds(i, txn) - } - // api product mapper - for i := 0; i < 10; i++ { - generateTestApiProductMapper(i, txn) - } - - // Following are data for company - // api products - for i := 100; i < 110; i++ { - generateTestApiProduct(i, txn) - } - - // companies - for i := 100; i < 110; i++ { - generateTestCompany(i, txn) - } - - // company developers - for i := 100; i < 110; i++ { - generateTestCompanyDeveloper(i, txn) - } - - // application - k = 100 - for i := 100; i < 110; i++ { - for j = k; j < 100+k; j++ { - generateTestAppCompany(j, i, txn) - } - k = j - } - // app credentials - for i := 100; i < 110; i++ { - generateTestAppCreds(i, txn) - } - // api product mapper - for i := 100; i < 110; i++ { - generateTestApiProductMapper(i, txn) - } - - txn.Commit() - var count int64 - db.QueryRow("select count(*) from EDGEX_DATA_SCOPE").Scan(&count) - log.Info("Found ", count) -}
diff --git a/verifyApiKeyStructs.go b/verifyApiKeyStructs.go index 49a4b15..8c15778 100644 --- a/verifyApiKeyStructs.go +++ b/verifyApiKeyStructs.go
@@ -87,6 +87,7 @@ type ErrorResponse struct { ResponseCode string `json:"response_code,omitempty"` ResponseMessage string `json:"response_message,omitempty"` + StatusCode int `json:"-"` Kind string `json:"kind,omitempty"` }