fix: adjust auth protocol to datastore protocol
This commit is contained in:
parent
f6e20a6d4f
commit
72138069dd
4 changed files with 1 additions and 162 deletions
|
|
@ -460,3 +460,4 @@ pub enum GatewayMsg {
|
||||||
/// Periodic nonce garbage collection tick.
|
/// Periodic nonce garbage collection tick.
|
||||||
NonceGcTick,
|
NonceGcTick,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::types::{NodeId, Signature};
|
|
||||||
|
|
||||||
// ─── ContentHash ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// A 32-byte blake3 digest used as content address for chunks and manifests.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub struct ContentHash(pub [u8; 32]);
|
|
||||||
|
|
||||||
impl fmt::Debug for ContentHash {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "ContentHash(")?;
|
|
||||||
for b in &self.0[..4] {
|
|
||||||
write!(f, "{:02x}", b)?;
|
|
||||||
}
|
|
||||||
write!(f, "\u{2026})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── DatastoreAction ────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// An action a client wants to perform on the datastore.
|
|
||||||
///
|
|
||||||
/// Carried inside a `SignedRequestPayload` for browser-relay auth (Auth Path 2).
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub enum DatastoreAction {
|
|
||||||
Put {
|
|
||||||
path: String,
|
|
||||||
content_hash: ContentHash,
|
|
||||||
size_bytes: u64,
|
|
||||||
tags: std::collections::BTreeMap<String, String>,
|
|
||||||
},
|
|
||||||
Get {
|
|
||||||
path: String,
|
|
||||||
},
|
|
||||||
Delete {
|
|
||||||
path: String,
|
|
||||||
},
|
|
||||||
List {
|
|
||||||
prefix: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── SignedRequestPayload ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// The signable payload of a client request.
|
|
||||||
///
|
|
||||||
/// Serialized canonically (serde_json) and signed by the client's ed25519 key.
|
|
||||||
/// Includes timestamp and nonce for replay protection.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct SignedRequestPayload {
|
|
||||||
pub action: DatastoreAction,
|
|
||||||
/// Unix timestamp in seconds.
|
|
||||||
pub timestamp: u64,
|
|
||||||
/// 16 random bytes — prevents replay within the timestamp window.
|
|
||||||
pub nonce: [u8; 16],
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── SignedRequest ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// A signed request envelope for browser-relay auth (Auth Path 2).
|
|
||||||
///
|
|
||||||
/// The relay forwards this opaquely — it cannot forge, modify, or replay it.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct SignedRequest {
|
|
||||||
pub payload: SignedRequestPayload,
|
|
||||||
/// The client's ed25519 public key.
|
|
||||||
pub public_key: NodeId,
|
|
||||||
/// ed25519 signature over the canonical serialization of `payload`.
|
|
||||||
pub signature: Signature,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── AccessControlList ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// The datastore's access control list.
|
|
||||||
///
|
|
||||||
/// Persisted as `acl.json` alongside the datastore's `storage_path`.
|
|
||||||
/// The owner always has implicit full access.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
pub struct AccessControlList {
|
|
||||||
/// The datastore owner's public key — always has full access.
|
|
||||||
pub owner: NodeId,
|
|
||||||
/// Explicitly authorized client keys.
|
|
||||||
pub authorized_keys: HashSet<NodeId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── AuthzResult ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// The outcome of an authorization check.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum AuthzResult {
|
|
||||||
Allowed,
|
|
||||||
Denied(DeniedReason),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Why a request was denied.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum DeniedReason {
|
|
||||||
/// The key is not in the ACL.
|
|
||||||
NotAuthorized,
|
|
||||||
/// The ed25519 signature is invalid.
|
|
||||||
InvalidSignature,
|
|
||||||
/// The request timestamp is outside the ±300s window.
|
|
||||||
RequestExpired,
|
|
||||||
/// The nonce has already been seen within the time window.
|
|
||||||
ReplayDetected,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── AuthzEngine ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/// Authorization engine — checks requests against the ACL and replay state.
|
|
||||||
///
|
|
||||||
/// Sits at the edge of the actor system (Auth Gate) and decides whether
|
|
||||||
/// to accept or reject external requests before they reach the actors.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AuthzEngine {
|
|
||||||
pub acl: AccessControlList,
|
|
||||||
// Nonce tracking and other runtime state will be added during implementation.
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuthzEngine {
|
|
||||||
/// Check whether a `NodeId` is authorized (connection-level, Auth Path 1).
|
|
||||||
pub fn check_node(&self, _node_id: &NodeId) -> AuthzResult {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify and authorize a signed request (Auth Path 2).
|
|
||||||
pub fn check_signed_request(&self, _request: &SignedRequest) -> AuthzResult {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Grant access to a `NodeId`. Owner-only operation.
|
|
||||||
pub fn grant(&mut self, _requester: &NodeId, _key: NodeId) -> Result<(), DeniedReason> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Revoke access from a `NodeId`. Owner-only operation.
|
|
||||||
pub fn revoke(&mut self, _requester: &NodeId, _key: NodeId) -> Result<(), DeniedReason> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use ed25519_dalek::{Signer, Verifier};
|
use ed25519_dalek::{Signer, Verifier};
|
||||||
|
|
||||||
use crate::auth::{SignedRequest, SignedRequestPayload};
|
|
||||||
use crate::types::{DirectoryEntry, DirectoryEntryPayload, NodeId, Signature};
|
use crate::types::{DirectoryEntry, DirectoryEntryPayload, NodeId, Signature};
|
||||||
|
|
||||||
// ─── Keypair ────────────────────────────────────────────────────────────────
|
// ─── Keypair ────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -42,13 +41,6 @@ impl Keypair {
|
||||||
Signature(sig.to_bytes())
|
Signature(sig.to_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a request payload, returning a complete `SignedRequest` envelope.
|
|
||||||
///
|
|
||||||
/// Used by clients for Auth Path 2 (browser relay).
|
|
||||||
pub fn sign_request(&self, _payload: &SignedRequestPayload) -> SignedRequest {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sign a directory entry payload, returning a complete `DirectoryEntry`.
|
/// Sign a directory entry payload, returning a complete `DirectoryEntry`.
|
||||||
pub fn sign_directory_entry(
|
pub fn sign_directory_entry(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -82,14 +74,6 @@ pub fn verify(node_id: &NodeId, msg: &[u8], sig: &Signature) -> bool {
|
||||||
vk.verify(msg, &signature).is_ok()
|
vk.verify(msg, &signature).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify a `SignedRequest`'s signature against its embedded `public_key`.
|
|
||||||
///
|
|
||||||
/// Checks only signature validity — does NOT check timestamp, nonce, or ACL.
|
|
||||||
/// Use `AuthzEngine::check_signed_request` for full verification.
|
|
||||||
pub fn verify_signed_request(_request: &SignedRequest) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify a `DirectoryEntry`'s signature against its embedded `node_id`.
|
/// Verify a `DirectoryEntry`'s signature against its embedded `node_id`.
|
||||||
pub fn verify_directory_entry(entry: &DirectoryEntry) -> bool {
|
pub fn verify_directory_entry(entry: &DirectoryEntry) -> bool {
|
||||||
let payload = entry.payload();
|
let payload = entry.payload();
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,3 @@ pub mod snapshot;
|
||||||
pub mod driver;
|
pub mod driver;
|
||||||
#[cfg(feature = "iroh")]
|
#[cfg(feature = "iroh")]
|
||||||
pub mod iroh_driver;
|
pub mod iroh_driver;
|
||||||
pub mod auth;
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue