I was working on some permission logic for Hyperledger , When need arise for a condition where i need to use 3 resources in permission.acl condition.
To Brief about scenario Let us suppose Resource Transaction is t for which Read Permission needs to be given, Participant is P Who needs to read transaction t.
Now Transaction t contains an identifier name for Asset A .
So I want to make a condition like if Asset A where Identifier(name) is equal to to the transaction T name , compares the Asset A registrar(This will hold participant name) with the Participant P Identifier. And If comparison is successful then give read permission of Resource(Transaction T) to Participant P.
Eg Snippet.
Asset ABC identified by name{
o String name;
--> Company registrar; (Company is type of participant)
}
Transaction CreateABC{
o String name;
}
So Participant P of type company should have permission to Read Transaction CreateABC if the Asset containing createABC.name has registar equal to P.getIdenitifer();
I have read that We can create separate functions in js file and call that from permission.acl but i am not yet able to fulfill this scenario.
An example of using 3 resources would be something like (example):
// first, access to invoke the transaction resource itself
rule Transaction_access {
description: "Can generate transaction"
participant: "org.acme.account.AccountTrader"
operation: CREATE
resource: "org.acme.account.TransferAmount"
action: ALLOW
}
// next, the (example) dynamic ACL rule that evaluates between who the transaction is being done
rule BiTrade_betweenTraders_only {
description: "Only Allow Updatee between Transferor/Transferee via named transaction"
participant(p): "org.acme.account.AccountTrader"
operation: ALL
resource(v): "org.acme.account.BankAccount"
transaction(tx): "org.acme.account.TransferAmount"
condition: ( p.getIdentifier() === v.owner.getIdentifier() && v.getIdentifier() === tx.targetAccount.getIdentifier() )
action: ALLOW
}
UPDATED with item 2:
An example of giving access to a transaction resource, based on an Asset ownership by a participant (both derived from the transaction object) might be:
rule my_restricted_Transaction_access {
description: "as per description above"
participant(p): "org.acme.account.AccountTrader"
operation: CREATE
resource(v): "org.acme.account.TransferAmount"
condition: ( p.getIdentifier() === v.account.owner.getIdentifier() )
action: ALLOW
}
where TransferAmount might be defined as:
transaction TransferAmount {
--> Account account // asset
}
and account has an --> owner field pointing back to AccountTrader ( the participant, in my original example - etc etc) - bear in mind, your ACLs would have to allow the participant to have access to the relevant asset and asset owner target resource too.
obviously this is a simple example - but you can define your function (to do the equivalent check, for your model) in the condition section. If you've added the JS script to your BNA under /lib (and upgraded the business network on Fabric to take effect) - you then just have to worry about whether your function name is what you called it (again, the links I sent you should provide a clear example of it in use).
Calling a function as part of your ACL condition is straightforward - you can see an example of this in this github test file -> the function (JS) is here https://github.com/hyperledger/composer/blob/master/packages/composer-tests-functional/systest/data/accesscontrols.js#L23 and the corresponding (calling) ACL ruleset is here -> https://github.com/hyperledger/composer/blob/master/packages/composer-tests-functional/systest/data/accesscontrols.acl#L124
UPDATED item 3:
eg in your permissions.acl file a rule like:
rule rule_func_condition {
description: "Allow all participants access to all resources"
participant(p): "org.acme.account.AccountTrader"
operation: CREATE
resource(a): "org.example.account.TransferAmount"
condition: (testOwnership(a, p))
action: ALLOW
}
in your functions.js (or whatever) in the /lib folder (or you can have it your existing logic.js if you prefer, however you want to do it):
/**
* Test that the specified asset is owned by the specified participant.
* #param {Resource} asset The asset.
* #param {Resource} participant The participant.
* #return {boolean} True if yes, false if no.
*/
function testOwnership(asset, participant) {
return asset.owner.getIdentifier() === participant.getIdentifier();
}
where asset and participant objects are passed into this particular function example.
Related
I am getting bellow error while playing around Hyperledger Composer.
{
"$class": "org.property.registration.purchaseProperty",
"propertyListing":
"resource:org.property.registration.PropertyListing#PL001"
}
Error: attempt to get property owner on an InvalidRelationship is not allowed. InvalidRelationship created due to Object with ID '1003' in collection with ID 'Asset:org.property.registration.Property' does not exist; [cause=Participant 'org.property.registration.User#0001' does not have 'READ' access to resource 'org.property.registration.Property#1003']
I am trying to access Asset property which is a part of another asset propertyListing.
asset Property identified by PID {
o String PID
o Integer marketPrice
o DateTime regDate
o String propertyType
o String location
o String status default= "Registered"
--> User owner
}
asset PropertyListing identified by PLID {
o String PLID
--> Property property
}
I am trying to access PropertyListing asset and change status of property asset inside it.
(I want to purchase the property from the propertyListing posted by some other user)
const registry = await getAssetRegistry(tx.propertyListing.property.getFullyQualifiedType());
await registry.update(tx.propertyListing.property);
// Remove the property from propertyListing()
const registry2 = await getAssetRegistry(tx.propertyListing.getFullyQualifiedType());
await registry2.remove(tx.propertyListing);
I hope and as per the error message it seems like some permission issue which is blocking me to purchase the property post by other user.
// User can see all properties listed for sale
rule UserAccessAllSaleProperties {
description: "Allow Users to access all properties listed for sale"
participant: "org.property.registration.User"
operation: ALL
resource: "org.property.registration.PropertyListing"
action: ALLOW
}
Here i want to access the property which is part of PropertyListing.
I am trying to find what short of ACL i can use. Still trying.
Guys your suggestions are welcome!!!
Try,
rule UserAccessAllSaleProperties {
description: "Allow Users to access all properties listed for sale"
participant: "org.property.registration.**"
operation: READ
resource: "org.property.registration.PropertyListing"
action: ALLOW
}
This will allow all participants to only "READ" the PropertyListing Asset which will be better suited to your application. It will be beneficial if you introduce any future participants. (Assuming User is defined as a participant and not an asset).
I would also recommend separating your participant and Asset files since you have a big application.
like
org.property.registration.Property (-> Will only contain Assets)
org.property.registration.Participants (-> Will only contain Participants)
And import them into each other.
so your rule will be like
rule UserAccessAllSaleProperties {
description: "Allow Users to access all properties listed for sale"
participant: "org.property.registration.Participants.**"
operation: READ
resource: "org.property.registration.PropertyListing"
action: ALLOW
}
I have this structure in my model.cto file :
namespace org.gov.budget
asset Tax identified by Id{
o String Id
--> TaxPayer payer
o Double amount
o Integer year
o Boolean processed
}
asset BudgetAccount identified by Id{
o String Id
o Double amount
}
participant Government identified by Id{
o String Id
--> BudgetAccount account
}
participant TaxPayer identified by PANID{
o String PANID
o String name
o Double income
o Integer taxSlab
}
transaction PayTax{
-->Tax tax
-->Government gov
}
Here is the implementation for the transaction.
async function payTax(tax){
tax.tax.amount = tax.tax.payer.income*tax.tax.payer.taxSlab*0.05;
tax.gov.account.amount+=tax.tax.amount;
tax.tax.processed = true;
let assetRegistry = await getAssetRegistry('org.gov.budget.BudgetAccount');
await assetRegistry.update(tax.gov.account);
assetRegistry = await getAssetRegistry('org.gov.budget.Tax');
await assetRegistry.update(tax.tax);
}
Now when I try to submit the PayTax transaction logged in as a TaxPayer participant (not admin) I run into all these troubles that TaxPayer doesn't have READ access to the resources involved in the transaction. I had to add the below two rules inorder to remove the first READ access problem regarding Government entity say 'G1' but after this it's throwing an error saying TaxPayer doesn't have READ access to type BugdetAccount 'B1' that is linked to 'G1'. Will I need to provide read access for every individual asset/participant/type that are accessed within a composite type here as in BudgetAccount within Government? Doesn't it get very complex if there are a lot of composite entities linked with each other?
rule abc{
description: "Grant business network administrators full access to system resources"
participant: "org.gov.budget.TaxPayer"
operation: READ
resource: "org.hyperledger.composer.system.ParticipantRegistry"
action: ALLOW
}
rule abc4{
description: "Grant business network administrators full access to system resources"
participant: "org.gov.budget.TaxPayer"
operation: READ
resource: "org.gov.budget.Government"
action: ALLOW
}
Yes, Composer decentralization system is very tight, so you need to grant each participant permission with specific roles. It makes your network safe.
If you want to make a transaction for TaxPayer, you should grant the necessary rights to execute the transaction like: READ Tax, Government, UPDATE Government, Tax and CREATE Tax ...
I have defined Participant and asset as below and I want to add a particular participant to array of access[type Participant] in transaction. Can someone tell me how this can be done.
How can I add the participant identity to the array. How do I need to pass the identity through params and then add it to the array of participants. I have created the permissions according to the question I asked earlier. Link to the question for permission
ASSET DECLARATION
asset Details identified by detailsId {
o String detailsId
o String description optional
--> Bank owner
o Bank[] access optional
o String document
}
PARTCIPANT DECLARATION
participant Bank identified by bankId {
o String bankId
o String Name
}
You have made a small change to the model between the ACL example and this example - changing the access array which was a relationship before.
--> Bank[] access optional original
vs
o Bank[] access optional here
Based on the original model, first add this Transaction to the model:
transaction AddAccess {
--> Details details
--> Bank additionalAccess
}
Then, this scripted Transaction should work for you:
/**
* Adding a Bank to an access list
* #param {org.example.test.AddAccess} addA
* #transaction
*/
async function addAccess(addA) {
// Add the new access to the array, checking if empty first as it is optional field at creation
if (typeof addA.details.access == 'undefined') {
addA.details.access = new Array();
addA.details.access[0] = addA.additionalAccess;
}
else {
addA.details.access.push(addA.additionalAccess);
}
// get the Registry
const assetRegistry = await getAssetRegistry('org.example.test.Details');
// Update the Registry with the new details
await assetRegistry.update(addA.details);
}
Test with JSON like this:
{
"$class": "org.example.test.AddAccess",
"details": "resource:org.example.test.Details#D11",
"additionalAccess": "resource:org.example.test.Bank#9241"
}
I would like to create a query which returns all the Requests (asset) in which the Container's (asset) owner's id is equal to the parameter.
Model file (owner of a container is a Company participant, identified by id):
namespace org.acme.shipping.assets
import org.acme.shipping.participants.*
asset Container identified by number {
o String number
o ContainerType type
o String content
o ContainerStatus status default = "FREE"
--> Company owner
}
enum ContainerType {
o DRY
o REEFER
}
enum ContainerStatus {
o LOCKED
o FREE
}
asset Request identified by id {
o String id
--> Container container
}
Query file
query getRequestsByCompany {
description: "Get requests by company"
statement:
SELECT org.acme.shipping.assets.Request
WHERE (container.owner.id == _$company_id)
}
However, the current query does not seem to work. Is this achievable with a query?
I did a lot of research also to do it using query file, but couldnt find a way, so I think that its not possible at moment.
The alternative way is to use loopback filters:
https://github.com/hyperledger/composer-knowledge-wiki/blob/latest/knowledge.md#information_source--filters-loopback
https://loopback.io/doc/en/lb2/Where-filter.html
Something like:
{"where":{"shipmentId":1000}, "include":"resolve"}
you can go one level in like search by number . I am working on it if get the exact solution .
query getRequestsByCompany {
description: "Get requests by company"
statement:
SELECT org.acme.shipping.assets.Request
WHERE (container == _$container)
}
In Hyperledger Composer, is logic.js related to Transaction?
How to access list from logic.js?
Have any good tutorial to understand what can I do in logic.js file?
Though, here is an answer, I'm explaining how logic.js related to a transaction, so that reader can understand.
Transaction is an asset transfer onto or off the ledger. In Hyperledger Composer transaction is defined in model (Contain .cto extension) file. In logic.js( can be anything.js) file contain the Transaction processor function to execute the transactions defined in the model file.
Here is sample model file:
/**
* My commodity trading network
*/
namespace org.acme.mynetwork
asset Commodity identified by tradingSymbol {
o String tradingSymbol
--> Trader owner
}
participant Trader identified by tradeId {
o String tradeId
o String firstName
o String lastName
}
transaction Trade {
--> Commodity commodity
--> Trader newOwner
}
In the model file, a Trade transaction was defined, specifying a relationship to an asset, and a participant. The Trade transaction is intended to simply accept the identifier of the Commodity asset which is being traded, and the identifier of the Trader participant to set as the new owner.
Here is a logic file which contain JavaScript logic to execute the transactions defined in the model file.
/**
* Track the trade of a commodity from one trader to another
* #param {org.acme.mynetwork.Trade} trade - the trade to be processed
* #transaction
*/
async function tradeCommodity(trade) {
trade.commodity.owner = trade.newOwner;
let assetRegistry = await getAssetRegistry('org.acme.mynetwork.Commodity');
await assetRegistry.update(trade.commodity);
}
Tutorial
Composer Official Documentation
IBM Blochain Developer Tutorial
Yes, but doesn't have to have the filename 'logic.js' exclusively. See more here https://hyperledger.github.io/composer/latest/reference/js_scripts
You model your Array fields first https://hyperledger.github.io/composer/latest/reference/cto_language.html then code it. Arrays are handled like any javascript array handling. See examples here -> https://github.com/hyperledger/composer-sample-networks/blob/master/packages/trade-network/lib/logic.js#L45 of results from a query being handled in an array. Also temperatureReadings is an array being handled in the sample networks here https://github.com/hyperledger/composer-sample-networks/blob/v0.16.x/packages/perishable-network/lib/logic.js#L37
I would encourage you to try out the tutorials to validate your understanding. https://hyperledger.github.io/composer/latest/tutorials/tutorials