Skip to content
This repository has been archived by the owner on Nov 7, 2024. It is now read-only.

Commit

Permalink
Mechanism and validation
Browse files Browse the repository at this point in the history
  • Loading branch information
guillemcordoba committed Jun 27, 2024
1 parent beb7ced commit 49b46bb
Show file tree
Hide file tree
Showing 8 changed files with 431 additions and 90 deletions.
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

120 changes: 74 additions & 46 deletions ui/src/roles-client.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,81 @@
import { RoleClaim } from './types.js';

import {
SignedActionHashed,
CreateLink,
Link,
DeleteLink,
Delete,
AppClient,
Record,
ActionHash,
EntryHash,
AgentPubKey,
} from '@holochain/client';
import { EntryRecord, ZomeClient } from '@holochain-open-dev/utils';
import {
ActionHash,
AgentPubKey,
AppClient,
CreateLink,
Delete,
DeleteLink,
EntryHash,
Link,
Record,
SignedActionHashed,
} from '@holochain/client';

import { RoleClaim } from './types.js';
import { RolesSignal } from './types.js';

export class RolesClient extends ZomeClient<RolesSignal> {
constructor(public client: AppClient, public roleName: string, public zomeName = 'roles') {
super(client, roleName, zomeName);
}
/** Role Claim */

async createRoleClaim(roleClaim: RoleClaim): Promise<EntryRecord<RoleClaim>> {
const record: Record = await this.callZome('create_role_claim', roleClaim);
return new EntryRecord(record);
}

async getRoleClaim(roleClaimHash: ActionHash): Promise<EntryRecord<RoleClaim> | undefined> {
const record: Record = await this.callZome('get_role_claim', roleClaimHash);
return record ? new EntryRecord(record) : undefined;
}

deleteRoleClaim(originalRoleClaimHash: ActionHash): Promise<ActionHash> {
return this.callZome('delete_role_claim', originalRoleClaimHash);
}

getAllDeletesForRoleClaim(originalRoleClaimHash: ActionHash): Promise<Array<SignedActionHashed<Delete>>> {
return this.callZome('get_all_deletes_for_role_claim', originalRoleClaimHash);
}

getOldestDeleteForRoleClaim(originalRoleClaimHash: ActionHash): Promise<SignedActionHashed<Delete> | undefined> {
return this.callZome('get_oldest_delete_for_role_claim', originalRoleClaimHash);
}

/** All Roles */

async getAllRoles(): Promise<Array<Link>> {
return this.callZome('get_all_roles', undefined);
}
constructor(
public client: AppClient,
public roleName: string,
public zomeName = 'roles',
) {
super(client, roleName, zomeName);
}
/** Role Claim */

async createRoleClaim(roleClaim: RoleClaim): Promise<EntryRecord<RoleClaim>> {
const record: Record = await this.callZome('create_role_claim', roleClaim);
return new EntryRecord(record);
}

async getRoleClaim(
roleClaimHash: ActionHash,
): Promise<EntryRecord<RoleClaim> | undefined> {
const record: Record = await this.callZome('get_role_claim', roleClaimHash);
return record ? new EntryRecord(record) : undefined;
}

getAllDeletesForRoleClaim(
originalRoleClaimHash: ActionHash,
): Promise<Array<SignedActionHashed<Delete>>> {
return this.callZome(
'get_all_deletes_for_role_claim',
originalRoleClaimHash,
);
}

/** All Roles */

async getAllRoles(): Promise<Array<Link>> {
return this.callZome('get_all_roles', undefined);
}

/** Assignees */

async getAssigneesForRole(role: string): Promise<Array<Link>> {
return this.callZome('get_assignees_for_role', role);
}

async assignRole(role: string, assignee: AgentPubKey): Promise<void> {
return this.callZome('assign_role', {
role,
assignee,
});
}

async requestUnassignRole(
role: string,
assignee: AgentPubKey,
): Promise<void> {
await this.callZome('request_unassign_role', {
role,
assignee,
});
}

async unassignMyRole(role: string): Promise<void> {
return this.callZome('unassign_my_role', role);
}
}
80 changes: 72 additions & 8 deletions zomes/coordinator/roles/src/assignees.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use hdk::prelude::*;
use roles_integrity::*;

use crate::role_claim::query_undeleted_role_claims_for_role;

#[derive(Serialize, Deserialize, Debug)]
pub struct AssignRoleInput {
pub role: String,
Expand All @@ -24,21 +26,83 @@ pub fn assign_role(input: AssignRoleInput) -> ExternResult<()> {
Ok(())
}

fn pending_unassignments_path() -> Path {
Path::from("pending_unassignments")
}

#[hdk_extern]
pub fn request_unassign_role(input: RequestUnassignRoleInput) -> ExternResult<()> {
let links = get_assignees_for_role(input.role.clone())?;

for link in links {
if let Some(target) = link.target.into_agent_pub_key() {
if target.eq(&input.assignee) {
delete_link(link.create_link_hash)?;
}
}
create_link(
pending_unassignments_path().path_entry_hash()?,
input.assignee.clone(),
LinkTypes::PendingUnassignments,
input.role,
)?;

Ok(())
}

#[hdk_extern]
pub fn unassign_my_role(pending_unassignment_link: ActionHash) -> ExternResult<()> {
let Some(record) = get(pending_unassignment_link.clone(), GetOptions::network())? else {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"PendingUnassigment link not found"
))));
};

