lib/dam/configadmin.go (1,006 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 dam
import (
"fmt"
"net/http"
"sort"
"strconv"
"google.golang.org/grpc/codes" /* copybara-comment */
"google.golang.org/grpc/status" /* copybara-comment */
"github.com/golang/protobuf/proto" /* copybara-comment */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/check" /* copybara-comment: check */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/ga4gh" /* copybara-comment: ga4gh */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/handlerfactory" /* copybara-comment: handlerfactory */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/httputils" /* copybara-comment: httputils */
"github.com/GoogleCloudPlatform/healthcare-federated-access-services/lib/storage" /* copybara-comment: storage */
cpb "github.com/GoogleCloudPlatform/healthcare-federated-access-services/proto/common/v1" /* copybara-comment: go_proto */
pb "github.com/GoogleCloudPlatform/healthcare-federated-access-services/proto/dam/v1" /* copybara-comment: go_proto */
)
func (s *Service) configFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "config",
PathPrefix: configPath,
HasNamedIdentifiers: false,
Service: func() handlerfactory.Service {
return NewConfigHandler(s)
},
}
}
type configHandler struct {
s *Service
input *pb.ConfigRequest
save *pb.DamConfig
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigHandler(s *Service) *configHandler {
return &configHandler{
s: s,
input: &pb.ConfigRequest{},
}
}
func (h *configHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.tx = tx
h.cfg = cfg
h.id = id
return status, err
}
func (h *configHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
// Trival name as there is only one config and it was fetched during Setup().
return true
}
func (h *configHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.DamConfig{}
}
if h.input.Modification == nil {
h.input.Modification = &pb.ConfigModification{}
}
if h.input.Item.Clients == nil {
h.input.Item.Clients = make(map[string]*cpb.Client)
}
if h.input.Item.Options == nil {
h.input.Item.Options = &pb.ConfigOptions{}
}
h.input.Item.Options = receiveConfigOptions(h.input.Item.Options, h.cfg)
normalizeConfig(h.input.Item)
return nil
}
func (h *configHandler) Get(r *http.Request, name string) (proto.Message, error) {
return makeConfig(h.cfg), nil
}
func (h *configHandler) Post(r *http.Request, name string) (proto.Message, error) {
return nil, fmt.Errorf("POST not allowed")
}
func (h *configHandler) Put(r *http.Request, name string) (proto.Message, error) {
if getRealm(r) != storage.DefaultRealm && !check.ClientsEqual(h.cfg.Clients, h.input.Item.Clients) {
return nil, status.Errorf(codes.PermissionDenied, "modify clients is only allowed on master realm")
}
if h.cfg.Version != h.input.Item.Version {
// TODO: consider upgrading older config versions automatically.
return nil, fmt.Errorf("PUT of config version %q mismatched with existing config version %q", h.input.Item.Version, h.cfg.Version)
}
h.save = receiveConfig(h.input.Item, h.cfg)
// Retain the revision number (it will be incremented upon saving).
h.save.Revision = h.cfg.Revision
return nil, nil
}
func (h *configHandler) Patch(r *http.Request, name string) (proto.Message, error) {
return nil, fmt.Errorf("PATCH not allowed")
}
func (h *configHandler) Remove(r *http.Request, name string) (proto.Message, error) {
return nil, fmt.Errorf("DELETE not allowed")
}
func (h *configHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.save, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
if err := h.s.saveConfig(h.save, desc, typeName, r, h.id, h.cfg, h.save, h.input.Modification, tx); err != nil {
return err
}
secrets, err := h.s.loadSecrets(tx)
if err != nil {
return err
}
// Assumes that secrets don't change within this handler.
if h.s.useHydra && !check.ClientsEqual(h.cfg.Clients, h.save.Clients) {
if _, err = h.s.syncToHydra(h.save.Clients, secrets.ClientSecrets, 0, tx); err != nil {
return err
}
}
if !proto.Equal(h.cfg.Options, h.save.Options) {
h.s.updateWarehouseOptions(h.save.Options, getRealm(r), h.tx)
return h.s.registerProject(h.save.Options.GcpServiceAccountProject, h.tx)
}
return nil
}
//////////////////////////////////////////////////////////////////
func (s *Service) configOptionsFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configOptions",
PathPrefix: configOptionsPath,
HasNamedIdentifiers: false,
Service: func() handlerfactory.Service {
return NewConfigOptionsHandler(s)
},
}
}
type configOptionsHandler struct {
s *Service
input *pb.ConfigOptionsRequest
item *pb.ConfigOptions
orig *pb.ConfigOptions
save *pb.ConfigOptions
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigOptionsHandler(s *Service) *configOptionsHandler {
return &configOptionsHandler{
s: s,
input: &pb.ConfigOptionsRequest{},
}
}
func (h *configOptionsHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configOptionsHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
h.item = h.cfg.Options
return true
}
func (h *configOptionsHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.ConfigOptions{}
}
h.input.Item = receiveConfigOptions(h.input.Item, h.cfg)
return nil
}
func (h *configOptionsHandler) Get(r *http.Request, name string) (proto.Message, error) {
return makeConfigOptions(h.item), nil
}
func (h *configOptionsHandler) Post(r *http.Request, name string) (proto.Message, error) {
return nil, fmt.Errorf("POST not allowed")
}
func (h *configOptionsHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.orig = &pb.ConfigOptions{}
proto.Merge(h.orig, h.item)
h.cfg.Options = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configOptionsHandler) Patch(r *http.Request, name string) (proto.Message, error) {
h.orig = &pb.ConfigOptions{}
proto.Merge(h.orig, h.item)
proto.Merge(h.item, h.input.Item)
h.item.ReadOnlyMasterRealm = h.input.Item.ReadOnlyMasterRealm
h.save = h.item
return nil, nil
}
func (h *configOptionsHandler) Remove(r *http.Request, name string) (proto.Message, error) {
return nil, fmt.Errorf("DELETE not allowed")
}
func (h *configOptionsHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configOptionsHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
if err := h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx); err != nil {
return err
}
if h.orig != nil && !proto.Equal(h.orig, h.save) {
h.s.updateWarehouseOptions(h.save, getRealm(r), h.tx)
return h.s.registerProject(h.save.GcpServiceAccountProject, h.tx)
}
return nil
}
//////////////////////////////////////////////////////////////////
func (s *Service) configResourceFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configResource",
PathPrefix: configResourcePath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigResourceHandler(s)
},
}
}
type configResourceHandler struct {
s *Service
input *pb.ConfigResourceRequest
item *pb.Resource
save *pb.Resource
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigResourceHandler(s *Service) *configResourceHandler {
return &configResourceHandler{
s: s,
input: &pb.ConfigResourceRequest{},
}
}
func (h *configResourceHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configResourceHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.Resources[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configResourceHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.Resource{}
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
h.input.Item = receiveResource(h.input.Item)
return nil
}
func (h *configResourceHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configResourceHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.Resources[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configResourceHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.Resources[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configResourceHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Views = h.input.Item.Views
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configResourceHandler) Remove(r *http.Request, name string) (proto.Message, error) {
rmTestResource(h.cfg, name)
delete(h.cfg.Resources, name)
h.save = &pb.Resource{}
return nil, nil
}
func (h *configResourceHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configResourceHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configViewFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configView",
PathPrefix: configViewPath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigViewHandler(s)
},
}
}
type configViewHandler struct {
s *Service
input *pb.ConfigViewRequest
item *pb.View
save *pb.View
res *pb.Resource
resName string
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigViewHandler(s *Service) *configViewHandler {
return &configViewHandler{
s: s,
input: &pb.ConfigViewRequest{},
}
}
func (h *configViewHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configViewHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
resName, ok := vars["resource"]
if !ok {
return false
}
res, ok := h.cfg.Resources[resName]
if !ok {
return false
}
h.res = res
h.resName = resName
item, ok := res.Views[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configViewHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.View{}
}
h.input.Item = receiveView(h.input.Item)
return nil
}
func (h *configViewHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configViewHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.Resources[h.resName].Views[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configViewHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.Resources[h.resName].Views[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configViewHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Items = h.input.Item.Items
h.item.Labels = h.input.Item.Labels
h.item.Roles = h.input.Item.Roles
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configViewHandler) Remove(r *http.Request, name string) (proto.Message, error) {
rmTestView(h.cfg, h.resName, name)
delete(h.cfg.Resources[h.resName].Views, name)
h.save = &pb.View{}
return nil, nil
}
func (h *configViewHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configViewHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configIssuerFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configTrustedIssuer",
PathPrefix: configTrustedIssuerPath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigIssuerHandler(s)
},
}
}
type configIssuerHandler struct {
s *Service
input *pb.ConfigTrustedIssuerRequest
item *pb.TrustedIssuer
save *pb.TrustedIssuer
saveSecret *pb.DamSecrets
sec *pb.DamSecrets
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigIssuerHandler(s *Service) *configIssuerHandler {
return &configIssuerHandler{
s: s,
input: &pb.ConfigTrustedIssuerRequest{},
}
}
func (h *configIssuerHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
if err != nil {
return status, err
}
sec, err := h.s.loadSecrets(tx)
if err != nil {
return http.StatusServiceUnavailable, err
}
h.cfg = cfg
h.id = id
h.tx = tx
h.sec = sec
if h.sec.BrokerSecrets == nil {
h.sec.BrokerSecrets = map[string]string{}
}
return http.StatusOK, nil
}
func (h *configIssuerHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.TrustedIssuers[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configIssuerHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.TrustedIssuer{}
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
return nil
}
func (h *configIssuerHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configIssuerHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.TrustedIssuers[name] = h.input.Item
h.save = h.input.Item
if err := h.modifyClientSecret(name); err != nil {
return nil, err
}
return nil, nil
}
func (h *configIssuerHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.TrustedIssuers[name] = h.input.Item
h.save = h.input.Item
if err := h.modifyClientSecret(name); err != nil {
return nil, err
}
return nil, nil
}
func (h *configIssuerHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Ui = h.input.Item.Ui
h.save = h.item
if err := h.modifyClientSecret(name); err != nil {
return nil, err
}
return nil, nil
}
func (h *configIssuerHandler) Remove(r *http.Request, name string) (proto.Message, error) {
delete(h.cfg.TrustedIssuers, name)
h.save = &pb.TrustedIssuer{}
if len(h.item.ClientId) > 0 {
h.saveSecret = h.sec
delete(h.saveSecret.BrokerSecrets, h.item.ClientId)
}
return nil, nil
}
func (h *configIssuerHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configIssuerHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
if h.input.Modification != nil && h.input.Modification.DryRun {
return nil
}
if err := h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx); err != nil {
return err
}
if h.saveSecret != nil {
if err := h.s.saveSecrets(h.saveSecret, desc, typeName, r, h.id, tx); err != nil {
return err
}
}
return nil
}
// modifyClientSecret when request includes clientSecret and clientId is set.
func (h *configIssuerHandler) modifyClientSecret(name string) error {
if len(h.input.ClientSecret) > 0 {
if len(h.save.ClientId) == 0 {
return status.Errorf(codes.InvalidArgument, "update trusted issuer %q client_secret but missing client_id", name)
}
h.saveSecret = h.sec
h.saveSecret.BrokerSecrets[h.input.Item.ClientId] = h.input.ClientSecret
}
return nil
}
//////////////////////////////////////////////////////////////////
func (s *Service) configSourceFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configTrustedSource",
PathPrefix: configTrustedSourcePath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigSourceHandler(s)
},
}
}
type configSourceHandler struct {
s *Service
input *pb.ConfigTrustedSourceRequest
item *pb.TrustedSource
save *pb.TrustedSource
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigSourceHandler(s *Service) *configSourceHandler {
return &configSourceHandler{
s: s,
input: &pb.ConfigTrustedSourceRequest{},
}
}
func (h *configSourceHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configSourceHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.TrustedSources[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configSourceHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.TrustedSource{}
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
return nil
}
func (h *configSourceHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configSourceHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.TrustedSources[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configSourceHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.TrustedSources[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configSourceHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Sources = h.input.Item.Sources
h.item.VisaTypes = h.input.Item.VisaTypes
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configSourceHandler) Remove(r *http.Request, name string) (proto.Message, error) {
delete(h.cfg.TrustedSources, name)
h.save = &pb.TrustedSource{}
return nil, nil
}
func (h *configSourceHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configSourceHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configPolicyFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configPolicy",
PathPrefix: configPolicyPath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigPolicyHandler(s)
},
}
}
type configPolicyHandler struct {
s *Service
input *pb.ConfigPolicyRequest
item *pb.Policy
save *pb.Policy
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigPolicyHandler(s *Service) *configPolicyHandler {
return &configPolicyHandler{
s: s,
input: &pb.ConfigPolicyRequest{},
}
}
func (h *configPolicyHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configPolicyHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.Policies[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configPolicyHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.Policy{}
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
return nil
}
func (h *configPolicyHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configPolicyHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.Policies[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configPolicyHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.Policies[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configPolicyHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.AnyOf = h.input.Item.AnyOf
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configPolicyHandler) Remove(r *http.Request, name string) (proto.Message, error) {
if _, ok := BuiltinPolicies[name]; ok {
return nil, status.Errorf(codes.InvalidArgument, "cannot delete built-in policy %q", name)
}
delete(h.cfg.Policies, name)
h.save = &pb.Policy{}
return nil, nil
}
func (h *configPolicyHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configPolicyHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configVisaTypeFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configVisaType",
PathPrefix: configVisaTypePath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigVisaTypeHandler(s)
},
}
}
type configVisaTypeHandler struct {
s *Service
input *pb.ConfigVisaTypeRequest
item *pb.VisaType
save *pb.VisaType
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigVisaTypeHandler(s *Service) *configVisaTypeHandler {
return &configVisaTypeHandler{
s: s,
input: &pb.ConfigVisaTypeRequest{},
}
}
func (h *configVisaTypeHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configVisaTypeHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.VisaTypes[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configVisaTypeHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.VisaType{}
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
return nil
}
func (h *configVisaTypeHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configVisaTypeHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.VisaTypes[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configVisaTypeHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.VisaTypes[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configVisaTypeHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configVisaTypeHandler) Remove(r *http.Request, name string) (proto.Message, error) {
delete(h.cfg.VisaTypes, name)
h.save = &pb.VisaType{}
return nil, nil
}
func (h *configVisaTypeHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configVisaTypeHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configServiceTemplateFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configServiceTemplate",
PathPrefix: configServiceTemplatePath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigServiceTemplateHandler(s)
},
}
}
type configServiceTemplateHandler struct {
s *Service
input *pb.ConfigServiceTemplateRequest
item *pb.ServiceTemplate
save *pb.ServiceTemplate
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigServiceTemplateHandler(s *Service) *configServiceTemplateHandler {
return &configServiceTemplateHandler{
s: s,
input: &pb.ConfigServiceTemplateRequest{},
}
}
func (h *configServiceTemplateHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configServiceTemplateHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.ServiceTemplates[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configServiceTemplateHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &pb.ServiceTemplate{}
}
if h.input.Item.Interfaces == nil {
h.input.Item.Interfaces = make(map[string]string)
}
if h.input.Item.ServiceRoles == nil {
h.input.Item.ServiceRoles = make(map[string]*pb.ServiceRole)
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
return nil
}
func (h *configServiceTemplateHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configServiceTemplateHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.ServiceTemplates[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configServiceTemplateHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.ServiceTemplates[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configServiceTemplateHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Interfaces = h.input.Item.Interfaces
h.item.ServiceRoles = h.input.Item.ServiceRoles
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configServiceTemplateHandler) Remove(r *http.Request, name string) (proto.Message, error) {
delete(h.cfg.ServiceTemplates, name)
h.save = &pb.ServiceTemplate{}
return nil, nil
}
func (h *configServiceTemplateHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configServiceTemplateHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
//////////////////////////////////////////////////////////////////
func (s *Service) configPersonaFactory() *handlerfactory.Options {
return &handlerfactory.Options{
TypeName: "configTestPersona",
PathPrefix: configTestPersonaPath,
HasNamedIdentifiers: true,
Service: func() handlerfactory.Service {
return NewConfigPersonaHandler(s)
},
}
}
type configPersonaHandler struct {
s *Service
input *pb.ConfigTestPersonaRequest
item *cpb.TestPersona
save *cpb.TestPersona
cfg *pb.DamConfig
id *ga4gh.Identity
tx storage.Tx
}
func NewConfigPersonaHandler(s *Service) *configPersonaHandler {
return &configPersonaHandler{
s: s,
input: &pb.ConfigTestPersonaRequest{},
}
}
func (h *configPersonaHandler) Setup(r *http.Request, tx storage.Tx) (int, error) {
cfg, id, status, err := h.s.handlerSetup(tx, r, noScope, h.input)
h.cfg = cfg
h.id = id
h.tx = tx
return status, err
}
func (h *configPersonaHandler) LookupItem(r *http.Request, name string, vars map[string]string) bool {
item, ok := h.cfg.TestPersonas[name]
if !ok {
return false
}
h.item = item
return true
}
func (h *configPersonaHandler) NormalizeInput(r *http.Request, name string, vars map[string]string) error {
if err := httputils.DecodeProtoReq(h.input, r); err != nil {
return err
}
if h.input.Item == nil {
h.input.Item = &cpb.TestPersona{}
}
if h.input.Item.Passport == nil {
h.input.Item.Passport = &cpb.Passport{}
}
if h.input.Item.Passport.StandardClaims == nil {
h.input.Item.Passport.StandardClaims = make(map[string]string)
}
if h.input.Item.Ui == nil {
h.input.Item.Ui = make(map[string]string)
}
if h.input.Item.Access != nil {
sort.Strings(h.input.Item.Access)
}
return nil
}
func (h *configPersonaHandler) Get(r *http.Request, name string) (proto.Message, error) {
return h.item, nil
}
func (h *configPersonaHandler) Post(r *http.Request, name string) (proto.Message, error) {
h.cfg.TestPersonas[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configPersonaHandler) Put(r *http.Request, name string) (proto.Message, error) {
h.cfg.TestPersonas[name] = h.input.Item
h.save = h.input.Item
return nil, nil
}
func (h *configPersonaHandler) Patch(r *http.Request, name string) (proto.Message, error) {
proto.Merge(h.item, h.input.Item)
h.item.Passport = h.input.Item.Passport
h.item.Access = h.input.Item.Access
h.item.Ui = h.input.Item.Ui
h.save = h.item
return nil, nil
}
func (h *configPersonaHandler) Remove(r *http.Request, name string) (proto.Message, error) {
delete(h.cfg.TestPersonas, name)
h.save = &cpb.TestPersona{}
return nil, nil
}
func (h *configPersonaHandler) CheckIntegrity(r *http.Request) *status.Status {
return configCheckIntegrity(h.cfg, h.input.Modification, r, h.s.ValidateCfgOpts(getRealm(r), h.tx))
}
func (h *configPersonaHandler) Save(r *http.Request, tx storage.Tx, name string, vars map[string]string, desc, typeName string) error {
return h.s.saveConfig(h.cfg, desc, typeName, r, h.id, h.item, h.save, h.input.Modification, h.tx)
}
////////////////////////////////////////////////////////////
// ConfigHistory implements the HistoryConfig RPC method.
func (s *Service) ConfigHistory(w http.ResponseWriter, r *http.Request) {
h, sts, err := storage.GetHistory(s.store, storage.ConfigDatatype, getRealm(r), storage.DefaultUser, storage.DefaultID, r)
if err != nil {
httputils.WriteError(w, status.Errorf(httputils.RPCCode(sts), "%v", err))
}
httputils.WriteResp(w, h)
}
// ConfigHistoryRevision implements the HistoryRevisionConfig RPC method.
func (s *Service) ConfigHistoryRevision(w http.ResponseWriter, r *http.Request) {
name := getName(r)
rev, err := strconv.ParseInt(name, 10, 64)
if err != nil {
httputils.WriteError(w, status.Errorf(codes.InvalidArgument, "invalid history revision: %q (must be a positive integer)", name))
return
}
cfg := &pb.DamConfig{}
if sts, err := s.realmReadTx(storage.ConfigDatatype, getRealm(r), storage.DefaultUser, storage.DefaultID, rev, cfg, nil); err != nil {
httputils.WriteError(w, status.Errorf(httputils.RPCCode(sts), "%v", err))
return
}
httputils.WriteResp(w, cfg)
}
// ConfigReset implements the corresponding method in the DAM API.
func (s *Service) ConfigReset(w http.ResponseWriter, r *http.Request) {
if _, err := s.store.Wipe(r.Context(), storage.AllRealms, 0, 0); err != nil {
httputils.WriteError(w, status.Errorf(codes.Internal, "%v", err))
return
}
if err := ImportConfig(s.store, s.serviceName, s.warehouse, nil, true, true, true); err != nil {
httputils.WriteError(w, status.Errorf(codes.Internal, "%v", err))
return
}
// Reset clients in Hyrdra
if s.useHydra {
if getRealm(r) != storage.DefaultRealm {
return
}
conf, err := s.loadConfig(nil, storage.DefaultRealm)
if err != nil {
httputils.WriteError(w, status.Errorf(codes.Unavailable, "%v", err))
return
}
secrets, err := s.loadSecrets(nil)
if err != nil {
httputils.WriteError(w, status.Errorf(codes.Unavailable, "%v", err))
return
}
if _, err := s.syncToHydra(conf.Clients, secrets.ClientSecrets, 0, nil); err != nil {
httputils.WriteError(w, status.Errorf(codes.Unavailable, "%v", err))
return
}
}
}
// ConfigTestPersonas implements the ConfigTestPersonas RPC method.
func (s *Service) ConfigTestPersonas(w http.ResponseWriter, r *http.Request) {
cfg, err := s.loadConfig(nil, getRealm(r))
if err != nil {
httputils.WriteError(w, status.Errorf(codes.Unavailable, "%v", err))
return
}
out := &pb.GetTestPersonasResponse{
Personas: cfg.TestPersonas,
}
httputils.WriteResp(w, out)
}