use std::collections::{HashMap, HashSet}; use endr::ObjectID; use ridl::symm_encr::KeySecret; use crew_rs::{ AddParent, AddRole, CrewChange, CrewID, CrewRulesV1, CrewState, EntrustInfo, MakeStatement, MemberCredential, RevealSecret, RevealSecretToParent, ADMIN_INVITATION_ROLE, ADMIN_ROLE, CONTENT_SECRET, PARTICIPATION_SECRET, READER_ROLE, READ_INVITATION_ROLE, WRITER_ROLE, WRITE_INVITATION_ROLE, }; #[test] fn admin_can_add_reader() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .roles_of(&reader.signer().pub_id()) .contains(&READER_ROLE.to_string())); } #[test] fn reader_can_not_add_other_reader() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let other_reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( reader .sign_tx([CrewChange::AddRole(AddRole { to: other_reader.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn admin_can_add_writer() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .roles_of(&writer.signer().pub_id()) .contains(&WRITER_ROLE.to_string())); } #[test] fn writer_can_not_add_other_writer() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let other_writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( writer .sign_tx([CrewChange::AddRole(AddRole { to: other_writer.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn writer_can_not_add_reader() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( writer .sign_tx([CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn admin_can_reveal_participation_secret_to_current_writers_only() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let other_writer = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .get_shared_secret(PARTICIPATION_SECRET, &[writer]) .unwrap() .const_time_eq(&participation_secret)); let result = state2.apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: other_writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: other_writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, admin_invitations, writers and admins can receive participation secrets" ); } #[test] fn reader_can_not_get_participation_secret_revealed_to_writer() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state2 .get_shared_secret(PARTICIPATION_SECRET, &[reader]) .unwrap_err(), "No secret revelation found" ); } #[test] fn admin_can_not_reveal_participation_secret_to_reader() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: reader .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, admin_invitations, writers and admins can receive participation secrets" ); } #[test] fn admin_can_reveal_content_secret_to_current_writers_only() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let wrong_writer = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: writer.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .get_shared_secret(CONTENT_SECRET, &[writer]) .unwrap() .const_time_eq(&content_secret)); let result = state2.apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: wrong_writer.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: wrong_writer .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, readers, writers and admins can receive content secrets" ); } #[test] fn admin_cam_reveal_content_secret_to_current_readers_only() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let wrong_reader = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .get_shared_secret(CONTENT_SECRET, &[reader]) .unwrap() .const_time_eq(&content_secret)); let result = state2.apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: wrong_reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: wrong_reader .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, readers, writers and admins can receive content secrets" ); } #[test] fn writer_can_not_reveal_participation_secret_even_to_other_writer() { let admin = crate::MemberCredential::new_random(); let writer1 = crate::MemberCredential::new_random(); let writer2 = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer1.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer2.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( writer1 .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer2.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer2 .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only write_invitations, admin_invitations and admins can reveal participation secrets" ); } #[test] fn writer_can_reveal_content_secret_to_reader() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( writer .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .get_shared_secret(CONTENT_SECRET, &[reader]) .unwrap() .const_time_eq(&content_secret)); } #[test] fn reader_can_not_reveal_content_secret_to_other_reader() { let admin = crate::MemberCredential::new_random(); let reader1 = crate::MemberCredential::new_random(); let reader2 = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader1.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader2.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( reader1 .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader2.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader2 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, admin_invitations, writers and admins can reveal content secrets" ); } #[test] fn admin_can_entrust_secret_info_to_participation_secret() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let secret_info = litl::Val::str("info_secret"); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); // first reveal participation secret to writer let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); // then entrust secret info to participation secret let state3 = state2 .apply( admin .sign_tx([CrewChange::EntrustInfo(EntrustInfo { to_secret_kind: PARTICIPATION_SECRET.to_string(), info_id: "info".to_string(), info: participation_secret.encrypt(&secret_info), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state3 .get_entrusted_info(PARTICIPATION_SECRET, "info", &[writer]) .unwrap(), secret_info ); } #[test] fn writer_can_not_entrust_secret_info_to_participation_secret() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let secret_info = litl::Val::str("info_secret"); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); // first reveal participation secret to writer let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); // then entrust secret info to participation secret let result = state2.apply( writer .sign_tx([CrewChange::EntrustInfo(EntrustInfo { to_secret_kind: PARTICIPATION_SECRET.to_string(), info_id: "info".to_string(), info: participation_secret.encrypt(&secret_info), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can entrust info to participation secrets" ); } #[test] fn reader_can_not_entrust_secret_info_to_participation_secret() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let secret_info = litl::Val::str("info_secret"); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); // this is nonsensical because the reader can't even receive the participation secret // but we want to test that the reader can't entrust info to the participation secret let result = state1.apply( reader .sign_tx([CrewChange::EntrustInfo(EntrustInfo { to_secret_kind: PARTICIPATION_SECRET.to_string(), info_id: "info".to_string(), info: participation_secret.encrypt(&secret_info), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can entrust info to participation secrets" ); } #[test] fn admin_can_entrust_secret_info_to_content_secret() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let secret_info = litl::Val::str("info_secret"); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); // first reveal content secret to reader let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); // then entrust secret info to content secret let state3 = state2 .apply( admin .sign_tx([CrewChange::EntrustInfo(EntrustInfo { to_secret_kind: CONTENT_SECRET.to_string(), info_id: "info".to_string(), info: content_secret.encrypt(&secret_info), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state3 .get_entrusted_info(CONTENT_SECRET, "info", &[reader]) .unwrap(), secret_info ); } #[test] fn writer_can_entrust_secret_info_to_content_secret() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let secret_info = litl::Val::str("info_secret"); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); // first reveal content secret to writer let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: writer.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); // then entrust secret info to content secret (as writer) let state3 = state2 .apply( writer .sign_tx([CrewChange::EntrustInfo(EntrustInfo { to_secret_kind: CONTENT_SECRET.to_string(), info_id: "info".to_string(), info: content_secret.encrypt(&secret_info), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); // then reveal content secret to reader let state4 = state3 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader.pub_id().recipient.encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state4 .get_entrusted_info(CONTENT_SECRET, "info", &[reader]) .unwrap(), secret_info ); } #[test] fn admin_can_add_parent_crew() { let admin = crate::MemberCredential::new_random(); let parent_crew_id = CrewID(ObjectID::test_random()); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddParent(AddParent { parent: parent_crew_id, })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state2.parents.iter().cloned().collect::>(), vec![parent_crew_id] ); } #[test] fn writer_can_not_add_parent_crew() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let parent_crew_id = CrewID(ObjectID::test_random()); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( writer .sign_tx([CrewChange::AddParent(AddParent { parent: parent_crew_id, })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can add parent crews" ); } #[test] fn reader_can_not_add_parent_crew() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let parent_crew_id = CrewID(ObjectID::test_random()); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( reader .sign_tx([CrewChange::AddParent(AddParent { parent: parent_crew_id, })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can add parent crews" ); } #[test] fn admin_can_make_statement() { let admin = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::MakeStatement(MakeStatement { path: "statement_1".to_owned(), value: litl::Val::str("hello"), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state2 .statements_with_prefix("statement") .into_iter() .collect::>(), vec![("statement_1".to_owned(), litl::Val::str("hello"))] ); } #[test] fn writers_can_make_statement() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( writer .sign_tx([CrewChange::MakeStatement(MakeStatement { path: "statement_1".to_owned(), value: litl::Val::str("hello"), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert_eq!( state2 .statements_with_prefix("statement") .into_iter() .collect::>(), vec![("statement_1".to_owned(), litl::Val::str("hello"))] ); } #[test] fn readers_can_not_make_statement() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( reader .sign_tx([CrewChange::MakeStatement(MakeStatement { path: "statement_1".to_owned(), value: litl::Val::str("hello"), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins and writers can make statements" ); } #[test] fn admin_can_add_read_invitation_role_and_reveal_content_and_participation_secret_to_it() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: read_invitation_credential.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: read_invitation_credential .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(CONTENT_SECRET, &[read_invitation_credential.clone()]) .unwrap() .const_time_eq(&content_secret)); let state4 = state3 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: read_invitation_credential.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: read_invitation_credential .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state4 .get_shared_secret(PARTICIPATION_SECRET, &[read_invitation_credential]) .unwrap() .const_time_eq(&participation_secret)); } #[test] fn writer_can_not_add_read_invitation_role() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( writer .sign_tx([CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn reader_can_not_add_read_invitation_role() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( reader .sign_tx([CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn read_invitation_role_can_add_reader() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( read_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .roles_of(&reader.signer().pub_id()) .contains(&READER_ROLE.to_string())); } #[test] fn read_invitation_role_can_not_add_writer() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( read_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn read_invitation_can_not_add_admin() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( read_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: writer.pub_id(), role: ADMIN_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn read_invitation_role_can_reveal_content_to_current_readers_only() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let reader1 = crate::MemberCredential::new_random(); let reader2 = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( read_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: reader1.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( read_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader1.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader1 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(CONTENT_SECRET, &[reader1]) .unwrap() .const_time_eq(&content_secret)); let result = state3.apply( read_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader2.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader2 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, readers, writers and admins can receive content secrets" ); } #[test] fn read_invitation_can_not_reveal_participant_secret_not_even_to_current_writers() { let admin = crate::MemberCredential::new_random(); let read_invitation_credential = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: read_invitation_credential.pub_id(), role: READ_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( read_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only write_invitations, admin_invitations and admins can reveal participation secrets" ); } #[test] fn admin_can_add_write_invitation_role_and_reveal_participation_secret_and_content_secret_to_it() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state.apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: write_invitation_credential.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: write_invitation_credential .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(PARTICIPATION_SECRET, &[write_invitation_credential.clone()]) .unwrap() .const_time_eq(&participation_secret)); let state4 = state3 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: write_invitation_credential.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: write_invitation_credential .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state4 .get_shared_secret(CONTENT_SECRET, &[write_invitation_credential]) .unwrap() .const_time_eq(&content_secret)); } #[test] fn write_invitation_role_can_add_writer() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( write_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .roles_of(&writer.signer().pub_id()) .contains(&WRITER_ROLE.to_string())); } #[test] fn write_invitation_can_not_add_reader() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( write_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn write_invitation_role_cannot_add_admin() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let result = state1.apply( write_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: writer.pub_id(), role: ADMIN_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can change crew" ); } #[test] fn write_invitation_can_reveal_participation_secret_to_current_writers_only() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let writer1 = crate::MemberCredential::new_random(); let writer2 = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: writer1.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( write_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer1.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer1 .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(PARTICIPATION_SECRET, &[writer1]) .unwrap() .const_time_eq(&participation_secret)); let result = state3.apply( write_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer2.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer2 .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, admin_invitations, writers and admins can receive participation secrets" ); } #[test] fn write_invitation_can_reveal_content_secret_to_current_readers_only() { let admin = crate::MemberCredential::new_random(); let write_invitation_credential = crate::MemberCredential::new_random(); let reader1 = crate::MemberCredential::new_random(); let reader2 = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: write_invitation_credential.pub_id(), role: WRITE_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: reader1.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( write_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader1.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader1 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(CONTENT_SECRET, &[reader1]) .unwrap() .const_time_eq(&content_secret)); let result = state3.apply( write_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader2.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader2 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, readers, writers and admins can receive content secrets" ); } #[test] fn admin_can_add_admin_invitation_role_and_reveal_participation_secret_and_content_secret_to_it() { let admin = crate::MemberCredential::new_random(); let admin_invitation_credential = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin_invitation_credential.pub_id(), role: ADMIN_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); assert!(state1 .roles_of(&admin_invitation_credential.signer().pub_id()) .contains(&ADMIN_INVITATION_ROLE.to_string())); let state2 = state1 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: admin_invitation_credential.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: admin_invitation_credential .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .get_shared_secret(PARTICIPATION_SECRET, &[admin_invitation_credential.clone()]) .unwrap() .const_time_eq(&participation_secret)); let state3 = state2 .apply( admin .sign_tx([CrewChange::RevealSecret(RevealSecret { to: admin_invitation_credential.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: admin_invitation_credential .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(CONTENT_SECRET, &[admin_invitation_credential.clone()]) .unwrap() .const_time_eq(&content_secret)); } #[test] fn admin_invitation_can_add_admin() { let admin = crate::MemberCredential::new_random(); let admin_invitation_credential = crate::MemberCredential::new_random(); let admin2 = crate::MemberCredential::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin_invitation_credential.pub_id(), role: ADMIN_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin_invitation_credential .sign_tx([CrewChange::AddRole(AddRole { to: admin2.pub_id(), role: ADMIN_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state2 .roles_of(&admin2.signer().pub_id()) .contains(&ADMIN_ROLE.to_string())); } #[test] fn admin_invitation_can_reveal_participation_secret_to_current_writers_only() { let admin = crate::MemberCredential::new_random(); let admin_invitation_credential = crate::MemberCredential::new_random(); let writer1 = crate::MemberCredential::new_random(); let writer2 = crate::MemberCredential::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin_invitation_credential.pub_id(), role: ADMIN_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: writer1.pub_id(), role: WRITER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( admin_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer1.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer1 .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(PARTICIPATION_SECRET, &[writer1]) .unwrap() .const_time_eq(&participation_secret)); let result = state3.apply( admin_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: writer2.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer2 .pub_id() .recipient .encrypt_from_anon(&participation_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, admin_invitations, writers and admins can receive participation secrets" ); } #[test] fn admin_invitation_can_reveal_content_secret_to_current_readers_only() { let admin = crate::MemberCredential::new_random(); let admin_invitation_credential = crate::MemberCredential::new_random(); let reader1 = crate::MemberCredential::new_random(); let reader2 = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin_invitation_credential.pub_id(), role: ADMIN_INVITATION_ROLE.to_string(), }), &admin.signer().pub_id(), ); let state2 = state1 .apply( admin .sign_tx([CrewChange::AddRole(AddRole { to: reader1.pub_id(), role: READER_ROLE.to_string(), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); let state3 = state2 .apply( admin_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader1.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader1 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ) .unwrap(); assert!(state3 .get_shared_secret(CONTENT_SECRET, &[reader1]) .unwrap() .const_time_eq(&content_secret)); let result = state3.apply( admin_invitation_credential .sign_tx([CrewChange::RevealSecret(RevealSecret { to: reader2.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader2 .pub_id() .recipient .encrypt_from_anon(&content_secret), })]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only read_invitations, write_invitations, readers, writers and admins can receive content secrets" ); } #[test] fn admins_can_reveal_content_and_participation_secrets_to_parents() { let admin = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: admin.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: admin.pub_id().recipient.encrypt_from_anon(&content_secret), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: admin.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: admin .pub_id() .recipient .encrypt_from_anon(&participation_secret), }), &admin.signer().pub_id(), ); let parent_id = CrewID(ObjectID::test_random()); let parent_content_secret = KeySecret::new_random(); let parent_participation_secret = KeySecret::new_random(); let state2 = state1 .apply( admin .sign_tx([ CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: CONTENT_SECRET.to_owned(), parent_secret_id: parent_content_secret.id, parent_id, encr: parent_content_secret.encrypt(&content_secret), }), CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: PARTICIPATION_SECRET.to_owned(), parent_secret_id: parent_participation_secret.id, parent_id, encr: parent_participation_secret.encrypt(&participation_secret), }), ]) .as_test_entry(), CrewRulesV1, ) .unwrap(); } #[test] fn writers_can_not_reveal_content_or_participation_secrets_to_parents() { let admin = crate::MemberCredential::new_random(); let writer = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: writer.pub_id(), role: WRITER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: writer.pub_id().recipient.encrypt_from_anon(&content_secret), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: writer.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: writer .pub_id() .recipient .encrypt_from_anon(&participation_secret), }), &admin.signer().pub_id(), ); let parent_id = CrewID(ObjectID::test_random()); let parent_content_secret = KeySecret::new_random(); let parent_participation_secret = KeySecret::new_random(); let result = state1.apply( writer .sign_tx([ CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: CONTENT_SECRET.to_owned(), parent_secret_id: parent_content_secret.id, parent_id, encr: parent_content_secret.encrypt(&content_secret), }), CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: PARTICIPATION_SECRET.to_owned(), parent_secret_id: parent_participation_secret.id, parent_id, encr: parent_participation_secret.encrypt(&participation_secret), }), ]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can reveal secrets to parents" ); } #[test] pub fn readers_can_not_reveal_content_participation_secrets_to_parents() { let admin = crate::MemberCredential::new_random(); let reader = crate::MemberCredential::new_random(); let content_secret = KeySecret::new_random(); let participation_secret = KeySecret::new_random(); let empty_state = CrewState::new(); let state1 = empty_state .apply_unchecked( &CrewChange::AddRole(AddRole { to: admin.pub_id(), role: ADMIN_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::AddRole(AddRole { to: reader.pub_id(), role: READER_ROLE.to_string(), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: CONTENT_SECRET.to_string(), encr: reader.pub_id().recipient.encrypt_from_anon(&content_secret), }), &admin.signer().pub_id(), ) .apply_unchecked( &CrewChange::RevealSecret(RevealSecret { to: reader.signer().pub_id(), secret_kind: PARTICIPATION_SECRET.to_string(), encr: reader .pub_id() .recipient .encrypt_from_anon(&participation_secret), }), &admin.signer().pub_id(), ); let parent_id = CrewID(ObjectID::test_random()); let parent_content_secret = KeySecret::new_random(); let parent_participation_secret = KeySecret::new_random(); let result = state1.apply( reader .sign_tx([ CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: CONTENT_SECRET.to_owned(), parent_secret_id: parent_content_secret.id, parent_id, encr: parent_content_secret.encrypt(&content_secret), }), CrewChange::RevealSecretToParent(RevealSecretToParent { secret_kind: PARTICIPATION_SECRET.to_owned(), parent_secret_id: parent_participation_secret.id, parent_id, encr: parent_participation_secret.encrypt(&participation_secret), }), ]) .as_test_entry(), CrewRulesV1, ); assert_eq!( result.unwrap_err(), "Error in change 0 in tx: Only admins can reveal secrets to parents" ); }