let Action::CreateLink(create_link) = record.action() else {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"Record was not of CreateLink type"
))));
};

let Ok(Some(LinkTypes::PendingUnassignments)) =
LinkTypes::from_type(zome_info()?.id, create_link.link_type)
else {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"Invalid LinkType"
))));
};

let Ok(role) = String::from_utf8(create_link.tag.0.clone()) else {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"The pending unassigment link does not carry the role in its tag"
))));
};

let role_claim_records = query_undeleted_role_claims_for_role(role.clone())?;

if role_claim_records.len() == 0 {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"RoleClaim not found in our source chain"
))));
} else if role_claim_records.len() > 0 {
return Err(wasm_error!(WasmErrorInner::Guest(String::from(
"Unreachable: can't have more than one undeleted RoleClaim for the same role"
))));
}

for role_claim_record in role_claim_records {
let role_claim = RoleClaim::try_from(role_claim_record.clone())?;
delete_entry(role_claim_record.action_address().clone())?;
delete_link(role_claim.assign_role_create_link_hash)?;
}

delete_link(pending_unassignment_link)?;
Ok(())
}

#[hdk_extern]
pub fn get_pending_unassignments() -> ExternResult<Vec<Link>> {
get_links(
GetLinksInputBuilder::try_new(
pending_unassignments_path().path_entry_hash()?,
LinkTypes::PendingUnassignments,
)?
.build(),
)
}

#[hdk_extern]
pub fn get_assignees_for_role(role: String) -> ExternResult<Vec<Link>> {
let path = role_path(&role)?;
Expand Down
54 changes: 37 additions & 17 deletions zomes/coordinator/roles/src/role_claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,43 @@ pub fn get_role_claim(role_claim_hash: ActionHash) -> ExternResult<Option<Record
}
}

#[hdk_extern]
pub fn query_undeleted_role_claims_for_role(role: String) -> ExternResult<Vec<Record>> {
let filter = ChainQueryFilter::new()
.entry_type(UnitEntryTypes::RoleClaim.try_into()?)
.include_entries(true)
.action_type(ActionType::Create);
let records = query(filter)?;
let filter = ChainQueryFilter::new().action_type(ActionType::Delete);
let delete_records = query(filter)?;

let all_deleted_hashes = delete_records
.into_iter()
.map(|r| match r.action() {
Action::Delete(d) => Ok(d.deletes_address.clone()),
_ => Err(wasm_error!(WasmErrorInner::Guest(String::from(
"Invalid Delete action"
)))),
})
.collect::<ExternResult<Vec<ActionHash>>>()?;

let undeleted_records: Vec<Record> = records
.into_iter()
.filter(|r| !all_deleted_hashes.contains(r.action_address()))
.collect();

let records_for_role = undeleted_records
.into_iter()
.filter(|r| {
let Ok(role_claim) = RoleClaim::try_from(r) else {
return false;
};
role_claim.role.eq(&role)
})
.collect();

Ok(records_for_role)
}

pub fn delete_role_claim(original_role_claim_hash: ActionHash) -> ExternResult<ActionHash> {
delete_entry(original_role_claim_hash)
}
Expand All @@ -42,19 +78,3 @@ pub fn get_all_deletes_for_role_claim(
Details::Record(record_details) => Ok(Some(record_details.deletes)),
}
}

#[hdk_extern]
pub fn get_oldest_delete_for_role_claim(
original_role_claim_hash: ActionHash,
) -> ExternResult<Option<SignedActionHashed>> {
let Some(mut deletes) = get_all_deletes_for_role_claim(original_role_claim_hash)? else {
return Ok(None);
};
deletes.sort_by(|delete_a, delete_b| {
delete_a
.action()
.timestamp()
.cmp(&delete_b.action().timestamp())
});
Ok(deletes.first().cloned())
}
9 changes: 6 additions & 3 deletions zomes/integrity/roles/src/admins.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use hdi::prelude::*;

use crate::had_role_claim_at_the_time;
use crate::validate_agent_had_undeleted_role_claim_at_the_time;

pub const ADMIN_ROLE: &'static str = "ADMIN";

pub fn was_admin_at_the_time(agent: &AgentPubKey, chain_top: &ActionHash) -> ExternResult<bool> {
had_role_claim_at_the_time(agent, chain_top, &ADMIN_ROLE.to_string())
pub fn validate_agent_was_admin_at_the_time(
agent: &AgentPubKey,
chain_top: &ActionHash,
) -> ExternResult<ValidateCallbackResult> {
validate_agent_had_undeleted_role_claim_at_the_time(agent, chain_top, &ADMIN_ROLE.to_string())
}
Loading

0 comments on commit 49b46bb

Please sign in to comment.