lib/adapter/gatekeeper_adapter.go (101 lines of code) (raw):

// Copyright 2019 Google LLC // // 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 ( "context" "fmt" "time" "github.com/pborman/uuid" /* copybara-comment */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/ga4gh" /* copybara-comment: ga4gh */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/httputils" /* copybara-comment: httputils */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/kms" /* copybara-comment: kms */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/srcutil" /* copybara-comment: srcutil */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/timeutil" /* copybara-comment: timeutil */ pb "github.com/GoogleCloudPlatform/healthcare-federated-access-services/proto/dam/v1" /* copybara-comment: go_proto */ ) const ( gatekeeperName = "gatekeeper" gatekeeperAdapterName = "token:jwt:gatekeeper" gatekeeperPlatform = "dam" secretsName = "secrets" mainID = "main" keyID = "kid" ) // GatekeeperToken is the token format that is minted here. type GatekeeperToken struct { *ga4gh.StdClaims Scopes []string `json:"scopes,omitempty"` } // GatekeeperAdapter generates downstream access tokens. type GatekeeperAdapter struct { desc map[string]*pb.ServiceDescriptor signer kms.Signer } // NewGatekeeperAdapter creates a GatekeeperAdapter. func NewGatekeeperAdapter(signer kms.Signer) (ServiceAdapter, error) { var msg pb.ServicesResponse path := adapterFilePath(gatekeeperName) if err := srcutil.LoadProto(path, &msg); err != nil { return nil, fmt.Errorf("reading %q service descriptors from path %q: %v", aggregatorName, path, err) } return &GatekeeperAdapter{ desc: msg.Services, signer: signer, }, nil } // Name returns the name identifier of the adapter as used in configurations. func (a *GatekeeperAdapter) Name() string { return gatekeeperAdapterName } // Platform returns the name identifier of the platform on which this adapter operates. func (a *GatekeeperAdapter) Platform() string { return gatekeeperPlatform } // Descriptors returns a map of ServiceAdapter descriptors. func (a *GatekeeperAdapter) Descriptors() map[string]*pb.ServiceDescriptor { return a.desc } // IsAggregator returns true if this adapter requires TokenAction.Aggregates. func (a *GatekeeperAdapter) IsAggregator() bool { return false } // CheckConfig validates that a new configuration is compatible with this adapter. func (a *GatekeeperAdapter) CheckConfig(templateName string, template *pb.ServiceTemplate, resName, viewName string, view *pb.View, cfg *pb.DamConfig, adapters *ServiceAdapters) (string, error) { if view != nil && len(view.Items) > 1 { return httputils.StatusPath("resources", resName, "views", viewName, "items"), fmt.Errorf("view %q has more than one target item defined", viewName) } return "", nil } // MintToken has the adapter mint a token. func (a *GatekeeperAdapter) MintToken(ctx context.Context, input *Action) (*MintTokenResult, error) { if input.MaxTTL > 0 && input.TTL > input.MaxTTL { return nil, fmt.Errorf("minting gatekeeper token: TTL of %q exceeds max TTL of %q", timeutil.TTLString(input.TTL), timeutil.TTLString(input.MaxTTL)) } now := time.Now() var auds []string // TODO: support standard audience formats instead of space-delimited. for _, item := range input.View.Items { if item.Args == nil { continue } if a, ok := item.Args["aud"]; ok { auds = append(auds, a) } } scopes := []string{} arg, ok := input.ServiceRole.ServiceArgs["scopes"] if ok { scopes = arg.Values } claims := &GatekeeperToken{ StdClaims: &ga4gh.StdClaims{ Issuer: input.Issuer, Subject: input.Identity.Subject, Audience: auds, ExpiresAt: now.Add(input.TTL).Unix(), NotBefore: now.Add(-1 * time.Minute).Unix(), IssuedAt: now.Unix(), ID: uuid.New(), }, Scopes: scopes, } token, err := a.signer.SignJWT(ctx, claims, nil) if err != nil { return nil, fmt.Errorf("minting gatekeeper token: sign token failed: %v", err) } return &MintTokenResult{ Credentials: map[string]string{ "account": input.Identity.Subject, "access_token": token, }, TokenFormat: "base64", }, nil }