I have an asset called MedicalFile which contains a reference to an organization. The participant HealthCareProfessional also belongs to an organization.
Now I'd like to define an ACL rule which limits the health care professional to only view medical files which MedicalFile's are connected to his organisation.
I came up with the following rule:
rule OrganisationMedicalFilePermission {
description: "An organisation may updates a medical file which they have permission from"
participant(h): "nl.epd.blockchain.HealthCareProfessional"
operation: ALL
resource(m): "nl.epd.blockchain.MedicalFile"
condition: (m.organisations.includes(h.organisation))
action: ALLOW
}
This results in an empty array once I invoke the RESTful API with Loopback. I'm authenticated as a health care professional.
Assets & Participant:
asset Organisation identified by id {
o String id
o String name
o String city
o String zipCode
o String street
o String houseNumber
o String houseNumberExtra optional
o OrganisationType organisationType
}
asset MedicalFile identified by bsn {
o String bsn
--> Patient owner
--> Patient[] mentors optional
--> Organisation[] organisations optional
o Visit[] visits optional
o String[] allergies optional
o Treatment[] treatments optional
o Medicine[] medicine optional
}
participant HealthCareProfessional identified by bsn {
o String bsn
o String firstName
o String namePrefix optional
o String lastName
--> Organisation organisation
}
My question is if it's possible to create a condition which validates this problem. If not, what are my options?
It's a good question; there's an updated ACL below that I've tested using the online playground.
This is the updated rule:
rule LimitAccess {
description: "An organisation may updates a medical file which they have permission from"
participant(h): "nl.epd.blockchain.HealthCareProfessional"
operation: ALL
resource(m): "nl.epd.blockchain.MedicalFile"
condition: (
m.organisations.some(function (organisation) {
return organisation.getIdentifier() === h.organisation.getIdentifier();
} )
)
action: ALLOW
}
The some function is the critical piece here to scan the array of relationships. Also note the use of the getIdentifier() function as well rather than trying to access the identifier directly.
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 ...
1.i want to know whether i can make changes to the parmeter defined in the relationship.
what i want to do is to make a function with name of bookflight and then make changes to the number of seats if the flight is booked .
these are my cto files
namespace org.acme.airline.aircraft
/** Aircraft is an ACME Asset*/
asset Aircraft identified by aircraftId {
o String aircraftId
o Ownership ownershipType default="LEASED"
// Number of seats per class
o Integer firstClassSeats range = [4,]
o Integer businessClassSeats range = [6, 20]
o Integer economyClassSeats range = [30, ]
o String nickName optional
}
enum Ownership {
o LEASED
o OWNED
}
and for flight the code is
namespace org.acme.airline.flight
import org.acme.airline.aircraft.Aircraft
asset Flight identified by flightId {
// Solution to the exercise - try out the Regular expression at http://regex101.com
// Share your optimized regular expression with others :)
o String flightId regex=/[A-Z][A-Z][0-9][0-9][0-9]-[0-9][0-9]-[0-3][0-9]-[0-9][0-9]/
o String flightNumber
o Route route
o String[] aliasFlightNumber optional
--> Aircraft aircraft optional
}
concept Route {
o String origin regex=/[A-Z][A-Z][A-Z]/
o String destination regex=/[A-Z][A-Z][A-Z]/
o DateTime schedule
}
// Logistics department of ACME creates the flights
transaction CreateFlight {
o String flightNumber
o String origin
o String destination
o DateTime schedule
}
event FlightCreated {
o String flightId
}
// Assigns an aircraft to the flight
// The logistics / validation on availability of aircraft
// Kept outside of the Blockchain
transaction AssignAircraft {
o String flightId
o String aircraftId
}
// Event indicating that aircraft was assigned
event AircraftAssigned {
o String flightId
o String aircraftId
}
now what i want to make changes to reltionship in the flight
to make changes to it what should i do . i have made a javascript file.to
access make changes in it.
function booktickets(registry){
//array to recored the hold the instances of aircraft resourse
const bnDef = bnUtil.connection.getBusinessNetwork();
const factory = bnDef.getFactory();
let flightResource = factory.newResource(aircraftNamespace,aircraftType,'AE201-05-05-2020');
flightResource.setPropertyValue('flightNumber','AE101');
flightResource.route = factory.newConcept(aircraftNamespace,'Route');
flightResource.route.setPropertyValue('origin', 'DEL');
flightResource.route.setPropertyValue('destination' , 'APH');
flightResource.route.setPropertyValue('schedule' , new Date('2018-10-15T21:44Z'));
flightResource.aircraft = factory.newRelationship('org.acme.airline.aircraft', 'Aircraft', 'CRAFT01');
//.setPropertyValue()
flightResource.aircraft.setPropertyValue('ownershipType','LEASED');
flightResource.aircraft.setPropertyValue('firstClassSeats',10);
flightResource.aircraft.setPropertyValue('businessClassSeats',10);
flightResource.aircraft.setPropertyValue('economyClassSeats',100);
return registry.update(flightResource).then(()=>{
console.log('Successfully created the flight!!!');
bnUtil.disconnect();
}).catch((error)=>{
console.log(error);
bnUtil.disconnect();
});
}
your question appears to be: can you create a relationship from Flight (asset) to Aircraft (asset) in a transaction function (that operates in the chaincode runtime), and can you update the fields of the related Aircraft (in its separate registry). The answer is 'yes', you can, for both. You don't provide the model for the bookflight function, so can only make assumptions about its model definition. as a minimum (based on your code) it will need:
transaction bookflight { }
A code example of what you are trying to do with relationships - is shown here -> https://github.com/hyperledger/composer-sample-networks/blob/master/packages/perishable-network/lib/logic.js#L130
This section:
const bnDef = bnUtil.connection.getBusinessNetwork();
const factory = bnDef.getFactory();
is composer-client code - and will not work inside a transaction function (that is runtime code, you need to remove client code - the example below shows 'how' to do it.) Replace line 2 with :
const factory = getFactory();
See more on transaction functions, examples etc at https://hyperledger.github.io/composer/latest/reference/js_scripts
note: you can just assign values like:
flightResource.route.origin = 'DEL' ; // no need for.setPropertyValue('origin', 'DEL'); etc etc
I don't see your code to update the Aircraft registry (with flightResource.aircraft FYI) - but you would need that to update the fields in that related asset (presently, you're only updating the Flight registry above )
new Date() is non deterministic code - if you're hoping to achieve consensus from multiple peers/orgs.
You'll notice the link I sent earlier, shows the use of async/await rather than JS promises (.then etc etc) - easier to code, easier to read. cheers.
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)
}