Create a mock server for adapter testing. Clean up licenses and other things.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e5c2576 --- /dev/null +++ b/CONTRIBUTING.md
@@ -0,0 +1,22 @@ +# How to contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to any Google project must be accompanied by a Contributor License +Agreement. This is necessary because you own the copyright to your changes, even +after your contribution becomes part of this project. So this agreement simply +gives us permission to use and redistribute your contributions as part of the +project. Head over to <https://cla.developers.google.com/> to see your current +agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## How to Contribute + +We happily accept pull requests, bugs, and issues here in GitHub. +
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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.
diff --git a/Makefile b/Makefile index c989c2d..364ac8c 100644 --- a/Makefile +++ b/Makefile
@@ -6,5 +6,7 @@ checkfmt: (cd adapter; ../tools/checkfmt.sh) + (cd common; ../tools/checkfmt.sh) + (cd mock; ../tools/checkfmt.sh) presubmit: checkfmt test
diff --git a/README.md b/README.md index 134b6bd..86cfed4 100644 --- a/README.md +++ b/README.md
@@ -1,3 +1,7 @@ # Istio Apigee Adapter This workspace holds an apigee adapter for Istio. + +## Installation + +In order to use this, right now, you need a build of Istio \ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE index c29e9ff..1ca9c35 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -45,6 +45,13 @@ importpath = "github.com/golang/protobuf", ) +new_git_repository( + name = "com_github_googleapis_googleapis", + build_file = "bazel/BUILD.googleapis", + commit = "13ac2436c5e3d568bd0e938f6ed58b77a48aba15", + remote = "https://github.com/googleapis/googleapis.git", +) + new_go_repository( name = "com_github_hashicorp_go_multierror", commit = "ed905158d87462226a13fe39ddf685ea65f1c11f", @@ -57,9 +64,14 @@ importpath = "github.com/hashicorp/errwrap", ) -new_git_repository( - name = "com_github_googleapis_googleapis", - build_file = "bazel/BUILD.googleapis", - commit = "13ac2436c5e3d568bd0e938f6ed58b77a48aba15", - remote = "https://github.com/googleapis/googleapis.git", -) \ No newline at end of file +new_go_repository( + name = "com_github_julienschmidt_httprouter", + commit = "975b5c4c7c21c0e3d2764200bf2aa8e34657ae6e", + importpath = "github.com/julienschmidt/httprouter" +) + +new_go_repository( + name = "com_github_SermoDigital_jose", + tag = "1.1", + importpath = "github.com/SermoDigital/jose" +)
diff --git a/adapter/apigee.go b/adapter/apigee.go index 985e1bf..8bf7f7d 100644 --- a/adapter/apigee.go +++ b/adapter/apigee.go
@@ -1,5 +1,5 @@ /* -Copyright 2017 The apid Authors +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.
diff --git a/adapter/apigeeKeyChecker.go b/adapter/apigeeKeyChecker.go index 69b6476..52d5010 100644 --- a/adapter/apigeeKeyChecker.go +++ b/adapter/apigeeKeyChecker.go
@@ -1,5 +1,5 @@ /* -Copyright 2017 The apid Authors +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.
diff --git a/adapter/apigeeKeyChecker_test.go b/adapter/apigeeKeyChecker_test.go index 61a11e0..6f4862d 100644 --- a/adapter/apigeeKeyChecker_test.go +++ b/adapter/apigeeKeyChecker_test.go
@@ -1,5 +1,5 @@ /* -Copyright 2017 The apid Authors +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.
diff --git a/adapter/apigeeReport.go b/adapter/apigeeReport.go index 861dda7..d9f9325 100644 --- a/adapter/apigeeReport.go +++ b/adapter/apigeeReport.go
@@ -1,5 +1,5 @@ /* -Copyright 2017 The apid Authors +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.
diff --git a/common/BUILD b/common/BUILD new file mode 100644 index 0000000..5c09cc7 --- /dev/null +++ b/common/BUILD
@@ -0,0 +1,12 @@ +package(default_visibility = ["//visibility:public"]) + +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "types.go", + ], + deps = [ + ], +)
diff --git a/common/types.go b/common/types.go new file mode 100644 index 0000000..ba362ba --- /dev/null +++ b/common/types.go
@@ -0,0 +1,62 @@ +/* +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 common + +type Attribute struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type VerifyAPIKeyRequest struct { + Key string `json:"apiKey"` +} + +type VerifyAPIKeyResponse struct { + Token string `json:"token"` +} + +type APIProduct struct { + APIResources []string `json:"apiResources"` + ApprovalType string `json:"approvalType"` + Attributes []Attribute `json:"attributes"` + CreatedAt int64 `json:"createdAt"` + CreatedBy string `json:"createdBy"` + Description string `json:"description"` + DisplayName string `json:"displayName"` + Environments []string `json:"environments"` + LastModifiedAt int64 `json:"lastModifiedAt"` + LastModifiedBy string `json:"lastModifiedBy"` + Name string `json:"name"` + Proxies []string `json:"proxies"` + Quota string `json:"quota"` + QuotaInterval string `json:"quotaInterval"` + QuotaTimeUnit string `json:"quotaTimeUnit"` + Scopes []string `json:"scopes"` +} + +type APIFaultMessage struct { + Fault APIFault `json:"fault"` +} + +type APIFault struct { + FaultString string `json:"faultstring"` + Detail APIFaultDetail `json:"detail"` +} + +type APIFaultDetail struct { + ErrorCode string `json:"errorcode"` +}
diff --git a/mock/BUILD b/mock/BUILD new file mode 100644 index 0000000..4bf127c --- /dev/null +++ b/mock/BUILD
@@ -0,0 +1,27 @@ +package(default_visibility = ["//visibility:public"]) + +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "keys.go", + "mockserver.go", + ], + deps = [ + "//common:go_default_library", + "@com_github_julienschmidt_httprouter//:go_default_library", + "@com_github_SermoDigital_jose//crypto:go_default_library", + "@com_github_SermoDigital_jose//jws:go_default_library", + "@com_github_SermoDigital_jose//jwt:go_default_library", + ], +) + +go_test( + name = "small_tests", + size = "small", + srcs = [ + "mockserver_test.go", + ], + library = ":go_default_library", +)
diff --git a/mock/keys.go b/mock/keys.go new file mode 100644 index 0000000..72e9201 --- /dev/null +++ b/mock/keys.go
@@ -0,0 +1,73 @@ +/* +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 mock + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" +) + +var mockKey *rsa.PrivateKey +var mockKeyPEM, mockCertPEM []byte + +func makeKeys() { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err.Error()) + } + mockKey = key + + mockKeyPEM = pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + + templ := makeCertTemplate() + + certBytes, err := x509.CreateCertificate(rand.Reader, templ, templ, key.Public(), key) + if err != nil { + panic(err.Error()) + } + + mockCertPEM = pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) +} + +func makeCertTemplate() *x509.Certificate { + now := time.Now() + + return &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Country: []string{"US"}, + Organization: []string{"Google"}, + OrganizationalUnit: []string{"Cloud"}, + Locality: []string{"Mountain View"}, + Province: []string{"CA"}, + CommonName: "mockserver", + }, + NotBefore: now, + NotAfter: now.Add(time.Hour), + } +}
diff --git a/mock/mockserver.go b/mock/mockserver.go new file mode 100644 index 0000000..8ab7737 --- /dev/null +++ b/mock/mockserver.go
@@ -0,0 +1,187 @@ +/* +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 mock + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "sync" + "time" + + "github.com/SermoDigital/jose/crypto" + "github.com/SermoDigital/jose/jws" + "github.com/SermoDigital/jose/jwt" + "github.com/apid/istioApigeeAdapter/common" + "github.com/julienschmidt/httprouter" +) + +var mockInit = &sync.Once{} + +const ( + ValidAPIKey1 = "12345" +) + +type MockServer struct { + listener net.Listener +} + +func StartMockServer(port int) (*MockServer, error) { + mockInit.Do(makeKeys) + + l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + return nil, err + } + + ms := &MockServer{ + listener: l, + } + + router := httprouter.New() + router.GET("/publicKey", ms.getPublicKey) + router.GET("/products", ms.getProducts) + router.POST("/verifyApiKey", ms.getAPIKey) + + go func() { + http.Serve(l, router) + }() + + return ms, nil +} + +func (m *MockServer) Address() string { + return m.listener.Addr().String() +} + +func (m *MockServer) Stop() { + m.listener.Close() +} + +func (m *MockServer) getPublicKey(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + w.Header().Set("content-type", "text/plain") + w.Write(mockCertPEM) +} + +func (m *MockServer) getProducts(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + now := time.Now() + p := []common.APIProduct{ + { + Name: "First", + DisplayName: "First Product", + Description: "First test product", + CreatedAt: now.UnixNano(), + CreatedBy: "foo@bar.com", + LastModifiedAt: now.UnixNano(), + LastModifiedBy: "foo@bar.com", + + APIResources: []string{"/**"}, + ApprovalType: "auto", + Attributes: []common.Attribute{ + { + Name: "access", + Value: "public", + }, + }, + Environments: []string{"test", "prod"}, + Proxies: []string{"ProxyOne"}, + Quota: "10", + QuotaInterval: "1", + QuotaTimeUnit: "minute", + }, + } + + w.Header().Set("content-type", "application/json") + + enc := json.NewEncoder(w) + enc.Encode(p) +} + +func (m *MockServer) getAPIKey(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + if r.Header.Get("content-type") != "application/json" { + // That's what the existing service does + sendFault(w, 404, "Failed to resolve API Key variable apikey", + "steps.oauth.v2.FailedToResolveAPIKey") + return + } + + var request common.VerifyAPIKeyRequest + defer r.Body.Close() + dec := json.NewDecoder(r.Body) + err := dec.Decode(&request) + if err != nil { + // Again, what existing service does + sendFault(w, 500, "Failed to execute the ExtractVariables: Extract-API-Key", + "steps.extractvariables.ExecutionFailed") + return + } + + if request.Key != ValidAPIKey1 { + sendFault(w, 404, + fmt.Sprintf("API key %s is not valid", request.Key), + "steps.oauth.v2.FailedToResolveAPIKey") + return + } + + jwt := makeJWT1() + bod, err := jwt.Serialize(mockKey) + if err != nil { + sendFault(w, 500, err.Error(), "JWT") + return + } + + tok := common.VerifyAPIKeyResponse{ + Token: string(bod), + } + w.Header().Set("content-type", "application/json") + enc := json.NewEncoder(w) + enc.Encode(&tok) +} + +func sendFault(w http.ResponseWriter, errorCode int, fault, code string) { + w.Header().Set("content-type", "application/json") + w.WriteHeader(errorCode) + f := &common.APIFaultMessage{ + Fault: common.APIFault{ + FaultString: fault, + Detail: common.APIFaultDetail{ + ErrorCode: code, + }, + }, + } + + enc := json.NewEncoder(w) + enc.Encode(f) +} + +func makeJWT1() jwt.JWT { + now := time.Now() + c := jws.Claims{} + c.SetIssuedAt(now) + c.SetNotBefore(now) + c.Set("audience", "microgateway") + c.Set("jti", "52137037-6ce1-426e-a255-e471f94854e5") + c.Set("iss", "https://mock.foo.net/verifyApiKey") + c.Set("client_id", ValidAPIKey1) + c.Set("application_name", "TestApp1") + c.Set("api_product_list", []string{ + "TestProduct1", + }) + + return jws.NewJWT(c, crypto.SigningMethodRS256) +}
diff --git a/mock/mockserver_test.go b/mock/mockserver_test.go new file mode 100644 index 0000000..862383a --- /dev/null +++ b/mock/mockserver_test.go
@@ -0,0 +1,201 @@ +/* +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 mock + +import ( + "bytes" + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "io/ioutil" + "net/http" + "os" + "testing" + + "github.com/SermoDigital/jose/jws" + "github.com/apid/istioApigeeAdapter/common" +) + +var testMockServer *MockServer + +func TestMain(m *testing.M) { + var err error + testMockServer, err = StartMockServer(0) + if err != nil { + panic(err.Error()) + } + + result := m.Run() + testMockServer.Stop() + os.Exit(result) +} + +func TestServerSanity(t *testing.T) { + if mockKeyPEM == nil { + t.Fatal("Expected mock key to be generated") + } + if mockCertPEM == nil { + t.Fatal("Expected mock cert to be generated") + } +} + +func TestPublicKey(t *testing.T) { + resp, err := http.Get(fmt.Sprintf("http://%s/publicKey", testMockServer.Address())) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + t.Fatalf("Got HTTP status code %d", resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("Error reading body: %s", err) + } + pb, _ := pem.Decode(body) + if pb == nil { + t.Fatalf("Failed decoding public key body \"%s\"", string(body)) + } + if pb.Type != "CERTIFICATE" { + t.Fatalf("Got back invalid PEM type %s", pb.Type) + } + + cert, err := x509.ParseCertificate(pb.Bytes) + if err != nil { + t.Fatalf("Error decoding certificate: %s", err) + } + if cert.Subject.CommonName != "mockserver" { + t.Fatalf("Common name does not match \"mockserver\": \"%s\"", cert.Subject.CommonName) + } +} + +func TestProducts(t *testing.T) { + resp, err := http.Get(fmt.Sprintf("http://%s/products", testMockServer.Address())) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + t.Fatalf("Got HTTP status code %d", resp.StatusCode) + } + if resp.Header.Get("content-type") != "application/json" { + t.Fatalf("Invalid content typs %s", resp.Header.Get("content-type")) + } + + dec := json.NewDecoder(resp.Body) + var products []common.APIProduct + err = dec.Decode(&products) + if err != nil { + t.Fatalf("Error decoding response json: %s", err) + } +} + +func TestAPIKeyWrongFormat(t *testing.T) { + req := common.VerifyAPIKeyRequest{} + requestBod, _ := json.Marshal(&req) + resp, err := http.DefaultClient.Post( + fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), + "text/plain", + bytes.NewBuffer(requestBod)) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 404 { + t.Fatalf("Got HTTP status code %d -- expected 404", resp.StatusCode) + } +} + +func TestAPIKeyNotJSON(t *testing.T) { + resp, err := http.DefaultClient.Post( + fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), + "application/json", + bytes.NewBuffer([]byte("Hello!"))) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 500 { + t.Fatalf("Got HTTP status code %d -- expected 500", resp.StatusCode) + } +} + +func TestAPIKeyNoKey(t *testing.T) { + req := common.VerifyAPIKeyRequest{} + requestBod, _ := json.Marshal(&req) + resp, err := http.DefaultClient.Post( + fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), + "application/json", + bytes.NewBuffer(requestBod)) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 404 { + t.Fatalf("Got HTTP status code %d -- expected 404", resp.StatusCode) + } +} + +func TestAPIKeyBadKey(t *testing.T) { + req := common.VerifyAPIKeyRequest{ + Key: "foobar", + } + requestBod, _ := json.Marshal(&req) + resp, err := http.DefaultClient.Post( + fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), + "application/json", + bytes.NewBuffer(requestBod)) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 404 { + t.Fatalf("Got HTTP status code %d -- expected 404", resp.StatusCode) + } +} + +func TestAPIKeySuccess(t *testing.T) { + req := common.VerifyAPIKeyRequest{ + Key: ValidAPIKey1, + } + requestBod, _ := json.Marshal(&req) + resp, err := http.DefaultClient.Post( + fmt.Sprintf("http://%s/verifyApiKey", testMockServer.Address()), + "application/json", + bytes.NewBuffer(requestBod)) + if err != nil { + t.Fatalf("Network error: %s", err) + } + defer resp.Body.Close() + if resp.StatusCode != 200 { + t.Fatalf("Got HTTP status code %d -- expected 200", resp.StatusCode) + } + + var respBody common.VerifyAPIKeyResponse + dec := json.NewDecoder(resp.Body) + err = dec.Decode(&respBody) + if err != nil { + t.Fatalf("Error reading JSON response: %s", err) + } + + _, err = jws.ParseJWT([]byte(respBody.Token)) + if err != nil { + t.Fatalf("Error parsing JWT: %s", err) + } +}
diff --git a/tools/checkfmt.sh b/tools/checkfmt.sh index 68db939..c668975 100755 --- a/tools/checkfmt.sh +++ b/tools/checkfmt.sh
@@ -24,7 +24,7 @@ shopt -s nullglob for f in *.go *.[ch] do - lc=`egrep -c 'Copyright [0-9]+ The apid Authors|Apache License' $f` + lc=`egrep -c 'Copyright [0-9]+ Google Inc.|Apache License' $f` if [ $lc -lt 2 ] then echo "** $f is missing a license header"