[ISSUE-67869881] address comments, add test cases
diff --git a/accessEntity/data.go b/accessEntity/data.go index bb53fea..4634f61 100644 --- a/accessEntity/data.go +++ b/accessEntity/data.go
@@ -16,7 +16,6 @@ import ( "database/sql" "fmt" - "github.com/apid/apid-core/cipher" "github.com/apid/apid-core/util" "github.com/apid/apidApiMetadata/common" "strings" @@ -96,9 +95,7 @@ if err != nil || !email.Valid { return "", err } - // decryption - ret, err := d.CipherManager.TryDecryptBase64(email.String, org) - return ret, err + return email.String, err } func (d *DbManager) GetComNames(id string, idType string) ([]string, error) { @@ -287,6 +284,10 @@ appCredentials, err = d.getAppCredentialByAppId(priVal, org) } + if err != nil { + return + } + var plaintext string for i := range appCredentials { if plaintext, err = d.CipherManager.TryDecryptBase64(appCredentials[i].ConsumerSecret, org); err != nil { @@ -309,15 +310,6 @@ case IdentifierDeveloperId: developers, err = d.getDeveloperById(priVal, org) } - - var plaintext string - for i := range developers { - if plaintext, err = d.CipherManager.TryDecryptBase64(developers[i].Email, org); err != nil { - return - } - developers[i].Email = plaintext - } - return } @@ -369,17 +361,12 @@ appQuery = selectAppByNameAndDeveloperId( "?", selectDeveloperByEmail( - "?, ?", + "?", "id", ), "id", ) - var encrypted string - encrypted, err = d.CipherManager.EncryptBase64(devEmail, org, cipher.ModeEcb, cipher.PaddingPKCS5) - if err != nil { - return - } - args = append(args, devEmail, encrypted) + args = append(args, devEmail) case devId != "": appQuery = selectAppByNameAndDeveloperId( "?", @@ -438,17 +425,12 @@ query = selectAppByNameAndDeveloperId( "?", selectDeveloperByEmail( - "?, ?", + "?", "id", ), cols..., ) - var encrypted string - encrypted, err = d.CipherManager.EncryptBase64(devEmail, org, cipher.ModeEcb, cipher.PaddingPKCS5) - if err != nil { - return - } - args = append(args, devEmail, encrypted) + args = append(args, devEmail) case devId != "": query = selectAppByNameAndDeveloperId( "?", @@ -610,16 +592,11 @@ func (d *DbManager) getDeveloperByEmail(email, org string) (developers []common.Developer, err error) { cols := []string{"*"} query := selectDeveloperByEmail( - "?, ?", + "?", cols..., ) + " AND dev.tenant_id IN " + sql_select_tenant_org //log.Debugf("getDeveloperByEmail: %v", query) - var encrypted string - encrypted, err = d.CipherManager.EncryptBase64(email, org, cipher.ModeEcb, cipher.PaddingPKCS5) - if err != nil { - return - } - err = d.GetDb().QueryStructs(&developers, query, email, encrypted, org) + err = d.GetDb().QueryStructs(&developers, query, email, org) return }
diff --git a/accessEntity/data_test.sql b/accessEntity/data_test.sql index 16aa13f..7f01503 100644 --- a/accessEntity/data_test.sql +++ b/accessEntity/data_test.sql
@@ -36,9 +36,9 @@ CREATE TABLE kms_company_developer (tenant_id text,company_id text,developer_id text,roles text,created_at blob,created_by text,updated_at blob,updated_by text,_change_selector text, primary key (tenant_id,company_id,developer_id)); INSERT INTO "kms_company_developer" VALUES('515211e9','a94f75e2-69b0-44af-8776-155df7c7d22e','590f33bf-f05c-48c1-bb93-183759bd9ee1','admin','2017-11-02 16:00:16.287+00:00','haoming@apid.git','2017-11-02 16:00:16.287+00:00','haoming@apid.git','515211e9'); CREATE TABLE kms_developer (id text,tenant_id text,username text,first_name text,last_name text,password text,email text,status text,encrypted_password text,salt text,created_at blob,created_by text,updated_at blob,updated_by text,_change_selector text, primary key (id,tenant_id)); -INSERT INTO "kms_developer" VALUES('e41f04e8-9d3f-470a-8bfd-c7939945896c','515211e9','haoming','haoming','zhang','','encrypted:bar@google.com','ACTIVE','','','2017-08-16 22:39:46.669+00:00','foo@google.com','2017-08-16 22:39:46.669+00:00','foo@google.com','515211e9'); -INSERT INTO "kms_developer" VALUES('47d862db-884f-4b8e-9649-fe6d0be1a739','515211e9','qwe','qwe','qwe','','encrypted:barfoo@google.com','ACTIVE','','','2017-10-12 19:12:48.306+00:00','haoming@apid.git','2017-10-12 19:12:48.306+00:00','haoming@apid.git','515211e9'); -INSERT INTO "kms_developer" VALUES('590f33bf-f05c-48c1-bb93-183759bd9ee1','515211e9','remoteproxy','remote','proxy','','encrypted:fooo@google.com','ACTIVE','','','2017-09-20 23:03:52.327+00:00','haoming@apid.git','2017-09-20 23:03:52.327+00:00','haoming@apid.git','515211e9'); +INSERT INTO "kms_developer" VALUES('e41f04e8-9d3f-470a-8bfd-c7939945896c','515211e9','haoming','haoming','zhang','','bar@google.com','ACTIVE','','','2017-08-16 22:39:46.669+00:00','foo@google.com','2017-08-16 22:39:46.669+00:00','foo@google.com','515211e9'); +INSERT INTO "kms_developer" VALUES('47d862db-884f-4b8e-9649-fe6d0be1a739','515211e9','qwe','qwe','qwe','','barfoo@google.com','ACTIVE','','','2017-10-12 19:12:48.306+00:00','haoming@apid.git','2017-10-12 19:12:48.306+00:00','haoming@apid.git','515211e9'); +INSERT INTO "kms_developer" VALUES('590f33bf-f05c-48c1-bb93-183759bd9ee1','515211e9','remoteproxy','remote','proxy','','fooo@google.com','ACTIVE','','','2017-09-20 23:03:52.327+00:00','haoming@apid.git','2017-09-20 23:03:52.327+00:00','haoming@apid.git','515211e9'); CREATE TABLE edgex_apid_cluster (id text,name text,description text,umbrella_org_app_name text,created blob,created_by text,updated blob,updated_by text,_change_selector text, last_sequence text DEFAULT '', primary key (id)); INSERT INTO "edgex_apid_cluster" VALUES('950b30f1-8c41-4bf5-94a3-f10c104ff5d4','apid-haomingOrgCluster','','X-sZXhaOymL6VtWnNQqK7uPsFyPvZYq6FFnrc8','2017-08-23 23:31:49.134+00:00','temp@google.com','2017-08-23 23:31:49.134+00:00','temp@google.com','950b30f1-8c41-4bf5-94a3-f10c104ff5d4',''); CREATE TABLE kms_api_product (id text,tenant_id text,name text,display_name text,description text,api_resources text,approval_type text,scopes text,proxies text,environments text,quota text,quota_time_unit text,quota_interval integer,created_at blob,created_by text,updated_at blob,updated_by text,_change_selector text, primary key (id,tenant_id)); @@ -49,9 +49,9 @@ INSERT INTO "kms_api_product" VALUES('db90a25a-15c8-42ad-96c1-63ed9682b5a9','515211e9','apigee-remote-proxy','apigee-remote-proxy','','{/**,/}','AUTO','{""}','{apigee-remote-proxy}','{prod,test}','','',NULL,'2017-09-20 23:05:09.234+00:00','haoming@apid.git','2017-09-20 23:05:09.234+00:00','haoming@apid.git','515211e9'); INSERT INTO "kms_api_product" VALUES('fea8a6d5-8d34-477f-ac82-c397eaec06af','515211e9','testproductsdljnkpt','testproductsdljnkpt','','{/res1}','AUTO','{}','{}','{test}','','',NULL,'2017-11-02 16:00:15.608+00:00','haoming@apid.git','2017-11-02 16:00:18.125+00:00','haoming@apid.git','515211e9'); CREATE TABLE kms_app_credential (id text,tenant_id text,consumer_secret text,app_id text,method_type text,status text,issued_at blob,expires_at blob,app_status text,scopes text,created_at blob,created_by text,updated_at blob,updated_by text,_change_selector text, primary key (id,tenant_id)); -INSERT INTO "kms_app_credential" VALUES('abcd','515211e9','secret1','408ad853-3fa0-402f-90ee-103de98d71a5','','APPROVED','2017-08-18 22:13:18.35+00:00','','','{}','2017-08-18 22:13:18.35+00:00','-NA-','2017-08-18 22:13:18.352+00:00','-NA-','515211e9'); -INSERT INTO "kms_app_credential" VALUES('dcba','515211e9','secret2','ae053aee-f12d-4591-84ef-2e6ae0d4205d','','APPROVED','2017-09-20 23:05:59.148+00:00','','','{}','2017-09-20 23:05:59.148+00:00','-NA-','2017-09-20 23:05:59.151+00:00','-NA-','515211e9'); -INSERT INTO "kms_app_credential" VALUES('wxyz','515211e9','secret3','35608afe-2715-4064-bb4d-3cbb4e82c474','','APPROVED','2017-11-02 16:00:16.512+00:00','','','{}','2017-11-02 16:00:16.512+00:00','-NA-','2017-11-02 16:00:16.514+00:00','-NA-','515211e9'); +INSERT INTO "kms_app_credential" VALUES('abcd','515211e9','encrypted:secret1','408ad853-3fa0-402f-90ee-103de98d71a5','','APPROVED','2017-08-18 22:13:18.35+00:00','','','{}','2017-08-18 22:13:18.35+00:00','-NA-','2017-08-18 22:13:18.352+00:00','-NA-','515211e9'); +INSERT INTO "kms_app_credential" VALUES('dcba','515211e9','encrypted:secret2','ae053aee-f12d-4591-84ef-2e6ae0d4205d','','APPROVED','2017-09-20 23:05:59.148+00:00','','','{}','2017-09-20 23:05:59.148+00:00','-NA-','2017-09-20 23:05:59.151+00:00','-NA-','515211e9'); +INSERT INTO "kms_app_credential" VALUES('wxyz','515211e9','encrypted:secret3','35608afe-2715-4064-bb4d-3cbb4e82c474','','APPROVED','2017-11-02 16:00:16.512+00:00','','','{}','2017-11-02 16:00:16.512+00:00','-NA-','2017-11-02 16:00:16.514+00:00','-NA-','515211e9'); CREATE TABLE kms_app_credential_apiproduct_mapper (tenant_id text,appcred_id text,app_id text,apiprdt_id text,status text,_change_selector text, primary key (tenant_id,appcred_id,app_id,apiprdt_id)); INSERT INTO "kms_app_credential_apiproduct_mapper" VALUES('515211e9','abcd','408ad853-3fa0-402f-90ee-103de98d71a5','b7e0970c-4677-4b05-8105-5ea59fdcf4e7','APPROVED','515211e9'); INSERT INTO "kms_app_credential_apiproduct_mapper" VALUES('515211e9','dcba','ae053aee-f12d-4591-84ef-2e6ae0d4205d','db90a25a-15c8-42ad-96c1-63ed9682b5a9','APPROVED','515211e9');
diff --git a/common/cipher.go b/common/cipher.go index 6e88d33..5e727a2 100644 --- a/common/cipher.go +++ b/common/cipher.go
@@ -27,7 +27,7 @@ "time" ) -const RegEncrypted = `^\{[0-9A-Za-z]+/[0-9A-Za-z]+/[0-9A-Za-z]+\}.` +const regEncrypted = `^\{[0-9A-Za-z]+/[0-9A-Za-z]+/[0-9A-Za-z]+\}.` const retrieveEncryptKeyPath = "/encryptionkey" const EncryptAes = "AES" @@ -44,12 +44,11 @@ ) const errorCodeNoKey = "organizations.EncryptionKeyDoesNotExist" -var RegexpEncrypted = regexp.MustCompile(RegEncrypted) +var RegexpEncrypted = regexp.MustCompile(regEncrypted) func CreateCipherManager(client *http.Client, serverUrlBase string) *KmsCipherManager { return &KmsCipherManager{ serverUrlBase: serverUrlBase, - key: make(map[string][]byte), aes: make(map[string]*cipher.AesCipher), mutex: &sync.RWMutex{}, client: client, @@ -60,8 +59,6 @@ type KmsCipherManager struct { serverUrlBase string - // org-level key map {organization: key} - key map[string][]byte // org-level AesCipher map {organization: AesCipher} aes map[string]*cipher.AesCipher mutex *sync.RWMutex @@ -91,7 +88,6 @@ return case <-ticker.C: if err := c.retrieveKey(org); err != nil { - log.Error(err) } else { return @@ -103,14 +99,14 @@ func (c *KmsCipherManager) retrieveKey(org string) error { var key []byte req, err := http.NewRequest(http.MethodGet, c.serverUrlBase+retrieveEncryptKeyPath, nil) + if err != nil { + return fmt.Errorf("failed to create retrieving key request for org=%s : %v", org, err) + } pars := req.URL.Query() pars[parameterOrganization] = []string{org} req.URL.RawQuery = pars.Encode() req.Header.Set("Authorization", "Bearer "+services.Config().GetString(configBearerToken)) log.Debugf("Retrieving key: %s", req.URL.String()) - if err != nil { - return fmt.Errorf("failed to create retrieving key request for org=%s : %v", org, err) - } res, err := c.client.Do(req) if err != nil { return fmt.Errorf("failed to retrieve key for org=%s : %v", org, err) @@ -131,7 +127,6 @@ } if res.StatusCode != http.StatusOK { - err = fmt.Errorf("failed to retrieve key for org [%v] with status: %v", org, res.Status) return fmt.Errorf("failed to retrieve key for org [%v] with status: %v", org, res.Status) } @@ -151,7 +146,6 @@ return fmt.Errorf("CreateAesCipher error for org [%v] when CreateAesCipher: %v", org, err) } c.mutex.Lock() - c.key[org] = key c.aes[org] = a c.mutex.Unlock() return nil @@ -228,48 +222,47 @@ return } -// TODO: make sure this regex has no false positive for all possible inputs func IsEncrypted(input string) (encrypted bool) { return RegexpEncrypted.Match([]byte(input)) } func GetCiphertext(input string) (ciphertext string, mode cipher.Mode, padding cipher.Padding, err error) { - l := strings.SplitN(input, "}", 2) - if len(l) != 2 { + list := strings.SplitN(input, "}", 2) + if len(list) != 2 { err = fmt.Errorf("invalid input for GetCiphertext: %v", input) return } - ciphertext = l[1] - l = strings.Split(strings.TrimLeft(l[0], "{"), "/") - if len(l) != 3 { + ciphertext = list[1] + list = strings.Split(strings.TrimLeft(list[0], "{"), "/") + if len(list) != 3 { err = fmt.Errorf("invalid input for GetCiphertext: %v", input) return } // encryption algorithm - if l[0] != EncryptAes { - err = fmt.Errorf("unsupported algorithm for GetCiphertext: %v", l[0]) + if list[0] != EncryptAes { + err = fmt.Errorf("unsupported algorithm for GetCiphertext: %v", list[0]) return } // mode - mode = cipher.Mode(l[1]) + mode = cipher.Mode(list[1]) // padding - padding = cipher.Padding(l[2]) + padding = cipher.Padding(list[2]) return } -type ErrorRes struct { +type KeyErrorResponse struct { Code string Message string } -func parseErrorResponse(res *http.Response) (*ErrorRes, error) { - contentType := res.Header.Get("Content-Type") +func parseErrorResponse(res *http.Response) (*KeyErrorResponse, error) { + contentType := res.Header.Get(headerContentType) defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } - ret := &ErrorRes{} + ret := &KeyErrorResponse{} if contentType == typeJson { return ret, json.Unmarshal(body, ret) } else if contentType == typeXml {
diff --git a/common/cipher_test.go b/common/cipher_test.go index 4ff12b9..62162ad 100644 --- a/common/cipher_test.go +++ b/common/cipher_test.go
@@ -40,7 +40,6 @@ BeforeEach(func() { testCipherMan = CreateCipherManager(nil, "") // set key locally - testCipherMan.key[testOrg] = key var err error testCipherMan.aes[testOrg], err = cipher.CreateAesCipher(key) Expect(err).Should(Succeed()) @@ -66,6 +65,8 @@ // set key server server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() + Expect(r.URL.Path).Should(Equal(retrieveEncryptKeyPath)) + Expect(r.URL.Query().Get(parameterOrganization)).Should(Equal(testOrg)) Expect(w.Write([]byte(base64.StdEncoding.EncodeToString(key)))).Should(Equal(24)) })) time.Sleep(100 * time.Millisecond) @@ -84,5 +85,39 @@ It("Decryption", func() { Expect(testCipherMan.TryDecryptBase64(cipher64, testOrg)).Should(Equal(plaingtext)) }) + + It("Retrieve Key", func() { + testCipherMan.AddOrgs([]string{testOrg}) + for { + time.Sleep(100 * time.Millisecond) + testCipherMan.mutex.RLock() + aesCipher := testCipherMan.aes[testOrg] + testCipherMan.mutex.RUnlock() + if aesCipher != nil { + //close server to make sure key was retrieved by "AddOrgs" + server.Close() + Expect(testCipherMan.EncryptBase64(plaingtext, testOrg, cipher.ModeEcb, cipher.PaddingPKCS5)). + Should(Equal(cipher64)) + return + } + } + }, 2) + }) + + Context("IsEncrypted", func() { + It("IsEncrypted", func() { + testData := [][]interface{}{ + {"{AES/ECB/PKCS5Padding}foo", true}, + {"AES/ECB/PKCS5Padding}foo", false}, + {"{AES/ECB/PKCS5Paddingfoo", false}, + {"{AES/ECB/}foo", false}, + {"{AES/PKCS5Padding}foo", false}, + {"{AES//PKCS5Padding}foo", false}, + {"foo", false}, + } + for i := range testData { + Expect(IsEncrypted(testData[i][0].(string))).Should(Equal(testData[i][1])) + } + }) }) })
diff --git a/verifyApiKey/data.go b/verifyApiKey/data.go index c2169bb..a5d3708 100644 --- a/verifyApiKey/data.go +++ b/verifyApiKey/data.go
@@ -68,17 +68,11 @@ return errors.New("InvalidApiKey") } - email, err := dbc.CipherManager.TryDecryptBase64(dataWrapper.tempDeveloperDetails.Email, - dataWrapper.verifyApiKeyRequest.OrganizationName) - if err != nil { - return err - } secret, err := dbc.CipherManager.TryDecryptBase64(dataWrapper.verifyApiKeySuccessResponse.ClientId.ClientSecret, dataWrapper.verifyApiKeyRequest.OrganizationName) if err != nil { return err } - dataWrapper.tempDeveloperDetails.Email = email dataWrapper.verifyApiKeySuccessResponse.ClientId.ClientSecret = secret if dataWrapper.verifyApiKeySuccessResponse.App.CallbackUrl != "" {