lib/adapter/saw_adapter.go (115 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"
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/clouds" /* copybara-comment: clouds */
"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/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 (
// SawAdapterName is the name identifier exposed in config files.
SawAdapterName = "saw"
sawPlatform = "gcp"
// SawMaxUserIDLength is the service account desc max length.
SawMaxUserIDLength = 100
sawBucketVar = "bucket"
sawPaysBucketVar = "requester-pays-bucket"
)
// SawAdapter is a Service Account Warehouse (SAW) adapter.
type SawAdapter struct {
desc map[string]*pb.ServiceDescriptor
warehouse clouds.ResourceTokenCreator
}
// NewSawAdapter creates a Service Account Warehouse (SAW) adapter.
func NewSawAdapter(warehouse clouds.ResourceTokenCreator) (ServiceAdapter, error) {
var msg pb.ServicesResponse
path := adapterFilePath(SawAdapterName)
if err := srcutil.LoadProto(path, &msg); err != nil {
return nil, fmt.Errorf("reading %q service descriptors from path %q: %v", aggregatorName, path, err)
}
return &SawAdapter{
desc: msg.Services,
warehouse: warehouse,
}, nil
}
// Name returns the name identifier of the adapter as used in configurations.
func (a *SawAdapter) Name() string {
return SawAdapterName
}
// Platform returns the name identifier of the platform on which this adapter operates.
func (a *SawAdapter) Platform() string {
return sawPlatform
}
// Descriptors returns a map of ServiceDescriptor descriptor.
func (a *SawAdapter) Descriptors() map[string]*pb.ServiceDescriptor {
return a.desc
}
// IsAggregator returns true if this adapter requires TokenAction.Aggregates.
func (a *SawAdapter) IsAggregator() bool {
return false
}
// CheckConfig validates that a new configuration is compatible with this adapter.
func (a *SawAdapter) CheckConfig(templateName string, template *pb.ServiceTemplate, resName, viewName string, view *pb.View, cfg *pb.DamConfig, adapters *ServiceAdapters) (string, error) {
if cfg.Options == nil || len(cfg.Options.GcpServiceAccountProject) == 0 {
return httputils.StatusPath("serviceTemplates", templateName, "targetAdapter"), fmt.Errorf("service adapter uses service accounts but options.gcpServiceAccountProject is not defined")
}
return "", nil
}
// MintToken has the adapter mint a token.
func (a *SawAdapter) MintToken(ctx context.Context, input *Action) (*MintTokenResult, error) {
if a.warehouse == nil {
return nil, fmt.Errorf("SAW minting token: DAM service account warehouse not configured")
}
userID := ga4gh.TokenUserID(input.Identity, SawMaxUserIDLength)
maxKeyTTL := timeutil.ParseDurationWithDefault(input.Config.Options.GcpManagedKeysMaxRequestedTtl, input.MaxTTL)
params, err := resourceTokenCreationParams(input.GrantRole, input.ServiceTemplate, input.ServiceRole, input.View, input.Config, input.TokenFormat)
if err != nil {
return nil, fmt.Errorf("SAW minting token: %v", err)
}
result, err := a.warehouse.MintTokenWithTTL(ctx, userID, input.TTL, maxKeyTTL, int(input.Config.Options.GcpManagedKeysPerAccount), params)
if err != nil {
return nil, fmt.Errorf("SAW minting token: %v", err)
}
res := &MintTokenResult{
Credentials: map[string]string{
"account": result.Account,
},
TokenFormat: result.Format,
}
if len(result.Token) > 0 {
res.Credentials["access_token"] = result.Token
}
if len(result.AccountKey) > 0 {
res.Credentials["service_account_key"] = result.AccountKey
}
return res, nil
}
func resourceTokenCreationParams(role string, template *pb.ServiceTemplate, sRole *pb.ServiceRole, view *pb.View, cfg *pb.DamConfig, format string) (*clouds.ResourceTokenCreationParams, error) {
roles := []string{}
scopes := []string{"https://www.googleapis.com/auth/cloud-platform"}
if sRole != nil {
if arg, ok := sRole.ServiceArgs["roles"]; ok {
roles = arg.Values
}
if arg, ok := sRole.ServiceArgs["scopes"]; ok {
scopes = arg.Values
}
}
items := make([]map[string]string, len(view.Items))
for index, item := range view.Items {
items[index] = scrubVars(item.Args)
}
billingProject := cfg.Options.GcpIamBillingProject
if len(billingProject) == 0 {
billingProject = cfg.Options.GcpServiceAccountProject
}
return &clouds.ResourceTokenCreationParams{
AccountProject: cfg.Options.GcpServiceAccountProject,
Items: items,
Roles: roles,
Scopes: scopes,
TokenFormat: format,
BillingProject: billingProject,
}, nil
}
func scrubVars(vars map[string]string) map[string]string {
for k, v := range vars {
if len(v) == 0 {
delete(vars, k)
}
}
return vars
}