lib/adapter/aggregator_adapter.go (111 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" "strconv" "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/httputils" /* copybara-comment: httputils */ "github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/srcutil" /* copybara-comment: srcutil */ pb "github.com/GoogleCloudPlatform/healthcare-federated-access-services/proto/dam/v1" /* copybara-comment: go_proto */ ) const ( aggregatorName = "aggregator" ) // AggregatorAdapter combines views from other adapters. type AggregatorAdapter struct { desc map[string]*pb.ServiceDescriptor sawAdapter ServiceAdapter } // NewAggregatorAdapter creates a AggregatorAdapter. func NewAggregatorAdapter(adapters *ServiceAdapters) (ServiceAdapter, error) { var msg pb.ServicesResponse path := adapterFilePath(aggregatorName) if err := srcutil.LoadProto(path, &msg); err != nil { return nil, fmt.Errorf("reading %q service descriptors from path %q: %v", aggregatorName, path, err) } sawAdapter, ok := adapters.ByAdapterName[SawAdapterName] if !ok { return nil, fmt.Errorf("SAW adapter %q not available at time of view aggregator adapter initialization", SawAdapterName) } return &AggregatorAdapter{ desc: msg.Services, sawAdapter: sawAdapter, }, nil } // Name returns the name identifier of the adapter as used in configurations. func (a *AggregatorAdapter) Name() string { return "token:aggregate:view" } // Platform returns the name identifier of the platform on which this adapter operates. func (a *AggregatorAdapter) Platform() string { return a.sawAdapter.Platform() } // Descriptors returns a map of Service descriptors. func (a *AggregatorAdapter) Descriptors() map[string]*pb.ServiceDescriptor { return a.desc } // IsAggregator returns true if this adapter requires TokenAction.Aggregates. func (a *AggregatorAdapter) IsAggregator() bool { return true } // CheckConfig validates that a new configuration is compatible with this adapter. func (a *AggregatorAdapter) CheckConfig(templateName string, template *pb.ServiceTemplate, resName, viewName string, view *pb.View, cfg *pb.DamConfig, adapters *ServiceAdapters) (string, error) { if view == nil { return "", nil } if len(view.Items) == 0 { return httputils.StatusPath("resources", resName, "views", viewName, "items"), fmt.Errorf("view %q has no items defined", viewName) } adapterName := "" adapterST := "" for iIdx, item := range view.Items { vars, path, err := GetItemVariables(adapters, template.ServiceName, item) if err != nil { return httputils.StatusPath("resources", resName, "views", viewName, "items", strconv.Itoa(iIdx), path), err } refResName := vars["resource"] refRes, ok := cfg.Resources[refResName] if !ok { return httputils.StatusPath("resources", resName, "views", viewName, "items", strconv.Itoa(iIdx), "vars", "resource"), fmt.Errorf("resource %q not found", refResName) } refViewName := vars["view"] refView, ok := refRes.Views[refViewName] if !ok { return httputils.StatusPath("resources", resName, "views", viewName, "items", strconv.Itoa(iIdx), "vars", "view"), fmt.Errorf("view %q not found", refViewName) } refSt, ok := cfg.ServiceTemplates[refView.ServiceTemplate] if !ok { return httputils.StatusPath("resources", refResName, "views", refViewName, "serviceTemplate"), fmt.Errorf("view service template %q not found", refView.ServiceTemplate) } adapt, ok := adapters.ByServiceName[refSt.ServiceName] if !ok { return httputils.StatusPath("resources", refResName, "views", refViewName, "serviceTemplate"), fmt.Errorf("view service name %q not defined", refSt.ServiceName) } if len(adapterName) == 0 { adapterName = adapt.Name() adapterST = refView.ServiceTemplate } else if adapterName != adapt.Name() { return httputils.StatusPath("resources", resName, "views", viewName, "items", strconv.Itoa(iIdx), "vars", "view"), fmt.Errorf("view service template %q service adapter %q does not match other items using service adapter %q", refView.ServiceTemplate, refSt.ServiceName, adapterName) } desc, ok := adapters.Descriptors[refSt.ServiceName] if !ok || desc == nil || desc.Properties == nil { return httputils.StatusPath("serviceTemplates", adapterST, "serviceName", refSt.ServiceName), fmt.Errorf("lookup of descriptor properties for %q on adapter %q failed: desc:\n%+v", refSt.ServiceName, adapterName, desc) } if !desc.Properties.CanBeAggregated { return httputils.StatusPath("serviceTemplates", adapterST, "targetService", "properties", "canBeAggregated"), fmt.Errorf("aggregation on service adapter %q not supported", adapterName) } } if adapterName == "" { return httputils.StatusPath("resources", resName, "views", viewName, "items"), fmt.Errorf("included views offer no items to aggregate") } return "", nil } // MintToken has the adapter mint a token. func (a *AggregatorAdapter) MintToken(ctx context.Context, input *Action) (*MintTokenResult, error) { var result *MintTokenResult for _, entry := range input.Aggregates { action := *input action.ServiceTemplate = input.Config.ServiceTemplates[entry.View.ServiceTemplate] vsRole, err := ResolveServiceRole(input.GrantRole, entry.View, entry.Res, input.Config) if err != nil { return nil, err } action.ServiceRole = vsRole result, err = a.sawAdapter.MintToken(ctx, &action) if err != nil { return nil, fmt.Errorf("aggregator minting token on item %d resource view: %v", entry.Index, err) } } return result, nil }