Skip to content

Commit

Permalink
feat(sessions): implementing permission revoking
Browse files Browse the repository at this point in the history
  • Loading branch information
geekbrother committed Jul 12, 2024
1 parent d38c76e commit b06a332
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
58 changes: 58 additions & 0 deletions integration/sessions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ describe('Sessions/Permissions', () => {
onChainValidated: false
}
}
// New session PCI
let new_pci: string;
// New session signing (private) key
let signing_key: string;

it('create new session', async () => {
Expand Down Expand Up @@ -95,4 +97,60 @@ describe('Sessions/Permissions', () => {
)
expect(resp.status).toBe(200)
})

it('revoke PCI permission', async () => {
// Check PCI is exists
let resp = await httpClient.get(
`${baseUrl}/v1/sessions/${address}`
)
expect(resp.status).toBe(200)
expect(resp.data.pci.length).toBe(1)
expect(resp.data.pci[0]).toBe(new_pci)

// Create a signature
const privateKey = createPrivateKey({
key: Buffer.from(signing_key, 'base64'),
format: 'der',
type: 'sec1',
});

// Create a bad signature and try to revoke PCI
let bad_signature = createSign('SHA256');
bad_signature.update('bad_pci');
bad_signature.end();
let signature = bad_signature.sign(privateKey, 'base64');
let payload = {
pci: new_pci,
signature,
}
resp = await httpClient.post(
`${baseUrl}/v1/sessions/${address}/revoke`,
payload
)
expect(resp.status).toBe(401)

// Create a good signature and try to revoke PCI
const good_signature = createSign('SHA256');
good_signature.update(new_pci);
good_signature.end();
signature = good_signature.sign(privateKey, 'base64');
payload = {
pci: new_pci,
signature,
}

// Revoke PCI
resp = await httpClient.post(
`${baseUrl}/v1/sessions/${address}/revoke`,
payload
)
expect(resp.status).toBe(200)

// check PCI is revoked
resp = await httpClient.get(
`${baseUrl}/v1/sessions/${address}`
)
expect(resp.status).toBe(200)
expect(resp.data.pci.length).toBe(0)
})
})
9 changes: 9 additions & 0 deletions src/handlers/sessions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod context;
pub mod create;
pub mod get;
pub mod list;
pub mod revoke;

/// Payload to create a new permission
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -75,3 +76,11 @@ pub struct StoragePermissionsItem {
context: Option<PermissionContextItem>,
verification_key: String,
}

/// Permission revoke request schema
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionRevokeRequest {
pci: String,
signature: String,
}
61 changes: 61 additions & 0 deletions src/handlers/sessions/revoke.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use {
super::{super::HANDLER_TASK_METRICS, PermissionRevokeRequest, StoragePermissionsItem},
crate::{
error::RpcError,
state::AppState,
utils::crypto::{disassemble_caip10, verify_ecdsa_signature},
},
axum::{
extract::{Path, State},
response::Response,
Json,
},
std::{sync::Arc, time::SystemTime},
wc::future::FutureExt,
};

pub async fn handler(
state: State<Arc<AppState>>,
address: Path<String>,
Json(request_payload): Json<PermissionRevokeRequest>,
) -> Result<Response, RpcError> {
handler_internal(state, address, request_payload)
.with_metrics(HANDLER_TASK_METRICS.with_name("sessions_revoke"))
.await
}

#[tracing::instrument(skip(state), level = "debug")]
async fn handler_internal(
state: State<Arc<AppState>>,
Path(address): Path<String>,
request_payload: PermissionRevokeRequest,
) -> Result<Response, RpcError> {
let irn_client = state.irn.as_ref().ok_or(RpcError::IrnNotConfigured)?;

// Checking the CAIP-10 address format
disassemble_caip10(&address)?;

// Get the PCI object from the IRN
let irn_call_start = SystemTime::now();
let storage_permissions_item = irn_client
.hget(address.clone(), request_payload.pci.clone())
.await?
.ok_or_else(|| RpcError::PermissionNotFound(request_payload.pci.clone()))?;
state.metrics.add_irn_latency(irn_call_start, "hget".into());
let storage_permissions_item =
serde_json::from_str::<StoragePermissionsItem>(&storage_permissions_item)?;

// Check the signature
verify_ecdsa_signature(
&request_payload.pci,
&request_payload.signature,
&storage_permissions_item.verification_key,
)?;

// Remove the session/permission item from the IRN
let irn_call_start = SystemTime::now();
irn_client.hdel(address, request_payload.pci).await?;
state.metrics.add_irn_latency(irn_call_start, "hdel".into());

Ok(Response::default())
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ pub async fn bootstrap(config: Config) -> RpcResult<()> {
.route("/v1/sessions/:address", get(handlers::sessions::list::handler))
.route("/v1/sessions/:address/:pci", get(handlers::sessions::get::handler))
.route("/v1/sessions/:address/context", post(handlers::sessions::context::handler))
.route("/v1/sessions/:address/revoke", post(handlers::sessions::revoke::handler))
// Health
.route("/health", get(handlers::health::handler))
.route_layer(tracing_and_metrics_layer)
Expand Down

0 comments on commit b06a332

Please sign in to comment.