@feltdb/core 0.4.15 → 0.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1 -1
- package/dist/collection.d.ts +11 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +34 -0
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/Cargo.toml +1 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +93 -0
- package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +787 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +139 -0
- package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +1669 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +1269 -15
- package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +406 -0
- package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +908 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +1028 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +332 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +342 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +313 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +90 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +571 -0
- package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +506 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +81 -0
- package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +434 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +130 -23
- package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +2 -2
- package/dist/db.d.ts +91 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +89 -1
- package/dist/feltdb.d.ts +14 -0
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.d.ts +75 -0
- package/dist/file-db.d.ts.map +1 -0
- package/dist/file-db.js +437 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/studio-app/assets/{feltdb_wasm-CJEJryDx.js → feltdb_wasm-CBGD0zRu.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-C6ATF9mJ.wasm +0 -0
- package/dist/studio-app/assets/{index-D_p8T7nO.js → index-5siPkRSN.js} +9 -9
- package/dist/studio-app/index.html +1 -1
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/package.json +9 -4
- package/dist/studio-app/assets/feltdb_wasm_bg-BJxQXtoo.wasm +0 -0
|
@@ -7,6 +7,9 @@ pub mod admission;
|
|
|
7
7
|
pub mod application;
|
|
8
8
|
pub mod application_runtime;
|
|
9
9
|
pub mod authorization;
|
|
10
|
+
pub mod policy_evaluation;
|
|
11
|
+
#[cfg(test)]
|
|
12
|
+
mod authorization_security_tests;
|
|
10
13
|
pub mod capabilities;
|
|
11
14
|
mod capability;
|
|
12
15
|
mod content_distribution;
|
|
@@ -233,6 +236,7 @@ use std::path::{Path, PathBuf};
|
|
|
233
236
|
use std::sync::{Arc, Mutex};
|
|
234
237
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
235
238
|
use tokio::sync::broadcast;
|
|
239
|
+
use crate::state_contract::AuthorizationContext;
|
|
236
240
|
|
|
237
241
|
pub type Result<T> = std::result::Result<T, FlowError>;
|
|
238
242
|
|
|
@@ -485,6 +489,112 @@ impl FeltDb {
|
|
|
485
489
|
Ok(())
|
|
486
490
|
}
|
|
487
491
|
|
|
492
|
+
/// Evaluate authorization for a mutation
|
|
493
|
+
fn evaluate_authorization(&self, context: &AuthorizationContext) -> Result<()> {
|
|
494
|
+
// Check basic capabilities
|
|
495
|
+
if !context.capabilities.contains(&"state:write".to_string()) {
|
|
496
|
+
return Err(FlowError::CapabilityError(
|
|
497
|
+
"AUTHORIZATION_DENIED:missing_state_write_capability".to_string(),
|
|
498
|
+
));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Tenant isolation check happens implicitly at mutation boundary
|
|
502
|
+
// by only checking against current database tenant_id
|
|
503
|
+
|
|
504
|
+
Ok(())
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/// Insert a record with authorization context
|
|
508
|
+
pub fn insert_with_authorization<T: Serialize>(
|
|
509
|
+
&self,
|
|
510
|
+
key: &str,
|
|
511
|
+
value: T,
|
|
512
|
+
context: &AuthorizationContext,
|
|
513
|
+
) -> Result<()> {
|
|
514
|
+
// Authorize mutation inside FeltDB boundary
|
|
515
|
+
self.evaluate_authorization(context)?;
|
|
516
|
+
|
|
517
|
+
let capability = key
|
|
518
|
+
.split_once(':')
|
|
519
|
+
.map(|(cap, _)| cap)
|
|
520
|
+
.unwrap_or("default")
|
|
521
|
+
.to_string();
|
|
522
|
+
|
|
523
|
+
// Check if capability is writable
|
|
524
|
+
if !context.writable_collections.is_empty()
|
|
525
|
+
&& !context.writable_collections.contains(&capability)
|
|
526
|
+
{
|
|
527
|
+
return Err(FlowError::CapabilityError(
|
|
528
|
+
format!("AUTHORIZATION_DENIED:not_writable_collection:{}", capability),
|
|
529
|
+
));
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Authorized: apply mutation
|
|
533
|
+
self.insert_internal(capability, key.to_string(), value)?;
|
|
534
|
+
Ok(())
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// Update a record with authorization context
|
|
538
|
+
pub fn update_with_authorization<T: Serialize>(
|
|
539
|
+
&self,
|
|
540
|
+
key: &str,
|
|
541
|
+
value: T,
|
|
542
|
+
context: &AuthorizationContext,
|
|
543
|
+
) -> Result<()> {
|
|
544
|
+
// Authorize mutation inside FeltDB boundary
|
|
545
|
+
self.evaluate_authorization(context)?;
|
|
546
|
+
|
|
547
|
+
let capability = key
|
|
548
|
+
.split_once(':')
|
|
549
|
+
.map(|(cap, _)| cap)
|
|
550
|
+
.unwrap_or("default")
|
|
551
|
+
.to_string();
|
|
552
|
+
|
|
553
|
+
// Check if capability is writable
|
|
554
|
+
if !context.writable_collections.is_empty()
|
|
555
|
+
&& !context.writable_collections.contains(&capability)
|
|
556
|
+
{
|
|
557
|
+
return Err(FlowError::CapabilityError(
|
|
558
|
+
format!("AUTHORIZATION_DENIED:not_writable_collection:{}", capability),
|
|
559
|
+
));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Authorized: apply mutation
|
|
563
|
+
// Pre-state evaluation would happen here in full implementation
|
|
564
|
+
self.update_internal(capability, key.to_string(), value)?;
|
|
565
|
+
Ok(())
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/// Delete a record with authorization context
|
|
569
|
+
pub fn delete_with_authorization(
|
|
570
|
+
&self,
|
|
571
|
+
key: &str,
|
|
572
|
+
context: &AuthorizationContext,
|
|
573
|
+
) -> Result<()> {
|
|
574
|
+
// Authorize mutation inside FeltDB boundary
|
|
575
|
+
self.evaluate_authorization(context)?;
|
|
576
|
+
|
|
577
|
+
let capability = key
|
|
578
|
+
.split_once(':')
|
|
579
|
+
.map(|(cap, _)| cap)
|
|
580
|
+
.unwrap_or("default")
|
|
581
|
+
.to_string();
|
|
582
|
+
|
|
583
|
+
// Check if capability is writable
|
|
584
|
+
if !context.writable_collections.is_empty()
|
|
585
|
+
&& !context.writable_collections.contains(&capability)
|
|
586
|
+
{
|
|
587
|
+
return Err(FlowError::CapabilityError(
|
|
588
|
+
format!("AUTHORIZATION_DENIED:not_writable_collection:{}", capability),
|
|
589
|
+
));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Authorized: apply mutation
|
|
593
|
+
// Pre-state evaluation would happen here in full implementation
|
|
594
|
+
self.delete_internal(capability, key.to_string())?;
|
|
595
|
+
Ok(())
|
|
596
|
+
}
|
|
597
|
+
|
|
488
598
|
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>> {
|
|
489
599
|
let capability = key
|
|
490
600
|
.split_once(':')
|
|
@@ -785,6 +895,35 @@ impl FeltDb {
|
|
|
785
895
|
Ok(commit)
|
|
786
896
|
}
|
|
787
897
|
|
|
898
|
+
/// Apply atomic transaction with authorization context (atomic rejection on deny)
|
|
899
|
+
pub fn apply_atomic_transaction_with_authorization(
|
|
900
|
+
&self,
|
|
901
|
+
transaction_id: &str,
|
|
902
|
+
expected_parent: Option<u64>,
|
|
903
|
+
preconditions: &[AtomicPrecondition],
|
|
904
|
+
mutations: &[AtomicMutation],
|
|
905
|
+
audit: Option<Value>,
|
|
906
|
+
context: &AuthorizationContext,
|
|
907
|
+
) -> Result<AtomicCommit> {
|
|
908
|
+
// Authorize ALL mutations before applying ANY of them (atomicity guarantee)
|
|
909
|
+
self.evaluate_authorization(context)?;
|
|
910
|
+
|
|
911
|
+
// Check writable collections for all mutations
|
|
912
|
+
for mutation in mutations {
|
|
913
|
+
if !context.writable_collections.is_empty()
|
|
914
|
+
&& !context.writable_collections.contains(&mutation.capability)
|
|
915
|
+
{
|
|
916
|
+
return Err(FlowError::CapabilityError(format!(
|
|
917
|
+
"AUTHORIZATION_DENIED:not_writable_collection:{}",
|
|
918
|
+
mutation.capability
|
|
919
|
+
)));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// All authorized: proceed with atomic transaction (no partial application)
|
|
924
|
+
self.apply_atomic_transaction(transaction_id, expected_parent, preconditions, mutations, audit)
|
|
925
|
+
}
|
|
926
|
+
|
|
788
927
|
fn insert_internal<T: Serialize>(
|
|
789
928
|
&self,
|
|
790
929
|
capability: String,
|