| /* |
| 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 adapter |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| "github.com/apid/istioApigeeAdapter/adapter/config" |
| "istio.io/mixer/pkg/adapter" |
| "github.com/apid/istioApigeeAdapter/common" |
| ) |
| |
| const ( |
| keyAttrsName = "apigeeKeyAttributes" |
| keyAttrsDesc = "Set attributes based on an Apigee API key" |
| |
| keyParam = "apiKey" |
| jwtParam = "jwtToken" |
| pathParam = "requestPath" |
| apiKeySuccessParam = "apiKeySuccess" |
| apiKeySuccessStringParam = "apiKeySuccessString" |
| jwtSuccessParam = "jwtSuccess" |
| jwtSuccessStringParam = "jwtSuccessString" |
| clientIDParam = "clientID" |
| appNameParam = "applicationName" |
| productNameParam = "apiProduct" |
| ) |
| |
| var keyAttrsConf = &config.VerifyKeyParams{} |
| |
| type keyAttrsBuilder struct { |
| adapter.DefaultBuilder |
| } |
| |
| type keyAttrsGenerator struct { |
| env adapter.Env |
| applications *applicationManager |
| products *productManager |
| jwtVerifier *jwtVerifier |
| } |
| |
| func newKeyAttrsBuilder() adapter.AttributesGeneratorBuilder { |
| return keyAttrsBuilder{ |
| adapter.NewDefaultBuilder(keyAttrsName, keyAttrsDesc, keyAttrsConf), |
| } |
| } |
| |
| func (b keyAttrsBuilder) ValidateConfig(c adapter.Config) *adapter.ConfigErrors { |
| return validateKeyParams(c) |
| } |
| |
| func (b keyAttrsBuilder) BuildAttributesGenerator(env adapter.Env, c adapter.Config) (adapter.AttributesGenerator, error) { |
| cfg := c.(*config.VerifyKeyParams) |
| verifyPath, productsPath := getPaths(cfg) |
| publicKeyUrl := getPulicKeyUrl(cfg) |
| |
| g := &keyAttrsGenerator{ |
| env: env, |
| applications: newApplicationManager(env, defaultAppLifetime, verifyPath), |
| products: newProductManager(productsPath, defaultProductsFetch), |
| jwtVerifier: newVerifier(defaultKeyFetch, publicKeyUrl), |
| } |
| env.Logger().Infof("Created Apigee attributes generator to invoke \"%s\"", verifyPath) |
| env.Logger().Infof("Checking API products using \"%s\"", productsPath) |
| |
| return g, nil |
| } |
| |
| func (g *keyAttrsGenerator) Generate(in map[string]interface{}) (map[string]interface{}, error) { |
| |
| out := make(map[string]interface{}) |
| out[apiKeySuccessParam] = false |
| out[apiKeySuccessStringParam] = "false" |
| out[jwtSuccessParam] = false |
| out[jwtSuccessStringParam] = "false" |
| |
| apiKey := getString(in, keyParam) |
| jwt_token := getString(in, jwtParam) |
| |
| if apiKey == "" && jwt_token == "" { |
| return out, nil |
| } |
| |
| path := getString(in, pathParam) |
| if path == "" { |
| return out, nil |
| } |
| |
| if jwt_token != "" { |
| // TODO: throw appropriate error to user eg. token expired, token invalid |
| |
| if strings.HasPrefix(jwt_token, "Bearer") { |
| token := strings.Split(jwt_token, " ")[1] |
| jwt_token = token |
| } else { |
| g.env.Logger().Errorf("Cannot verify jwt token, Bearer missing") |
| } |
| |
| claims, verificationErr := g.jwtVerifier.Verify(jwt_token) |
| // considers token as valid if exp, iat, iss is not present |
| |
| if verificationErr != nil { |
| g.env.Logger().Errorf("Cannot verify jwt token : %s", verificationErr) |
| } else { |
| validationErr := claims.Valid() |
| |
| if validationErr != nil { |
| g.env.Logger().Errorf("Validation error, jwt token : %s", validationErr) |
| } else { |
| // TODO: check scopes |
| |
| key := claims.(*apigeeClaims).ClientId |
| success, app, products, err := g.checkKey(key) |
| |
| g.env.Logger().Errorf("Key check failed : %s", err) |
| |
| out[jwtSuccessParam] = success |
| out[jwtSuccessStringParam] = strconv.FormatBool(success) |
| |
| if app.valid { |
| out[clientIDParam] = app.clientID |
| out[appNameParam] = app.name |
| } |
| |
| if success { |
| out[productNameParam] = products[0].Name |
| } |
| } |
| } |
| } |
| |
| if apiKey != "" { |
| success, app, products, err := g.checkKey(apiKey) |
| |
| g.env.Logger().Errorf("key check failed : %s", err) |
| |
| if len(products) == 0 { |
| success = false |
| } |
| |
| // TODO match API products by path |
| |
| out[apiKeySuccessParam] = success |
| out[apiKeySuccessStringParam] = strconv.FormatBool(success) |
| |
| if app.valid { |
| out[clientIDParam] = app.clientID |
| out[appNameParam] = app.name |
| } |
| |
| if success { |
| out[productNameParam] = products[0].Name |
| } |
| } |
| |
| return out, nil |
| } |
| |
| func (g *keyAttrsGenerator) Close() error { |
| return nil |
| } |
| |
| func (g *keyAttrsGenerator) checkKey(key string) (bool, *application, []common.APIProduct, error) { |
| // Look up API key from cache, making HTTP request if necessary |
| app, err := g.applications.get(key) |
| if err != nil { |
| g.env.Logger().Errorf("Error verifying API key: %s", err) |
| return false, nil, nil, err |
| } |
| |
| success := app.valid |
| |
| // Look up API products from cache, making HTTP request if necessary |
| products, err := g.products.getProducts(app.apiProducts) |
| if err != nil { |
| return false, nil, nil, fmt.Errorf("Cannot fetch API product list: %s", err) |
| } |
| |
| if len(products) == 0 { |
| success = false |
| } |
| |
| // TODO match API products by path |
| |
| return success, app, products, err |
| } |
| |
| func getPulicKeyUrl(cfg *config.VerifyKeyParams) (string) { |
| return fmt.Sprintf(defaultPublicKeyURL, cfg.Organization, cfg.Environment) |
| } |