7.9 KiB
layout | title | tagline | header | order | learn |
---|---|---|---|---|---|
guide | Tutorial: RBAC | Create tribes where people with different roles can create proposals and or vote. | header-rbac.jpg | 4 | - How RBAC works on BigchainDB |
Hi there! Welcome to our next tutorial about Role-Based Access Control in BigchainDB. For this tutorial, we assume that you are familiar with the BigchainDB primitives (assets, inputs, outputs, transactions etc.). If you are not, familiarize yourself with the Key concepts of BigchainDB. We also assume that you have completed our first tutorial.
RBAC
Role based access control is a way to restrict the system access to certain users. In BigchainDB this function enables the creation of hierarchies of role and permissions. Furthermore, users can be assigned roles to “act on behalf of” or “represent” other users or groups. In our case we propose different tribes where people have different roles, some can create proposals and some others can vote those proposals.
{% include_relative _setup.md %}
Let's create the app. You will create an asset for Admin type which will act as the admin group for the app. Async/await functions will be used in this tutorial
const nameSpace = 'rbac-bdb-tutorial'
async function createApp(){
// Generate keypair for admin instance
const admin1 = new driver.Ed25519Keypair()
// Create admin user type. This is the asset representing the group of
// admins
const adminGroupAsset = {
ns: `${nameSpace}.admin`,
name: 'admin'
}
const adminGroupMetadata = {
canLink: [admin1.publicKey]
}
const adminGroupId = (await createNewAsset(admin1, adminGroupAsset,
adminGroupMetadata)).id
document.body.innerHTML ='<h3>Admin Group asset created</h3>'
document.body.innerHTML +=adminGroupId.id
// Create admin user instance. This is a single user with admin role
// represented by an asset. Is the asset representing admin1 user
// Create app asset with admin1, the umbrella asset for representing the app
const appAsset = {
ns: nameSpace,
name: nameSpace
}
const appMetadata = {
canLink: adminGroupId
}
const appId = (await createNewAsset(admin1, appAsset, appMetadata)).id
console.log('App: ' + appId)
}
The createNewAsset
function looks like this
async function createNewAsset(keypair, asset, metadata) {
let condition = driver.Transaction.makeEd25519Condition(keypair.publicKey,
true)
let output = driver.Transaction.makeOutput(condition)
output.public_keys = [keypair.publicKey]
const transaction = driver.Transaction.makeCreateTransaction(
asset,
metadata,
[output],
keypair.publicKey
)
const txSigned = driver.Transaction.signTransaction(transaction,
keypair.privateKey)
let tx
await conn.postTransaction(txSigned)
.then(() => conn.pollStatusAndFetchTransaction(txSigned.id))
.then(retrievedTx => {
tx = retrievedTx
})
return tx
}
You have just generated the admin type and app asset, so now you are able to create all of the other assets for this RBAC example, which are the users (including the admin one) and the tribe.
function createUsers() {
const user1 = new driver.Ed25519Keypair()
const user2 = new driver.Ed25519Keypair()
const user3 = new driver.Ed25519Keypair()
const adminuser1Metadata = {
event: 'User Assigned',
date: new Date(),
timestamp: Date.now(),
publicKey: admin1.publicKey,
eventData: {
userType: 'admin'
}
}
// Admin user instance belongs to the AdminGroup
const adminUserId = (await createUser(admin1, adminGroupId, 'admin',
admin1.publicKey, adminuser1Metadata)).id
document.body.innerHTML ='<h3>Admin user asset created</h3>'
document.body.innerHTML +=adminUserId
// Tribes are user groups
const tribe1Id = (await createType('tribe1', appId, adminGroupId)).id
document.body.innerHTML ='<h3>Tribe 1 asset created</h3>'
document.body.innerHTML +=tribe1Id
const tribe2Id = (await createType('tribe2', appId, adminGroupId)).id
document.body.innerHTML ='<h3>Tribe 2 asset created</h3>'
document.body.innerHTML +=tribe2Id
// create user instances
const user1Metadata = {
event: 'User Assigned',
date: new Date(),
timestamp: Date.now(),
publicKey: admin1.publicKey,
eventData: {
userType: 'tribe1'
}
}
// Create the asset representing user1 with the admin1 keys.
// Add it to tribe 1
const user1AssetId = (await createUser(admin1, tribe1Id, 'tribe1',
user1.publicKey, user1Metadata)).id
document.body.innerHTML ='<h3>User 1 asset created</h3>'
document.body.innerHTML +=user1AssetId
// create user instances
const user2Metadata = {
event: 'User Assigned',
date: new Date(),
timestamp: Date.now(),
publicKey: admin1.publicKey,
eventData: {
userType: 'tribe2'
}
}
// user 2 added to tribe 2
// Is the asset representing user1
const user2AssetId = (await createUser(admin1, tribe2Id, 'tribe2',
user2.publicKey, user2Metadata)).id
document.body.innerHTML ='<h3>User 2 asset created</h3>'
document.body.innerHTML +=user2AssetId
const user3Metadata = {
event: 'User Assigned',
date: new Date(),
timestamp: Date.now(),
publicKey: admin1.publicKey,
eventData: {
userType: 'tribe1'
}
}
// user 3 added to tribe 1
const user3AssetId = (await createUser(admin1, tribe1Id, 'tribe1',
user3.publicKey, user3Metadata)).id
document.body.innerHTML ='<h3>User 3 asset created</h3>'
document.body.innerHTML +=user3AssetId
}
You have created the users, some of them belonging to tribe 1 and others to tribe 2. Now is time to give different permissions to the different tribes. For that you will create asset types for proposals and for votes.
async function usersToTribes(){
// Non users
// Proposal: only tribe 1 users can create proposal
const proposalGroupId = (await createType('proposal', appId, tribe1Id)).id
console.log('ProposalGroup: ' + proposalGroupId)
// Vote: only tribe 2 users can create vote
const voteGroupId = (await createType('vote', appId, tribe2Id)).id
document.body.innerHTML ='<h3>Vote group asset created</h3>'
document.body.innerHTML +=voteGroupId
So now simply create proposals and votes from different users. You will see how just some users will be able to create proposals and some others will be able to vote.
// create proposal by user 1 - should pass
const proposal1 = await createTypeInstance(user1, 'proposal',
proposalGroupId, { name: 'new proposal by user 1',
timestamp: Date.now() })
document.body.innerHTML ='<h3>Proposal group asset created</h3>'
document.body.innerHTML +=proposal1.id
// create vote by user 2 - should pass
const vote1 = await createTypeInstance(user2, 'vote', voteGroupId,
{ name: 'new vote by user 2', timestamp: Date.now() })
document.body.innerHTML ='<h3>Vote instance created</h3>'
document.body.innerHTML +=vote1.id
// create proposal by user 3 - should pass
const proposal2 = await createTypeInstance(user3, 'proposal',
proposalGroupId, { name: 'new proposal by user 3',
timestamp: Date.now() })
document.body.innerHTML ='<h3>Vote instance created</h3>'
document.body.innerHTML +=proposal2.id
// create vote by user 1 - should fail
const vote2 = await createTypeInstance(user1, 'vote',
voteGroupId, { name: 'new vote by user 1', timestamp: Date.now() })
document.body.innerHTML ='<h3>Vote instance could not be created</h3>'
document.body.innerHTML +=vote2.id
}