1use crate::{
4 error::{PhalanxError, Result},
5 identity::{Identity, PublicKey},
6 message::{GroupMessage, MessageContent, MessageType},
7 protocol::{ProtocolVersion, HandshakeMessage, KeyRotationMessage},
8 crypto::{SymmetricKey, hash_multiple},
9 key_manager::{AdvancedKeyManager, KeySet, RotationPolicy},
10};
11use std::collections::{HashMap, HashSet};
12use std::time::{SystemTime, UNIX_EPOCH};
13use x25519_dalek::PublicKey as X25519PublicKey;
14
15#[cfg(feature = "serde")]
16use serde::{Serialize, Deserialize};
17
18#[derive(Debug, Clone)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct GroupConfig {
22 pub max_members: usize,
24 pub key_rotation_interval: u64,
26 pub persistent_storage: bool,
28 pub visibility: GroupVisibility,
30 pub admin_only_operations: Vec<GroupOperation>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37pub enum GroupVisibility {
38 Public,
40 Private,
42 InviteOnly,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49pub enum GroupOperation {
50 AddMember,
52 RemoveMember,
54 ChangeSettings,
56 RotateKeys,
58 DeleteMessage,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65pub enum MemberRole {
66 Member,
68 Admin,
70 Owner,
72 Moderator,
74 Guest,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81pub enum MemberStatus {
82 Active,
84 Muted,
86 Banned,
88 Pending,
90 Suspended,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97pub enum GroupPermission {
98 SendMessages,
100 DeleteOwnMessages,
102 DeleteAnyMessages,
104 InviteMembers,
106 RemoveMembers,
108 ModifySettings,
110 ManageRoles,
112 RotateKeys,
114 ViewAuditLogs,
116 ManageBans,
118 CreateChannels,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
124#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
125pub enum MemberPresence {
126 Online,
128 Away,
130 DoNotDisturb,
132 Offline,
134 Invisible,
136}
137
138#[derive(Debug, Clone)]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141pub struct GroupMember {
142 pub public_key: PublicKey,
144 pub ephemeral_key: Option<X25519PublicKey>,
146 pub role: MemberRole,
148 pub joined_at: u64,
150 pub last_seen: Option<u64>,
152 pub nickname: Option<String>,
154 pub status: MemberStatus,
156 pub permissions: Vec<GroupPermission>,
158 pub presence: MemberPresence,
160}
161
162#[derive(Debug, Clone)]
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165pub struct MembershipProof {
166 pub proof_type: String,
168 pub proof_data: Vec<u8>,
170 pub signature: Option<ed25519_dalek::Signature>,
172}
173
174#[derive(Debug, Clone)]
176#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
177pub struct AuditLogEntry {
178 pub timestamp: u64,
180 pub actor: [u8; 32],
182 pub action: AuditAction,
184 pub target: Option<[u8; 32]>,
186 pub details: Option<String>,
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
192#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
193pub enum AuditAction {
194 MemberJoined,
196 MemberLeft,
198 MemberRemoved,
200 RoleChanged,
202 MemberBanned,
204 MemberUnbanned,
206 KeysRotated,
208 SettingsChanged,
210 ChannelCreated,
212 ChannelDeleted,
214 MessageDeleted,
216}
217
218#[derive(Debug, Clone)]
220#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
221pub struct GroupChannel {
222 pub id: String,
224 pub name: String,
226 pub description: Option<String>,
228 pub members: HashSet<[u8; 32]>,
230 pub permissions: HashMap<[u8; 32], Vec<GroupPermission>>,
232 pub archived: bool,
234 pub created_at: u64,
236 pub last_activity: Option<u64>,
238}
239
240#[derive(Debug, Clone)]
242#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
243pub struct BanEntry {
244 pub banned_at: u64,
246 pub banned_by: [u8; 32],
248 pub reason: Option<String>,
250 pub expires_at: Option<u64>,
252 pub ban_type: BanType,
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
259pub enum BanType {
260 Full,
262 ReadOnly,
264 Temporary,
266}
267
268#[derive(Debug)]
270pub struct PhalanxGroup {
271 group_id: [u8; 32],
273 identity: Identity,
275 group_key: SymmetricKey,
277 config: GroupConfig,
279 members: HashMap<[u8; 32], GroupMember>,
281 message_sequence: u64,
283 key_sequence: u64,
285 last_key_rotation: u64,
287 version: ProtocolVersion,
289 audit_log: Vec<AuditLogEntry>,
293 channels: HashMap<String, GroupChannel>,
295 banned_members: HashMap<[u8; 32], BanEntry>,
297}
298
299impl Default for GroupConfig {
300 fn default() -> Self {
301 Self {
302 max_members: 100,
303 key_rotation_interval: crate::constants::DEFAULT_KEY_ROTATION_INTERVAL,
304 persistent_storage: false,
305 visibility: GroupVisibility::Private,
306 admin_only_operations: vec![
307 GroupOperation::AddMember,
308 GroupOperation::RemoveMember,
309 GroupOperation::ChangeSettings,
310 GroupOperation::RotateKeys,
311 ],
312 }
313 }
314}
315
316impl PhalanxGroup {
317 pub fn new(identity: Identity) -> Self {
319 Self::with_config(identity, GroupConfig::default())
320 }
321
322 fn default_permissions_for_role(role: &MemberRole) -> Vec<GroupPermission> {
324 use GroupPermission::*;
325 match role {
326 MemberRole::Owner => vec![
327 SendMessages, DeleteOwnMessages, DeleteAnyMessages, InviteMembers,
328 RemoveMembers, ModifySettings, ManageRoles, RotateKeys, ViewAuditLogs,
329 ManageBans, CreateChannels
330 ],
331 MemberRole::Admin => vec![
332 SendMessages, DeleteOwnMessages, DeleteAnyMessages, InviteMembers,
333 RemoveMembers, ManageRoles, ViewAuditLogs, ManageBans, CreateChannels
334 ],
335 MemberRole::Moderator => vec![
336 SendMessages, DeleteOwnMessages, DeleteAnyMessages, ManageBans, ViewAuditLogs
337 ],
338 MemberRole::Member => vec![
339 SendMessages, DeleteOwnMessages
340 ],
341 MemberRole::Guest => vec![],
342 }
343 }
344
345 pub fn with_config(identity: Identity, config: GroupConfig) -> Self {
347 let group_id = Self::generate_group_id(&identity.public_key());
348 let group_key = SymmetricKey::generate();
349
350 let timestamp = SystemTime::now()
351 .duration_since(UNIX_EPOCH)
352 .map(|d| d.as_secs())
353 .unwrap_or(0);
354
355 let mut members = HashMap::new();
356 let creator_member = GroupMember {
357 public_key: identity.public_key(),
358 ephemeral_key: None,
359 role: MemberRole::Owner,
360 joined_at: timestamp,
361 last_seen: Some(timestamp),
362 nickname: None,
363 status: MemberStatus::Active,
364 permissions: Self::default_permissions_for_role(&MemberRole::Owner),
365 presence: MemberPresence::Online,
366 };
367
368 members.insert(identity.id(), creator_member);
369
370 let mut audit_log = Vec::new();
376 audit_log.push(AuditLogEntry {
377 timestamp,
378 actor: identity.id(),
379 action: AuditAction::MemberJoined,
380 target: Some(identity.id()),
381 details: Some("Group creator".to_string()),
382 });
383
384 Self {
385 group_id,
386 identity,
387 group_key,
388 config,
389 members,
390 message_sequence: 0,
391 key_sequence: 0,
392 last_key_rotation: timestamp,
393 version: ProtocolVersion::current(),
394 audit_log,
396 channels: HashMap::new(),
397 banned_members: HashMap::new(),
398 }
399 }
400
401 pub fn join(
403 identity: Identity,
404 handshake: HandshakeMessage,
405 group_key: SymmetricKey,
406 ) -> Result<Self> {
407 let payload = handshake.verify_and_decrypt()?;
408
409 let config = GroupConfig::default(); let timestamp = SystemTime::now()
412 .duration_since(UNIX_EPOCH)
413 .map(|d| d.as_secs())
414 .unwrap_or(0);
415
416 let mut members = HashMap::new();
417
418 let member = GroupMember {
420 public_key: identity.public_key(),
421 ephemeral_key: Some(handshake.ephemeral_key),
422 role: MemberRole::Member,
423 joined_at: timestamp,
424 last_seen: Some(timestamp),
425 nickname: None,
426 status: MemberStatus::Active,
427 permissions: Self::default_permissions_for_role(&MemberRole::Member),
428 presence: MemberPresence::Online,
429 };
430 members.insert(identity.id(), member);
431
432 let sender = GroupMember {
434 public_key: handshake.sender_key.clone(),
435 ephemeral_key: Some(handshake.ephemeral_key),
436 role: MemberRole::Owner, joined_at: timestamp, last_seen: Some(timestamp),
439 nickname: None,
440 status: MemberStatus::Active,
441 permissions: Self::default_permissions_for_role(&MemberRole::Owner),
442 presence: MemberPresence::Online,
443 };
444 members.insert(handshake.sender_key.id(), sender);
445
446 Ok(Self {
447 group_id: payload.group_id,
448 identity,
449 group_key,
450 config,
451 members,
452 message_sequence: 0,
453 key_sequence: 0,
454 last_key_rotation: timestamp,
455 version: ProtocolVersion::current(),
456 audit_log: Vec::new(),
458 channels: HashMap::new(),
459 banned_members: HashMap::new(),
460 })
461 }
462
463 pub fn group_id(&self) -> &[u8; 32] {
465 &self.group_id
466 }
467
468 pub fn identity(&self) -> &Identity {
470 &self.identity
471 }
472
473 pub fn config(&self) -> &GroupConfig {
475 &self.config
476 }
477
478 pub fn members(&self) -> &HashMap<[u8; 32], GroupMember> {
480 &self.members
481 }
482
483 pub fn get_member(&self, member_id: &[u8; 32]) -> Option<&GroupMember> {
485 self.members.get(member_id)
486 }
487
488 pub fn is_admin(&self) -> bool {
490 if let Some(member) = self.members.get(&self.identity.id()) {
491 matches!(member.role, MemberRole::Admin | MemberRole::Owner)
492 } else {
493 false
494 }
495 }
496
497 pub fn has_permission(&self, permission: &GroupPermission) -> bool {
499 if let Some(member) = self.members.get(&self.identity.id()) {
500 member.permissions.contains(permission)
501 } else {
502 false
503 }
504 }
505
506 pub fn add_member(&mut self, member_key: PublicKey, role: MemberRole) -> Result<()> {
508 if !self.has_permission(&GroupPermission::InviteMembers) {
510 return Err(PhalanxError::membership("No permission to add members"));
511 }
512
513 if self.banned_members.contains_key(&member_key.id()) {
515 return Err(PhalanxError::membership("Cannot add banned member"));
516 }
517
518 if self.members.len() >= self.config.max_members {
520 return Err(PhalanxError::group("Group is at maximum capacity"));
521 }
522
523 let timestamp = SystemTime::now()
524 .duration_since(UNIX_EPOCH)
525 .map(|d| d.as_secs())
526 .unwrap_or(0);
527
528 let member = GroupMember {
529 public_key: member_key.clone(),
530 ephemeral_key: None,
531 role: role.clone(),
532 joined_at: timestamp,
533 last_seen: None,
534 nickname: None,
535 status: MemberStatus::Active,
536 permissions: Self::default_permissions_for_role(&role),
537 presence: MemberPresence::Online,
538 };
539
540 let member_id = member_key.id();
541 self.members.insert(member_id, member);
542
543 self.audit_log.push(AuditLogEntry {
545 timestamp,
546 actor: self.identity.id(),
547 action: AuditAction::MemberJoined,
548 target: Some(member_id),
549 details: Some(format!("Added with role: {:?}", role)),
550 });
551
552 Ok(())
553 }
554
555 pub fn remove_member(&mut self, member_id: &[u8; 32]) -> Result<()> {
557 if self.config.admin_only_operations.contains(&GroupOperation::RemoveMember) && !self.is_admin() {
559 return Err(PhalanxError::membership("Only admins can remove members"));
560 }
561
562 if member_id == &self.identity.id() {
564 return Err(PhalanxError::membership("Cannot remove yourself"));
565 }
566
567 if let Some(member) = self.members.get(member_id) {
569 if member.role == MemberRole::Owner {
570 return Err(PhalanxError::membership("Cannot remove group owner"));
571 }
572 }
573
574 self.members.remove(member_id);
575
576 self.rotate_keys()?;
578
579 Ok(())
580 }
581
582 pub fn ban_member(&mut self, member_id: &[u8; 32], reason: Option<String>, duration: Option<u64>) -> Result<()> {
584 if !self.has_permission(&GroupPermission::ManageBans) {
585 return Err(PhalanxError::membership("No permission to ban members"));
586 }
587
588 let timestamp = SystemTime::now()
589 .duration_since(UNIX_EPOCH)
590 .map(|d| d.as_secs())
591 .unwrap_or(0);
592
593 self.members.remove(member_id);
595
596 let ban_entry = BanEntry {
598 banned_at: timestamp,
599 banned_by: self.identity.id(),
600 reason: reason.clone(),
601 expires_at: duration.map(|d| timestamp + d),
602 ban_type: BanType::Full,
603 };
604
605 self.banned_members.insert(*member_id, ban_entry);
606
607 self.audit_log.push(AuditLogEntry {
609 timestamp,
610 actor: self.identity.id(),
611 action: AuditAction::MemberBanned,
612 target: Some(*member_id),
613 details: reason,
614 });
615
616 Ok(())
617 }
618
619 pub fn unban_member(&mut self, member_id: &[u8; 32]) -> Result<()> {
621 if !self.has_permission(&GroupPermission::ManageBans) {
622 return Err(PhalanxError::membership("No permission to unban members"));
623 }
624
625 self.banned_members.remove(member_id);
626
627 let timestamp = SystemTime::now()
628 .duration_since(UNIX_EPOCH)
629 .map(|d| d.as_secs())
630 .unwrap_or(0);
631
632 self.audit_log.push(AuditLogEntry {
634 timestamp,
635 actor: self.identity.id(),
636 action: AuditAction::MemberUnbanned,
637 target: Some(*member_id),
638 details: None,
639 });
640
641 Ok(())
642 }
643
644 pub fn change_member_role(&mut self, member_id: &[u8; 32], new_role: MemberRole) -> Result<()> {
646 if !self.has_permission(&GroupPermission::ManageRoles) {
647 return Err(PhalanxError::membership("No permission to change member roles"));
648 }
649
650 let member = self.members.get_mut(member_id)
651 .ok_or_else(|| PhalanxError::membership("Member not found"))?;
652
653 let old_role = member.role.clone();
654 member.role = new_role.clone();
655 member.permissions = Self::default_permissions_for_role(&new_role);
656
657 let timestamp = SystemTime::now()
658 .duration_since(UNIX_EPOCH)
659 .map(|d| d.as_secs())
660 .unwrap_or(0);
661
662 self.audit_log.push(AuditLogEntry {
664 timestamp,
665 actor: self.identity.id(),
666 action: AuditAction::RoleChanged,
667 target: Some(*member_id),
668 details: Some(format!("{:?} -> {:?}", old_role, new_role)),
669 });
670
671 Ok(())
672 }
673
674 pub fn set_member_permissions(&mut self, member_id: &[u8; 32], permissions: Vec<GroupPermission>) -> Result<()> {
676 if !self.has_permission(&GroupPermission::ManageRoles) {
677 return Err(PhalanxError::membership("No permission to set member permissions"));
678 }
679
680 let member = self.members.get_mut(member_id)
681 .ok_or_else(|| PhalanxError::membership("Member not found"))?;
682
683 member.permissions = permissions;
684
685 Ok(())
686 }
687
688 pub fn create_channel(&mut self, channel_id: String, name: String, description: Option<String>) -> Result<()> {
690 if !self.has_permission(&GroupPermission::CreateChannels) {
691 return Err(PhalanxError::membership("No permission to create channels"));
692 }
693
694 if self.channels.contains_key(&channel_id) {
695 return Err(PhalanxError::group("Channel already exists"));
696 }
697
698 let timestamp = SystemTime::now()
699 .duration_since(UNIX_EPOCH)
700 .map(|d| d.as_secs())
701 .unwrap_or(0);
702
703 let channel = GroupChannel {
704 id: channel_id.clone(),
705 name,
706 description,
707 members: HashSet::new(),
708 permissions: HashMap::new(),
709 archived: false,
710 created_at: timestamp,
711 last_activity: None,
712 };
713
714 let channel_id_for_audit = channel_id.clone();
715 self.channels.insert(channel_id, channel);
716
717 self.audit_log.push(AuditLogEntry {
719 timestamp,
720 actor: self.identity.id(),
721 action: AuditAction::ChannelCreated,
722 target: None,
723 details: Some(format!("Channel: {}", channel_id_for_audit)),
724 });
725
726 Ok(())
727 }
728
729 pub fn add_member_to_channel(&mut self, channel_id: &str, member_id: &[u8; 32]) -> Result<()> {
731 if !self.has_permission(&GroupPermission::ManageRoles) {
732 return Err(PhalanxError::membership("No permission to manage channel membership"));
733 }
734
735 if !self.members.contains_key(member_id) {
737 return Err(PhalanxError::membership("Member not found in group"));
738 }
739
740 let channel = self.channels.get_mut(channel_id)
741 .ok_or_else(|| PhalanxError::group("Channel not found"))?;
742
743 channel.members.insert(*member_id);
744 Ok(())
745 }
746
747 pub fn set_presence(&mut self, presence: MemberPresence) -> Result<()> {
749 let member = self.members.get_mut(&self.identity.id())
750 .ok_or_else(|| PhalanxError::membership("Member not found"))?;
751
752 member.presence = presence;
753
754 let timestamp = SystemTime::now()
755 .duration_since(UNIX_EPOCH)
756 .map(|d| d.as_secs())
757 .unwrap_or(0);
758 member.last_seen = Some(timestamp);
759
760 Ok(())
761 }
762
763 pub fn get_audit_log(&self) -> Result<&Vec<AuditLogEntry>> {
765 if !self.has_permission(&GroupPermission::ViewAuditLogs) {
766 return Err(PhalanxError::membership("No permission to view audit logs"));
767 }
768 Ok(&self.audit_log)
769 }
770
771 pub fn advanced_key_rotation(&mut self) -> Result<()> {
773 if !self.has_permission(&GroupPermission::RotateKeys) {
774 return Err(PhalanxError::membership("No permission to rotate keys"));
775 }
776
777 self.group_key = SymmetricKey::generate();
779 self.key_sequence += 1;
780
781 let timestamp = SystemTime::now()
782 .duration_since(UNIX_EPOCH)
783 .map(|d| d.as_secs())
784 .unwrap_or(0);
785 self.last_key_rotation = timestamp;
786
787 self.audit_log.push(AuditLogEntry {
789 timestamp,
790 actor: self.identity.id(),
791 action: AuditAction::KeysRotated,
792 target: None,
793 details: Some(format!("Advanced rotation to version {}", self.key_sequence)),
794 });
795
796 Ok(())
797 }
798
799 pub fn schedule_key_rotation(&mut self, _policy: RotationPolicy) -> Result<()> {
801 if !self.has_permission(&GroupPermission::RotateKeys) {
802 return Err(PhalanxError::membership("No permission to schedule key rotation"));
803 }
804
805 Ok(())
807 }
808
809 pub fn get_health_metrics(&self) -> GroupHealthMetrics {
811 let timestamp = SystemTime::now()
812 .duration_since(UNIX_EPOCH)
813 .map(|d| d.as_secs())
814 .unwrap_or(0);
815
816 let active_members = self.members.values()
817 .filter(|m| {
818 if let Some(last_seen) = m.last_seen {
819 timestamp - last_seen < 3600 } else {
821 false
822 }
823 })
824 .count();
825
826 let banned_count = self.banned_members.len();
827 let channel_count = self.channels.len();
828
829 let key_rotation_health = if self.needs_key_rotation() {
830 HealthStatus::Warning
831 } else {
832 HealthStatus::Healthy
833 };
834
835 GroupHealthMetrics {
836 total_members: self.members.len(),
837 active_members,
838 banned_members: banned_count,
839 channels: channel_count,
840 key_rotation_health,
841 last_activity: timestamp,
842 }
843 }
844
845 pub fn encrypt_message(&mut self, content: &MessageContent) -> Result<GroupMessage> {
847 self.message_sequence += 1;
848
849 GroupMessage::new(
850 &self.identity,
851 MessageType::Text,
852 content,
853 self.message_sequence,
854 &self.group_key,
855 )
856 }
857
858 pub fn decrypt_message(&self, message: &GroupMessage) -> Result<MessageContent> {
860 let sender_id = message.sender.id();
862 if !self.members.contains_key(&sender_id) {
863 return Err(PhalanxError::membership("Message from non-member"));
864 }
865
866 message.decrypt(&self.group_key)
867 }
868
869 pub fn rotate_keys(&mut self) -> Result<KeyRotationMessage> {
871 if self.config.admin_only_operations.contains(&GroupOperation::RotateKeys) && !self.is_admin() {
873 return Err(PhalanxError::membership("Only admins can rotate keys"));
874 }
875
876 self.group_key = SymmetricKey::generate();
878 self.key_sequence += 1;
879
880 self.last_key_rotation = SystemTime::now()
882 .duration_since(UNIX_EPOCH)
883 .map(|d| d.as_secs())
884 .unwrap_or(0);
885
886 let mut member_keys = Vec::new();
888 for member in self.members.values_mut() {
889 if let Some(ephemeral) = &member.ephemeral_key {
890 member_keys.push((member.public_key.clone(), *ephemeral));
891 }
892 }
893
894 KeyRotationMessage::new(&self.identity, self.key_sequence, member_keys)
895 }
896
897 pub fn needs_key_rotation(&self) -> bool {
899 let current_time = SystemTime::now()
900 .duration_since(UNIX_EPOCH)
901 .map(|d| d.as_secs())
902 .unwrap_or(0);
903
904 current_time - self.last_key_rotation >= self.config.key_rotation_interval
905 }
906
907 pub fn update_member_activity(&mut self, member_id: &[u8; 32]) {
909 if let Some(member) = self.members.get_mut(member_id) {
910 let timestamp = SystemTime::now()
911 .duration_since(UNIX_EPOCH)
912 .map(|d| d.as_secs())
913 .unwrap_or(0);
914 member.last_seen = Some(timestamp);
915 }
916 }
917
918 pub fn set_member_nickname(&mut self, member_id: &[u8; 32], nickname: Option<String>) -> Result<()> {
920 let member = self.members.get_mut(member_id)
921 .ok_or_else(|| PhalanxError::membership("Member not found"))?;
922
923 member.nickname = nickname;
924 Ok(())
925 }
926
927 fn generate_group_id(creator_key: &PublicKey) -> [u8; 32] {
929 let timestamp = SystemTime::now()
930 .duration_since(UNIX_EPOCH)
931 .map(|d| d.as_millis() as u64)
932 .unwrap_or(0)
933 .to_be_bytes();
934 hash_multiple(&[
935 &creator_key.id(),
936 ×tamp,
937 b"PHALANX_GROUP_V1",
938 ])
939 }
940
941 pub fn create_handshake(&self) -> Result<HandshakeMessage> {
943 HandshakeMessage::new(
944 &self.identity,
945 self.group_id,
946 vec!["phalanx/v1".to_string()],
947 "phalanx-client/1.0".to_string(),
948 )
949 }
950
951 pub fn create_handshake_with_group_key(&mut self, recipient_public_key: &PublicKey) -> Result<HandshakeMessage> {
953 HandshakeMessage::new_with_group_key(
954 &mut self.identity,
955 recipient_public_key,
956 self.group_id,
957 vec!["phalanx/v1".to_string()],
958 "phalanx-client/1.0".to_string(),
959 &self.group_key,
960 )
961 }
962
963 pub fn join_with_handshake(
965 mut identity: Identity,
966 handshake: HandshakeMessage,
967 ) -> Result<Self> {
968 let group_key = handshake.extract_group_key(&mut identity)?
970 .ok_or_else(|| PhalanxError::protocol("No group key in handshake message"))?;
971
972 Self::join(identity, handshake, group_key)
974 }
975
976 pub fn stats(&self) -> GroupStats {
978 let active_members = self.members.values()
979 .filter(|m| {
980 if let Some(last_seen) = m.last_seen {
981 let now = SystemTime::now()
982 .duration_since(UNIX_EPOCH)
983 .map(|d| d.as_secs())
984 .unwrap_or(0);
985 now - last_seen < 3600 } else {
987 false
988 }
989 })
990 .count();
991
992 GroupStats {
993 total_members: self.members.len(),
994 active_members,
995 message_count: self.message_sequence,
996 key_rotation_count: self.key_sequence,
997 created_at: self.members.get(&self.identity.id())
998 .map(|m| m.joined_at)
999 .unwrap_or(0),
1000 }
1001 }
1002}
1003
1004#[derive(Debug, Clone)]
1006#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1007pub struct GroupStats {
1008 pub total_members: usize,
1010 pub active_members: usize,
1012 pub message_count: u64,
1014 pub key_rotation_count: u64,
1016 pub created_at: u64,
1018}
1019
1020#[derive(Debug, Clone)]
1022#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1023pub struct GroupHealthMetrics {
1024 pub total_members: usize,
1026 pub active_members: usize,
1028 pub banned_members: usize,
1030 pub channels: usize,
1032 pub key_rotation_health: HealthStatus,
1034 pub last_activity: u64,
1036}
1037
1038#[derive(Debug, Clone, PartialEq, Eq)]
1040#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1041pub enum HealthStatus {
1042 Healthy,
1044 Warning,
1046 Critical,
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use super::*;
1053 use crate::message::MessageContent;
1054
1055 #[test]
1056 fn test_group_creation() {
1057 let identity = Identity::generate();
1058 let group = PhalanxGroup::new(identity.clone());
1059
1060 assert_eq!(group.members.len(), 1);
1061 assert!(group.members.contains_key(&identity.id()));
1062 assert!(group.is_admin()); }
1064
1065 #[test]
1066 fn test_message_encryption_decryption() {
1067 let identity = Identity::generate();
1068 let mut group = PhalanxGroup::new(identity);
1069
1070 let content = MessageContent::text("Hello, group!");
1071 let encrypted = group.encrypt_message(&content).unwrap();
1072 let decrypted = group.decrypt_message(&encrypted).unwrap();
1073
1074 assert_eq!(decrypted.as_string().unwrap(), "Hello, group!");
1075 }
1076
1077 #[test]
1078 fn test_member_management() {
1079 let admin = Identity::generate();
1080 let member = Identity::generate();
1081
1082 let mut group = PhalanxGroup::new(admin);
1083
1084 group.add_member(member.public_key(), MemberRole::Member).unwrap();
1086 assert_eq!(group.members.len(), 2);
1087
1088 group.remove_member(&member.id()).unwrap();
1090 assert_eq!(group.members.len(), 1);
1092 }
1093
1094 #[test]
1095 fn test_key_rotation() {
1096 let identity = Identity::generate();
1097 let mut group = PhalanxGroup::new(identity);
1098
1099 let old_sequence = group.key_sequence;
1100 let rotation_msg = group.rotate_keys().unwrap();
1101
1102 assert_eq!(group.key_sequence, old_sequence + 1);
1103 assert!(rotation_msg.verify(&group.identity.public_key()).is_ok());
1104 }
1105
1106 #[test]
1107 fn test_permissions() {
1108 let admin = Identity::generate();
1109 let member = Identity::generate();
1110
1111 let mut group = PhalanxGroup::new(admin);
1112
1113 group.add_member(member.public_key(), MemberRole::Member).unwrap();
1115
1116 group.identity = member;
1118
1119 let new_member = Identity::generate();
1120 let result = group.add_member(new_member.public_key(), MemberRole::Member);
1121
1122 assert!(result.is_err());
1124 }
1125}