Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- **内置登录方式控制** — 新增可热更新的密码登录开关,并继续与 Passkey 开关独立组合;关闭密码登录会同时关闭公开注册、激活重发、密码邀请接受、密码重置和外部身份密码绑定,未完成的密码第一因子 MFA flow 会在完成时重新检查策略,外部认证和 Passkey 登录不再被遗留的强制改密标记阻塞。后端仅在存在已启用外部认证 provider 时允许同时关闭密码与 Passkey,并阻止禁用或删除最后一个外部 provider,避免保存后失去全部登录入口。
- **远端节点连接生命周期审计** — reverse tunnel 连接、正常下线、异常断线和心跳超时现在会按 remote node / binding 聚合写入系统 audit;四条 streaming lane 的同时变化只产生一次节点级状态转换,并记录连接次数、中断次数、lane 数量、transport 和稳定 reason code,不包含 access key、secret、signature、URL 凭据或 token。

## [v0.5.0] - 2026-08-20

Expand Down
28 changes: 28 additions & 0 deletions crates/aster_drive_model/src/types/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ macro_rules! define_audit_action_list {
TagDelete,
TagAttach,
TagDetach,
RemoteNodeConnected,
RemoteNodeGracefulDisconnect,
RemoteNodeUnexpectedDisconnect,
RemoteNodeHeartbeatTimeout,
}
};
}
Expand Down Expand Up @@ -689,6 +693,14 @@ pub enum AuditAction {
TagAttach,
#[sea_orm(string_value = "tag_detach")]
TagDetach,
#[sea_orm(string_value = "remote_node_connected")]
RemoteNodeConnected,
#[sea_orm(string_value = "remote_node_graceful_disconnect")]
RemoteNodeGracefulDisconnect,
#[sea_orm(string_value = "remote_node_unexpected_disconnect")]
RemoteNodeUnexpectedDisconnect,
#[sea_orm(string_value = "remote_node_heartbeat_timeout")]
RemoteNodeHeartbeatTimeout,
}

impl AuditAction {
Expand Down Expand Up @@ -846,6 +858,10 @@ impl AuditAction {
Self::TagDelete => 146,
Self::TagAttach => 147,
Self::TagDetach => 148,
Self::RemoteNodeConnected => 149,
Self::RemoteNodeGracefulDisconnect => 150,
Self::RemoteNodeUnexpectedDisconnect => 151,
Self::RemoteNodeHeartbeatTimeout => 152,
}
}

Expand Down Expand Up @@ -1002,6 +1018,10 @@ impl AuditAction {
Self::TagDelete => "tag_delete",
Self::TagAttach => "tag_attach",
Self::TagDetach => "tag_detach",
Self::RemoteNodeConnected => "remote_node_connected",
Self::RemoteNodeGracefulDisconnect => "remote_node_graceful_disconnect",
Self::RemoteNodeUnexpectedDisconnect => "remote_node_unexpected_disconnect",
Self::RemoteNodeHeartbeatTimeout => "remote_node_heartbeat_timeout",
}
}

