From 7ecd0e0e3f42fe9d1ed9377652dde214e0bfaa22 Mon Sep 17 00:00:00 2001 From: Gautam Dhameja Date: Thu, 20 Sep 2018 17:11:41 +0200 Subject: [PATCH] added rbac --- bigchaindb/__init__.py | 6 +- bigchaindb/rbac/__init__.py | 5 ++ bigchaindb/rbac/transaction.py | 128 +++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 bigchaindb/rbac/__init__.py create mode 100644 bigchaindb/rbac/transaction.py diff --git a/bigchaindb/__init__.py b/bigchaindb/__init__.py index 223d7a0e..d2ccbe3d 100644 --- a/bigchaindb/__init__.py +++ b/bigchaindb/__init__.py @@ -96,8 +96,12 @@ from bigchaindb.common.transaction import Transaction # noqa from bigchaindb import models # noqa from bigchaindb.upsert_validator import ValidatorElection # noqa from bigchaindb.elections.vote import Vote # noqa +from bigchaindb.rbac import LinkedTransaction # noqa -Transaction.register_type(Transaction.CREATE, models.Transaction) +# Use validations from LinkedTransaction for CREATE transactions for RBAC +Transaction.register_type(Transaction.CREATE, LinkedTransaction) + +# Use default validation for other operaions Transaction.register_type(Transaction.TRANSFER, models.Transaction) Transaction.register_type(ValidatorElection.OPERATION, ValidatorElection) Transaction.register_type(ChainMigrationElection.OPERATION, ChainMigrationElection) diff --git a/bigchaindb/rbac/__init__.py b/bigchaindb/rbac/__init__.py new file mode 100644 index 00000000..b6b85034 --- /dev/null +++ b/bigchaindb/rbac/__init__.py @@ -0,0 +1,5 @@ +# Copyright BigchainDB GmbH and BigchainDB contributors +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +from bigchaindb.rbac.transaction import LinkedTransaction # noqa diff --git a/bigchaindb/rbac/transaction.py b/bigchaindb/rbac/transaction.py new file mode 100644 index 00000000..83d8bc7f --- /dev/null +++ b/bigchaindb/rbac/transaction.py @@ -0,0 +1,128 @@ +# Copyright BigchainDB GmbH and BigchainDB contributors +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +import logging + +from bigchaindb.common.exceptions import (ValidationError) +from bigchaindb.models import Transaction + +ASSET_RULE_LINK = 'link' +METADATA_RULE_CAN_LINK = 'can_link' + +logger = logging.getLogger(__name__) + +class LinkedTransaction(Transaction): + + def validate(self, bigchain, current_transactions=[]): + super().validate(bigchain, current_transactions) + self.validate_link(bigchain) + return self + + def validate_link(self, bigchain): + transaction = self + logger.info('Validating link') + public_key = transaction.inputs[0].owners_before[0] + + # Don't do anything when it's GENESIS or TRANSFER transaction + if transaction.operation == 'GENESIS' or\ + transaction.operation == 'TRANSFER': + return + + # Don't do anything when there is no asset.data + if not hasattr(transaction.asset, 'data'): + return + + # If link is not being used, don't do anything + if transaction.asset['data'] and ASSET_RULE_LINK not in transaction.asset['data']: + return + + link = transaction.asset['data']['link'] + logger.info('Link: %s', link) + tx_to_link = bigchain.get_transaction(link) + + if not tx_to_link: + raise ValidationError('Transaction not resolved to link: {}' + .format(link)) + + logger.info('Link Transaction: %s', tx_to_link.id) + + if tx_to_link and not hasattr(tx_to_link, 'metadata'): + raise ValidationError('Metadata not found in transaction {}' + .format(tx_to_link)) + + if tx_to_link.metadata is None or METADATA_RULE_CAN_LINK not in tx_to_link.metadata: + raise ValidationError('can_link not found in metadata of transaction {}' + .format(tx_to_link)) + + can_link = tx_to_link.metadata[METADATA_RULE_CAN_LINK] + logger.info('Can link: %s', can_link) + + # can_link validation + # if can_link is a list + # check if can_link is a list of transaction ids or public keys + # check if the public key of the user is a part of it or not + # OR + # check if the user has a premission asset linked to the can_link asset + if isinstance(can_link, list): + logger.info('can_link is a list') + if self.check_if_transaction_id(bigchain, can_link[0]): + self.validate_can_link(bigchain, can_link, public_key) + else: + if public_key in can_link: + logger.info('Link valid: public key in can_link') + return + else: + raise ValidationError('Linking is not authorized for: {}'.format( + public_key)) + # backward compatibility - if can_link is string then convert it to a list + elif isinstance(can_link, str): + logger.info('can_link is a string') + can_link_list = [can_link] + self.validate_can_link(bigchain, can_link_list, public_key) + else: + raise ValidationError('can_link is not valid') + return + + def validate_can_link(self, bigchain, can_link, public_key): + logger.info('validating can_link, looking up assets in owner wallet') + wallet_tx = bigchain.get_owned_ids(public_key) + wallet_tx_ids = [tx.txid for tx in wallet_tx] + logger.info('Wallet has %s assets', len(wallet_tx_ids)) + + for asset_id in wallet_tx_ids: + logger.info('Looking up asset: %s', asset_id) + trans = bigchain.get_transaction(asset_id) + if trans.operation == Transaction.TRANSFER: + permission_asset = bigchain.get_transaction( + trans.asset['id']).asset + else: + permission_asset = trans.asset + if permission_asset and permission_asset['data'] and\ + ASSET_RULE_LINK in permission_asset['data']: + if permission_asset['data']['link'] in can_link: + logger.info('Link valid: asset.link found in can_link') + break + else: + continue + else: + raise ValidationError('Linking is not authorized for: {}'.format( + public_key)) + return + + def check_if_transaction_id(self, bigchain, transaction_id): + logger.info('Checking if tx id: {}'.format(transaction_id)) + is_tx_id = True + try: + tx = bigchain.get_transaction(transaction_id) + if tx: + logger.info( + 'Tx id check passed for: {}'.format(transaction_id)) + else: + logger.info( + 'Tx id check failed for: {}'.format(transaction_id)) + is_tx_id = False + except: + logger.info('Tx id check failed for: {}'.format(transaction_id)) + is_tx_id = False + return is_tx_id