github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/examples/chaincode/go/rbac_tcerts_no_attrs/rbac.go (about)

     1  /*
     2   Copyright IBM Corp All Rights Reserved.
     3  
     4   Licensed under the Apache License, Version 2.0 (the "License");
     5   you may not use this file except in compliance with the License.
     6   You may obtain a copy of the License at
     7  
     8        http://www.apache.org/licenses/LICENSE-2.0
     9  
    10   Unless required by applicable law or agreed to in writing, software
    11   distributed under the License is distributed on an "AS IS" BASIS,
    12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   See the License for the specific language governing permissions and
    14   limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"encoding/asn1"
    24  	"encoding/base64"
    25  	"strings"
    26  
    27  	"github.com/hyperledger/fabric/accesscontrol/impl"
    28  	"github.com/hyperledger/fabric/core/chaincode/shim"
    29  	pb "github.com/hyperledger/fabric/protos/peer"
    30  	"github.com/op/go-logging"
    31  )
    32  
    33  var myLogger = logging.MustGetLogger("rbac_chaincode")
    34  
    35  // RBACMetadata metadata structure for RBAC information
    36  type RBACMetadata struct {
    37  	Cert  []byte
    38  	Sigma []byte
    39  }
    40  
    41  // RBACChaincode RBAC chaincode structure
    42  type RBACChaincode struct {
    43  }
    44  
    45  // Init method will be called during deployment
    46  func (t *RBACChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    47  	myLogger.Info("Init")
    48  
    49  	myLogger.Debug("Creating RBAC Table...")
    50  
    51  	// Create RBAC table
    52  	err := stub.CreateTable("RBAC", []*shim.ColumnDefinition{
    53  		&shim.ColumnDefinition{Name: "ID", Type: shim.ColumnDefinition_BYTES, Key: true},
    54  		&shim.ColumnDefinition{Name: "Roles", Type: shim.ColumnDefinition_STRING, Key: false},
    55  	})
    56  	if err != nil {
    57  		return shim.Error("Failed creating RBAC table.")
    58  	}
    59  
    60  	myLogger.Debug("Assign 'admin' role...")
    61  
    62  	// Give to the deployer the role 'admin'
    63  	deployer, err := stub.GetCallerMetadata()
    64  	if err != nil {
    65  		return shim.Error("Failed getting metadata.")
    66  	}
    67  	if len(deployer) == 0 {
    68  		return shim.Error("Invalid admin certificate. Empty.")
    69  	}
    70  
    71  	myLogger.Debug("Add admin [% x][%s]", deployer, "admin")
    72  	ok, err := stub.InsertRow("RBAC", shim.Row{
    73  		Columns: []*shim.Column{
    74  			&shim.Column{Value: &shim.Column_Bytes{Bytes: deployer}},
    75  			&shim.Column{Value: &shim.Column_String_{String_: "admin"}},
    76  		},
    77  	})
    78  	if !ok && err == nil {
    79  		return shim.Error("Failed initiliazing RBAC entries.")
    80  	}
    81  	if err != nil {
    82  		return shim.Error(fmt.Sprintf("Failed initiliazing RBAC entries [%s]", err))
    83  	}
    84  
    85  	myLogger.Debug("Done.")
    86  
    87  	return shim.Success(nil)
    88  }
    89  
    90  // Invoke Run callback representing the invocation of a chaincode
    91  func (t *RBACChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    92  	function, args := stub.GetFunctionAndParameters()
    93  	// Handle different functions
    94  	switch function {
    95  	case "addRole":
    96  		return t.addRole(stub, args)
    97  	case "write":
    98  		return t.write(stub, args)
    99  	case "read":
   100  		return t.read(stub, args)
   101  	}
   102  
   103  	return shim.Error(fmt.Sprintf("Received unknown function invocation [%s]", function))
   104  }
   105  
   106  func (t *RBACChaincode) addRole(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   107  	if len(args) != 2 {
   108  		return shim.Error("Incorrect number of arguments. Expecting 2")
   109  	}
   110  	id, err := base64.StdEncoding.DecodeString(args[0])
   111  	if err != nil {
   112  		return shim.Error(fmt.Sprintf("Failed decoding tcert:  %s", err))
   113  	}
   114  	//id := []byte(args[0])
   115  	role := args[1]
   116  
   117  	myLogger.Debug("Add role [% x][%s]", id, role)
   118  
   119  	// Verify that the invoker has the 'admin' role
   120  	ok, _, err := t.hasInvokerRole(stub, "admin")
   121  	if err != nil {
   122  		return shim.Error(fmt.Sprintf("Failed checking role [%s]", err))
   123  	}
   124  	if !ok {
   125  		return shim.Error("The invoker does not have the required roles.")
   126  	}
   127  
   128  	// Add role to id
   129  	myLogger.Debug("Permission granted to the invoker")
   130  
   131  	// Retrieve id's row
   132  	var columns []shim.Column
   133  	idCol := shim.Column{Value: &shim.Column_Bytes{Bytes: id}}
   134  	columns = append(columns, idCol)
   135  	row, err := stub.GetRow("RBAC", columns)
   136  	if err != nil {
   137  		return shim.Error(fmt.Sprintf("Failed retriving associated row [%s]", err))
   138  	}
   139  	if len(row.Columns) == 0 {
   140  		// Insert row
   141  		ok, err = stub.InsertRow("RBAC", shim.Row{
   142  			Columns: []*shim.Column{
   143  				&shim.Column{Value: &shim.Column_Bytes{Bytes: id}},
   144  				&shim.Column{Value: &shim.Column_String_{String_: role}},
   145  			},
   146  		})
   147  		if err != nil {
   148  			return shim.Error(fmt.Sprintf("Failed inserting row [%s]", err))
   149  		}
   150  		if !ok {
   151  			return shim.Error("Failed inserting row.")
   152  		}
   153  
   154  	} else {
   155  		// Update row
   156  		ok, err = stub.ReplaceRow("RBAC", shim.Row{
   157  			Columns: []*shim.Column{
   158  				&shim.Column{Value: &shim.Column_Bytes{Bytes: id}},
   159  				&shim.Column{Value: &shim.Column_String_{String_: row.Columns[1].GetString_() + " " + role}},
   160  			},
   161  		})
   162  		if err != nil {
   163  			return shim.Error(fmt.Sprintf("Failed replacing row [%s]", err))
   164  		}
   165  		if !ok {
   166  			return shim.Error("Failed replacing row.")
   167  		}
   168  	}
   169  
   170  	return shim.Success(nil)
   171  }
   172  
   173  func (t *RBACChaincode) read(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   174  	if len(args) != 0 {
   175  		return shim.Error("Incorrect number of arguments. Expecting 0")
   176  	}
   177  	myLogger.Debug("Read...")
   178  
   179  	// Verify that the invoker has the 'reader' role
   180  	ok, _, err := t.hasInvokerRole(stub, "reader")
   181  	if err != nil {
   182  		return shim.Error(fmt.Sprintf("Failed checking role [%s]", err))
   183  	}
   184  	if !ok {
   185  		return shim.Error("The invoker does not have the required roles")
   186  	}
   187  
   188  	res, err := stub.GetState("state")
   189  	if err != nil {
   190  		return shim.Error(fmt.Sprintf("Failed getting state [%s]", err))
   191  	}
   192  
   193  	myLogger.Debug("State [%s]", string(res))
   194  
   195  	return shim.Success(res)
   196  }
   197  
   198  func (t *RBACChaincode) write(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   199  	if len(args) != 1 {
   200  		return shim.Error("Incorrect number of arguments. Expecting 1")
   201  	}
   202  
   203  	value := args[0]
   204  
   205  	myLogger.Debug("Write [%s]", value)
   206  
   207  	// Verify that the invoker has the 'writer' role
   208  	ok, _, err := t.hasInvokerRole(stub, "writer")
   209  	if err != nil {
   210  		return shim.Error(fmt.Sprintf("Failed checking role [%s]", err))
   211  	}
   212  	if !ok {
   213  		return shim.Error("The invoker does not have the required roles.")
   214  	}
   215  
   216  	err = stub.PutState("state", []byte(value))
   217  	if err != nil {
   218  		return shim.Error(err.Error())
   219  	}
   220  
   221  	return shim.Success(nil)
   222  }
   223  
   224  func (t *RBACChaincode) hasInvokerRole(stub shim.ChaincodeStubInterface, role string) (bool, []byte, error) {
   225  	// In order to enforce access control, we require that the
   226  	// metadata contains the following items:
   227  	// 1. a certificate Cert
   228  	// 2. a signature Sigma under the signing key corresponding
   229  	// to the verification key inside Cert of :
   230  	// (a) Cert;
   231  	// (b) The payload of the transaction (namely, function name and args) and
   232  	// (c) the transaction binding.
   233  
   234  	// Verify Sigma=Sign(certificate.sk, Cert||tx.Payload||tx.Binding) against Cert.vk
   235  
   236  	// Unmarshall metadata
   237  	metadata, err := stub.GetCallerMetadata()
   238  	rbacMetadata := new(RBACMetadata)
   239  	_, err = asn1.Unmarshal(metadata, rbacMetadata)
   240  	if err != nil {
   241  		return false, nil, fmt.Errorf("Failed unmarshalling metadata [%s]", err)
   242  	}
   243  
   244  	// Verify signature
   245  	payload, err := stub.GetArgsSlice()
   246  	if err != nil {
   247  		return false, nil, errors.New("Failed getting payload")
   248  	}
   249  	binding, err := stub.GetBinding()
   250  	if err != nil {
   251  		return false, nil, errors.New("Failed getting binding")
   252  	}
   253  
   254  	myLogger.Debug("passed certificate [% x]", rbacMetadata.Cert)
   255  	myLogger.Debug("passed sigma [% x]", rbacMetadata.Sigma)
   256  	myLogger.Debug("passed payload [% x]", payload)
   257  	myLogger.Debug("passed binding [% x]", binding)
   258  
   259  	ok, err := impl.NewAccessControlShim(stub).VerifySignature(
   260  		rbacMetadata.Cert,
   261  		rbacMetadata.Sigma,
   262  		append(rbacMetadata.Cert, append(payload, binding...)...),
   263  	)
   264  	if err != nil {
   265  		return false, nil, fmt.Errorf("Failed verifying signature [%s]", err)
   266  	}
   267  	if !ok {
   268  		return false, nil, fmt.Errorf("Signature is not valid!")
   269  	}
   270  
   271  	myLogger.Debug("Signature verified. Check for role...")
   272  
   273  	myLogger.Debug("ID [% x]", rbacMetadata.Cert)
   274  
   275  	// Check role
   276  	var columns []shim.Column
   277  	idCol := shim.Column{Value: &shim.Column_Bytes{Bytes: rbacMetadata.Cert}}
   278  	columns = append(columns, idCol)
   279  	row, err := stub.GetRow("RBAC", columns)
   280  	if err != nil {
   281  		return false, nil, fmt.Errorf("Failed retrieveing RBAC row [%s]", err)
   282  	}
   283  	if len(row.GetColumns()) == 0 {
   284  		return false, nil, fmt.Errorf("Failed retrieveing RBAC row [%s]", err)
   285  	}
   286  
   287  	myLogger.Debug("#Columns: [%d]", len(row.Columns))
   288  	roles := row.Columns[1].GetString_()
   289  
   290  	result := strings.Split(roles, " ")
   291  	for i := range result {
   292  		if result[i] == role {
   293  			myLogger.Debug("Role found.")
   294  			return true, rbacMetadata.Cert, nil
   295  		}
   296  	}
   297  
   298  	myLogger.Debug("Role not found.")
   299  
   300  	return false, nil, nil
   301  }
   302  
   303  func main() {
   304  	err := shim.Start(new(RBACChaincode))
   305  	if err != nil {
   306  		fmt.Printf("Error starting AssetManagementChaincode: %s", err)
   307  	}
   308  }