Expand Down Expand Up @@ -1158,6 +1178,10 @@ impl AuditAction {
"tag_delete" => Some(Self::TagDelete),
"tag_attach" => Some(Self::TagAttach),
"tag_detach" => Some(Self::TagDetach),
"remote_node_connected" => Some(Self::RemoteNodeConnected),
"remote_node_graceful_disconnect" => Some(Self::RemoteNodeGracefulDisconnect),
"remote_node_unexpected_disconnect" => Some(Self::RemoteNodeUnexpectedDisconnect),
"remote_node_heartbeat_timeout" => Some(Self::RemoteNodeHeartbeatTimeout),
_ => None,
}
}
Expand Down Expand Up @@ -1303,6 +1327,10 @@ impl AuditAction {
| Self::TagDelete
| Self::TagAttach
| Self::TagDetach => "tag",
Self::RemoteNodeConnected
| Self::RemoteNodeGracefulDisconnect
| Self::RemoteNodeUnexpectedDisconnect
| Self::RemoteNodeHeartbeatTimeout => "remote",
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions frontend-panel/src/i18n/locales/en/admin/audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@
"audit_action_trash_purge_all": "Emptied trash",
"audit_action_remote_enrollment_redeem": "Redeemed remote enrollment",
"audit_action_remote_enrollment_ack": "Acknowledged remote enrollment",
"audit_action_remote_node_connected": "Remote node connected",
"audit_action_remote_node_graceful_disconnect": "Remote node disconnected gracefully",
"audit_action_remote_node_unexpected_disconnect": "Remote node disconnected unexpectedly",
"audit_action_remote_node_heartbeat_timeout": "Remote node heartbeat timed out",
"audit_action_user_revoke_other_sessions": "Revoked other sessions",
"audit_action_user_revoke_session": "Revoked session",
"audit_action_user_update_preferences": "Updated user preferences",
Expand Down Expand Up @@ -225,6 +229,7 @@
"audit_presentation_mfa_email_code_sent": "{{method}} flow {{flow_id}}, expires in {{expires_in}}s",
"audit_presentation_wopi_user_info_updated": "{{app_key}} for file {{file_id}}, {{user_info_len}} bytes",
"audit_presentation_remote_enrollment_changed": "{{phase}} {{remote_node_name}}, enabled {{is_enabled}}",
"audit_presentation_remote_node_connection_lifecycle": "{{reason}} on {{transport}} (connection #{{generation}}, interruption #{{outage_generation}}, {{active_lanes}} active / {{lane_count}} lane(s))",
"audit_presentation_invitation_snapshot": "{{email}} status {{status}}, expires {{expires_at}}",
"audit_presentation_external_auth_unlinked": "{{provider_key}} identity {{subject}}",
"audit_presentation_follower_binding_synced": "{{name}} enabled {{is_enabled}}",
Expand Down
5 changes: 5 additions & 0 deletions frontend-panel/src/i18n/locales/zh/admin/audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@
"audit_action_trash_purge_all": "清空回收站",
"audit_action_remote_enrollment_redeem": "兑换远程节点注册",
"audit_action_remote_enrollment_ack": "确认远程节点注册",
"audit_action_remote_node_connected": "远程节点已连接",
"audit_action_remote_node_graceful_disconnect": "远程节点正常下线",
"audit_action_remote_node_unexpected_disconnect": "远程节点异常断线",
"audit_action_remote_node_heartbeat_timeout": "远程节点心跳超时",
"audit_action_user_revoke_other_sessions": "撤销其他登录会话",
"audit_action_user_revoke_session": "撤销登录会话",
"audit_action_user_update_preferences": "更新用户偏好",
Expand Down Expand Up @@ -225,6 +229,7 @@
"audit_presentation_mfa_email_code_sent": "{{method}} flow {{flow_id}},{{expires_in}} 秒后过期",
"audit_presentation_wopi_user_info_updated": "{{app_key}} 更新文件 {{file_id}} 的用户信息,{{user_info_len}} 字节",
"audit_presentation_remote_enrollment_changed": "{{phase}} {{remote_node_name}},启用 {{is_enabled}}",
"audit_presentation_remote_node_connection_lifecycle": "{{transport}} 上发生 {{reason}}(第 {{generation}} 次连接,第 {{outage_generation}} 次中断,{{active_lanes}} 条活跃 / 共 {{lane_count}} 条 lane)",
"audit_presentation_invitation_snapshot": "{{email}} 状态 {{status}},过期 {{expires_at}}",
"audit_presentation_external_auth_unlinked": "{{provider_key}} 身份 {{subject}}",
"audit_presentation_follower_binding_synced": "{{name}} 启用 {{is_enabled}}",
Expand Down
2 changes: 1 addition & 1 deletion frontend-panel/src/services/api.generated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/runtime/startup/primary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub async fn prepare_primary() -> Result<PreparedPrimaryRuntime> {

let remote_protocol = crate::runtime::PrimaryAppState::new_remote_protocol();
remote_protocol.set_persistence_db(common.database.clone());
remote_protocol.set_audit_runtime_config(runtime_config.clone());
remote_protocol.configure_tunnel_owner_directory(
common.database.clone(),
&common.cfg.deployment,
Expand Down
20 changes: 20 additions & 0 deletions src/services/ops/audit/details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,26 @@ pub struct FollowerBindingAuditDetails<'a> {
pub is_enabled: bool,
}

/// Stable, redacted context for a remote node reverse-tunnel lifecycle event.
///
/// The tunnel has multiple polling/streaming lanes, but the audit contract is
/// node/binding scoped. Generation counters let operators correlate a recovery
/// with the outage it follows without persisting credentials or URLs.
#[derive(Serialize)]
pub struct RemoteNodeConnectionAuditDetails<'a> {
pub remote_node_id: i64,
pub binding_id: i64,
pub transport: &'a str,
pub reason: &'a str,
pub generation: u64,
pub outage_generation: u64,
pub active_lanes: usize,
pub lane_count: usize,
pub observed_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub first_lane_id: Option<&'a str>,
}

#[derive(Serialize)]
pub struct FollowerObjectAuditDetails<'a> {
pub binding_id: i64,
Expand Down
22 changes: 12 additions & 10 deletions src/services/ops/audit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,23 @@ pub use details::{
MfaEmailCodeAuditDetails, PolicyGroupAuditDetails, PolicyGroupMigrationDetails,
PropertyAuditDetails, RemoteEnrollmentAuditDetails, RemoteIngressProfileAuditDetails,
RemoteIngressProfileDeleteAuditDetails, RemoteNodeAuditDetails,
RemoteNodeEnrollmentTokenAuditDetails, RemoteNodeParamTestAuditDetails,
ShareBatchDeleteDetails, ShareCreateAuditDetails, ShareDeleteAuditDetails, ShareUpdateDetails,
StoragePolicyActionAuditDetails, StoragePolicyAuditDetails, StoragePolicyPromotionAuditDetails,
TagAssignmentAuditDetails, TagAuditDetails, TaskRetryAuditDetails, TeamAuditDetails,
TeamCleanupAuditDetails, TeamMemberAddAuditDetails, TeamMemberRemoveAuditDetails,
TeamMemberUpdateAuditDetails, TrashPurgeAllAuditDetails, UploadCancelAuditDetails,
UserAvatarSourceAuditDetails, UserAvatarUploadAuditDetails, UserLoginAuditDetails,
UserMfaManageAuditDetails, UserPreferencesAuditDetails, UserProfileAuditDetails,
UserWopiInfoAuditDetails, WorkspaceTransferCopyDetails, WorkspaceTransferMoveDetails,
WorkspaceTransferScopeDetails, details,
RemoteNodeConnectionAuditDetails, RemoteNodeEnrollmentTokenAuditDetails,
RemoteNodeParamTestAuditDetails, ShareBatchDeleteDetails, ShareCreateAuditDetails,
ShareDeleteAuditDetails, ShareUpdateDetails, StoragePolicyActionAuditDetails,
StoragePolicyAuditDetails, StoragePolicyPromotionAuditDetails, TagAssignmentAuditDetails,
TagAuditDetails, TaskRetryAuditDetails, TeamAuditDetails, TeamCleanupAuditDetails,
TeamMemberAddAuditDetails, TeamMemberRemoveAuditDetails, TeamMemberUpdateAuditDetails,
TrashPurgeAllAuditDetails, UploadCancelAuditDetails, UserAvatarSourceAuditDetails,
UserAvatarUploadAuditDetails, UserLoginAuditDetails, UserMfaManageAuditDetails,
UserPreferencesAuditDetails, UserProfileAuditDetails, UserWopiInfoAuditDetails,
WorkspaceTransferCopyDetails, WorkspaceTransferMoveDetails, WorkspaceTransferScopeDetails,
details,
};
pub use filters::{AuditLogFilterQuery, AuditLogFilters};
pub use manager::{
AuditLogInput, log, log_with_db_and_config, log_with_details, log_with_transaction,
should_record, should_record_with_config,
};
pub use models::{AuditLogEntry, AuditPresentation, AuditPresentationMessage, TeamAuditEntryInfo};
pub use presentation::{sanitize_details, sanitize_entity_name};
pub use query::{cleanup_expired, query, query_team_entries};
Loading
Loading