554 lines
16 KiB
Go
554 lines
16 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package configtx
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
pb "github.com/hyperledger/fabric-protos-go/peer"
|
|
)
|
|
|
|
// Application is a copy of the orderer configuration with the addition of an anchor peers
|
|
// list in the organization definition.
|
|
type Application struct {
|
|
Organizations []Organization
|
|
Capabilities []string
|
|
Policies map[string]Policy
|
|
ACLs map[string]string
|
|
ModPolicy string
|
|
}
|
|
|
|
// ApplicationGroup encapsulates the part of the config that controls
|
|
// application channels.
|
|
type ApplicationGroup struct {
|
|
applicationGroup *cb.ConfigGroup
|
|
}
|
|
|
|
// ApplicationOrg encapsulates the parts of the config that control
|
|
// an application organization's configuration.
|
|
type ApplicationOrg struct {
|
|
orgGroup *cb.ConfigGroup
|
|
name string
|
|
}
|
|
|
|
// MSP returns an OrganizationMSP object that can be used to configure the organization's MSP.
|
|
func (a *ApplicationOrg) MSP() *OrganizationMSP {
|
|
return &OrganizationMSP{
|
|
configGroup: a.orgGroup,
|
|
}
|
|
}
|
|
|
|
// Application returns the application group the updated config.
|
|
func (c *ConfigTx) Application() *ApplicationGroup {
|
|
applicationGroup := c.updated.ChannelGroup.Groups[ApplicationGroupKey]
|
|
return &ApplicationGroup{applicationGroup: applicationGroup}
|
|
}
|
|
|
|
// Organization returns the application org from the updated config.
|
|
func (a *ApplicationGroup) Organization(name string) *ApplicationOrg {
|
|
organizationGroup, ok := a.applicationGroup.Groups[name]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &ApplicationOrg{name: name, orgGroup: organizationGroup}
|
|
}
|
|
|
|
// SetOrganization sets the organization config group for the given application
|
|
// org key in an existing Application configuration's Groups map.
|
|
// If the application org already exists in the current configuration, its value will be overwritten.
|
|
func (a *ApplicationGroup) SetOrganization(org Organization) error {
|
|
orgGroup, err := newApplicationOrgConfigGroup(org)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create application org %s: %v", org.Name, err)
|
|
}
|
|
|
|
a.applicationGroup.Groups[org.Name] = orgGroup
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveOrganization removes an org from the Application group.
|
|
// Removal will panic if the application group does not exist.
|
|
func (a *ApplicationGroup) RemoveOrganization(orgName string) {
|
|
delete(a.applicationGroup.Groups, orgName)
|
|
}
|
|
|
|
// Configuration returns the existing application configuration values from a config
|
|
// transaction as an Application type. This can be used to retrieve existing values for the application
|
|
// prior to updating the application configuration.
|
|
func (a *ApplicationGroup) Configuration() (Application, error) {
|
|
var applicationOrgs []Organization
|
|
for orgName := range a.applicationGroup.Groups {
|
|
orgConfig, err := a.Organization(orgName).Configuration()
|
|
if err != nil {
|
|
return Application{}, fmt.Errorf("retrieving application org %s: %v", orgName, err)
|
|
}
|
|
|
|
applicationOrgs = append(applicationOrgs, orgConfig)
|
|
}
|
|
|
|
capabilities, err := a.Capabilities()
|
|
if err != nil {
|
|
return Application{}, fmt.Errorf("retrieving application capabilities: %v", err)
|
|
}
|
|
|
|
policies, err := a.Policies()
|
|
if err != nil {
|
|
return Application{}, fmt.Errorf("retrieving application policies: %v", err)
|
|
}
|
|
|
|
acls, err := a.ACLs()
|
|
if err != nil {
|
|
return Application{}, fmt.Errorf("retrieving application acls: %v", err)
|
|
}
|
|
|
|
return Application{
|
|
Organizations: applicationOrgs,
|
|
Capabilities: capabilities,
|
|
Policies: policies,
|
|
ACLs: acls,
|
|
}, nil
|
|
}
|
|
|
|
// Configuration returns the existing application org configuration values
|
|
// from the updated config.
|
|
func (a *ApplicationOrg) Configuration() (Organization, error) {
|
|
org, err := getOrganization(a.orgGroup, a.name)
|
|
if err != nil {
|
|
return Organization{}, err
|
|
}
|
|
return org, nil
|
|
}
|
|
|
|
// Capabilities returns a map of enabled application capabilities
|
|
// from the updated config.
|
|
func (a *ApplicationGroup) Capabilities() ([]string, error) {
|
|
capabilities, err := getCapabilities(a.applicationGroup)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("retrieving application capabilities: %v", err)
|
|
}
|
|
|
|
return capabilities, nil
|
|
}
|
|
|
|
// AddCapability sets capability to the provided channel config.
|
|
// If the provided capability already exists in current configuration, this action
|
|
// will be a no-op.
|
|
func (a *ApplicationGroup) AddCapability(capability string) error {
|
|
capabilities, err := a.Capabilities()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = addCapability(a.applicationGroup, capabilities, AdminsPolicyKey, capability)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveCapability removes capability to the provided channel config.
|
|
func (a *ApplicationGroup) RemoveCapability(capability string) error {
|
|
capabilities, err := a.Capabilities()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = removeCapability(a.applicationGroup, capabilities, AdminsPolicyKey, capability)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Policies returns a map of policies for the application config group in
|
|
// the updatedconfig.
|
|
func (a *ApplicationGroup) Policies() (map[string]Policy, error) {
|
|
return getPolicies(a.applicationGroup.Policies)
|
|
}
|
|
|
|
// SetModPolicy sets the specified modification policy for the application group.
|
|
func (a *ApplicationGroup) SetModPolicy(modPolicy string) error {
|
|
if modPolicy == "" {
|
|
return errors.New("non empty mod policy is required")
|
|
}
|
|
|
|
a.applicationGroup.ModPolicy = modPolicy
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetPolicy sets the specified policy in the application group's config policy map.
|
|
// If the policy already exists in current configuration, its value will be overwritten.
|
|
func (a *ApplicationGroup) SetPolicy(policyName string, policy Policy) error {
|
|
err := setPolicy(a.applicationGroup, policyName, policy)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set policy '%s': %v", policyName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetPolicies sets the specified policies in the application group's config policy map.
|
|
// If the policies already exist in current configuration, the values will be replaced with new policies.
|
|
func (a *ApplicationGroup) SetPolicies(policies map[string]Policy) error {
|
|
err := setPolicies(a.applicationGroup, policies)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set policies: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemovePolicy removes an existing policy from an application's configuration.
|
|
// Removal will panic if the application group does not exist.
|
|
func (a *ApplicationGroup) RemovePolicy(policyName string) error {
|
|
policies, err := a.Policies()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
removePolicy(a.applicationGroup, policyName, policies)
|
|
return nil
|
|
}
|
|
|
|
// Policies returns the map of policies for a specific application org in
|
|
// the updated config.
|
|
func (a *ApplicationOrg) Policies() (map[string]Policy, error) {
|
|
return getPolicies(a.orgGroup.Policies)
|
|
}
|
|
|
|
// SetModPolicy sets the specified modification policy for the application organization group.
|
|
func (a *ApplicationOrg) SetModPolicy(modPolicy string) error {
|
|
if modPolicy == "" {
|
|
return errors.New("non empty mod policy is required")
|
|
}
|
|
|
|
a.orgGroup.ModPolicy = modPolicy
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetPolicy sets the specified policy in the application org group's config policy map.
|
|
// If an Organization policy already exists in current configuration, its value will be overwritten.
|
|
func (a *ApplicationOrg) SetPolicy(policyName string, policy Policy) error {
|
|
err := setPolicy(a.orgGroup, policyName, policy)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set policy '%s': %v", policyName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetPolicies sets the specified policies in the application org group's config policy map.
|
|
// If the policies already exist in current configuration, the values will be replaced with new policies.
|
|
func (a *ApplicationOrg) SetPolicies(policies map[string]Policy) error {
|
|
err := setPolicies(a.orgGroup, policies)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set policies: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemovePolicy removes an existing policy from an application organization.
|
|
func (a *ApplicationOrg) RemovePolicy(policyName string) error {
|
|
policies, err := a.Policies()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
removePolicy(a.orgGroup, policyName, policies)
|
|
return nil
|
|
}
|
|
|
|
// AnchorPeers returns the list of anchor peers for an application org
|
|
// in the updated config.
|
|
func (a *ApplicationOrg) AnchorPeers() ([]Address, error) {
|
|
anchorPeerConfigValue, ok := a.orgGroup.Values[AnchorPeersKey]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
anchorPeersProto := &pb.AnchorPeers{}
|
|
|
|
err := proto.Unmarshal(anchorPeerConfigValue.Value, anchorPeersProto)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed unmarshaling %s's anchor peer endpoints: %v", a.name, err)
|
|
}
|
|
|
|
if len(anchorPeersProto.AnchorPeers) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
anchorPeers := []Address{}
|
|
for _, ap := range anchorPeersProto.AnchorPeers {
|
|
anchorPeers = append(anchorPeers, Address{
|
|
Host: ap.Host,
|
|
Port: int(ap.Port),
|
|
})
|
|
}
|
|
|
|
return anchorPeers, nil
|
|
}
|
|
|
|
// AddAnchorPeer adds an anchor peer to an application org's configuration
|
|
// in the updated config.
|
|
func (a *ApplicationOrg) AddAnchorPeer(newAnchorPeer Address) error {
|
|
anchorPeersProto := &pb.AnchorPeers{}
|
|
|
|
if anchorPeerConfigValue, ok := a.orgGroup.Values[AnchorPeersKey]; ok {
|
|
// Unmarshal existing anchor peers if the config value exists
|
|
err := proto.Unmarshal(anchorPeerConfigValue.Value, anchorPeersProto)
|
|
if err != nil {
|
|
return fmt.Errorf("failed unmarshaling anchor peer endpoints: %v", err)
|
|
}
|
|
}
|
|
|
|
// Persist existing anchor peers if found
|
|
anchorProtos := anchorPeersProto.AnchorPeers
|
|
|
|
for _, anchorPeer := range anchorProtos {
|
|
if anchorPeer.Host == newAnchorPeer.Host && anchorPeer.Port == int32(newAnchorPeer.Port) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// Append new anchor peer to anchorProtos
|
|
anchorProtos = append(anchorProtos, &pb.AnchorPeer{
|
|
Host: newAnchorPeer.Host,
|
|
Port: int32(newAnchorPeer.Port),
|
|
})
|
|
|
|
// Add anchor peers config value back to application org
|
|
err := setValue(a.orgGroup, anchorPeersValue(anchorProtos), AdminsPolicyKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveAnchorPeer removes an anchor peer from an application org's configuration
|
|
// in the updated config.
|
|
func (a *ApplicationOrg) RemoveAnchorPeer(anchorPeerToRemove Address) error {
|
|
anchorPeersProto := &pb.AnchorPeers{}
|
|
|
|
if anchorPeerConfigValue, ok := a.orgGroup.Values[AnchorPeersKey]; ok {
|
|
// Unmarshal existing anchor peers if the config value exists
|
|
err := proto.Unmarshal(anchorPeerConfigValue.Value, anchorPeersProto)
|
|
if err != nil {
|
|
return fmt.Errorf("failed unmarshaling anchor peer endpoints for application org %s: %v", a.name, err)
|
|
}
|
|
}
|
|
|
|
existingAnchorPeers := anchorPeersProto.AnchorPeers[:0]
|
|
for _, anchorPeer := range anchorPeersProto.AnchorPeers {
|
|
if anchorPeer.Host != anchorPeerToRemove.Host || anchorPeer.Port != int32(anchorPeerToRemove.Port) {
|
|
existingAnchorPeers = append(existingAnchorPeers, anchorPeer)
|
|
|
|
// Add anchor peers config value back to application org
|
|
err := setValue(a.orgGroup, anchorPeersValue(existingAnchorPeers), AdminsPolicyKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove anchor peer %v from org %s: %v", anchorPeerToRemove, a.name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(existingAnchorPeers) == len(anchorPeersProto.AnchorPeers) {
|
|
return fmt.Errorf("could not find anchor peer %s:%d in application org %s", anchorPeerToRemove.Host, anchorPeerToRemove.Port, a.name)
|
|
}
|
|
|
|
// Add anchor peers config value back to application org
|
|
err := setValue(a.orgGroup, anchorPeersValue(existingAnchorPeers), AdminsPolicyKey)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove anchor peer %v from org %s: %v", anchorPeerToRemove, a.name, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ACLs returns a map of ACLS for given config application.
|
|
func (a *ApplicationGroup) ACLs() (map[string]string, error) {
|
|
aclConfigValue, ok := a.applicationGroup.Values[ACLsKey]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
aclProtos := &pb.ACLs{}
|
|
|
|
err := proto.Unmarshal(aclConfigValue.Value, aclProtos)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unmarshaling %s: %v", ACLsKey, err)
|
|
}
|
|
|
|
retACLs := map[string]string{}
|
|
for apiResource, policyRef := range aclProtos.Acls {
|
|
retACLs[apiResource] = policyRef.PolicyRef
|
|
}
|
|
|
|
return retACLs, nil
|
|
}
|
|
|
|
// SetACLs sets ACLS to an existing channel config application.
|
|
// If an ACL already exists in current configuration, it will be replaced with new ACL.
|
|
func (a *ApplicationGroup) SetACLs(acls map[string]string) error {
|
|
err := setValue(a.applicationGroup, aclValues(acls), AdminsPolicyKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveACLs a list of ACLs from given channel config application.
|
|
// Specifying acls that do not exist in the application ConfigGroup of the channel config will not return a error.
|
|
// Removal will panic if application group does not exist.
|
|
func (a *ApplicationGroup) RemoveACLs(acls []string) error {
|
|
configACLs, err := a.ACLs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, acl := range acls {
|
|
delete(configACLs, acl)
|
|
}
|
|
|
|
err = setValue(a.applicationGroup, aclValues(configACLs), AdminsPolicyKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetMSP updates the MSP config for the specified application
|
|
// org group.
|
|
func (a *ApplicationOrg) SetMSP(updatedMSP MSP) error {
|
|
currentMSP, err := a.MSP().Configuration()
|
|
if err != nil {
|
|
return fmt.Errorf("retrieving msp: %v", err)
|
|
}
|
|
|
|
if currentMSP.Name != updatedMSP.Name {
|
|
return errors.New("MSP name cannot be changed")
|
|
}
|
|
|
|
err = updatedMSP.validateCACerts()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = a.setMSPConfig(updatedMSP)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *ApplicationOrg) setMSPConfig(updatedMSP MSP) error {
|
|
mspConfig, err := newMSPConfig(updatedMSP)
|
|
if err != nil {
|
|
return fmt.Errorf("new msp config: %v", err)
|
|
}
|
|
|
|
err = setValue(a.orgGroup, mspValue(mspConfig), AdminsPolicyKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// newApplicationGroupTemplate returns the application component of the channel
|
|
// configuration with only the names of the application organizations.
|
|
// By default, it sets the mod_policy of all elements to "Admins".
|
|
func newApplicationGroupTemplate(application Application) (*cb.ConfigGroup, error) {
|
|
var err error
|
|
|
|
applicationGroup := newConfigGroup()
|
|
applicationGroup.ModPolicy = AdminsPolicyKey
|
|
|
|
if application.ModPolicy != "" {
|
|
applicationGroup.ModPolicy = application.ModPolicy
|
|
}
|
|
|
|
if err = setPolicies(applicationGroup, application.Policies); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(application.ACLs) > 0 {
|
|
err = setValue(applicationGroup, aclValues(application.ACLs), AdminsPolicyKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(application.Capabilities) > 0 {
|
|
err = setValue(applicationGroup, capabilitiesValue(application.Capabilities), AdminsPolicyKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, org := range application.Organizations {
|
|
applicationGroup.Groups[org.Name] = newConfigGroup()
|
|
}
|
|
|
|
return applicationGroup, nil
|
|
}
|
|
|
|
// newApplicationGroup returns the application component of the channel
|
|
// configuration with the entire configuration for application organizations.
|
|
// By default, it sets the mod_policy of all elements to "Admins".
|
|
func newApplicationGroup(application Application) (*cb.ConfigGroup, error) {
|
|
applicationGroup, err := newApplicationGroupTemplate(application)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, org := range application.Organizations {
|
|
applicationGroup.Groups[org.Name], err = newOrgConfigGroup(org)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("org group '%s': %v", org.Name, err)
|
|
}
|
|
}
|
|
|
|
return applicationGroup, nil
|
|
}
|
|
|
|
// aclValues returns the config definition for an application's resources based ACL definitions.
|
|
// It is a value for the /Channel/Application/.
|
|
func aclValues(acls map[string]string) *standardConfigValue {
|
|
a := &pb.ACLs{
|
|
Acls: make(map[string]*pb.APIResource),
|
|
}
|
|
|
|
for apiResource, policyRef := range acls {
|
|
a.Acls[apiResource] = &pb.APIResource{PolicyRef: policyRef}
|
|
}
|
|
|
|
return &standardConfigValue{
|
|
key: ACLsKey,
|
|
value: a,
|
|
}
|
|
}
|
|
|
|
// anchorPeersValue returns the config definition for an org's anchor peers.
|
|
// It is a value for the /Channel/Application/*.
|
|
func anchorPeersValue(anchorPeers []*pb.AnchorPeer) *standardConfigValue {
|
|
return &standardConfigValue{
|
|
key: AnchorPeersKey,
|
|
value: &pb.AnchorPeers{AnchorPeers: anchorPeers},
|
|
}
|
|
}
|