v1.0.1
This commit is contained in:
parent
212d64abfa
commit
b8e17df2db
17 changed files with 1145 additions and 732 deletions
|
|
@ -77,10 +77,10 @@ class Two_Factor_Extended_Audit_Log {
|
|||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param string $action Action type.
|
||||
* @param string $description Event description.
|
||||
* @param int $user_id User ID (optional).
|
||||
* @param array $metadata Additional metadata (optional).
|
||||
* @param string $action Action type.
|
||||
* @param string $description Event description.
|
||||
* @param int $user_id User ID (optional).
|
||||
* @param array<string, mixed> $metadata Additional metadata (optional).
|
||||
*/
|
||||
public function log_event( string $action, string $description, int $user_id = 0, array $metadata = array() ): void {
|
||||
$logs = $this->get_logs();
|
||||
|
|
@ -176,6 +176,11 @@ class Two_Factor_Extended_Audit_Log {
|
|||
|
||||
// Check if failure was due to 2FA requirements.
|
||||
$enforcement = two_factor_extended()->get_enforcement();
|
||||
|
||||
if ( null === $enforcement ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$required = $enforcement->get_required_providers_for_user( $user->ID );
|
||||
|
||||
if ( ! empty( $required ) && ! $enforcement->user_meets_requirements( $user->ID, $required ) ) {
|
||||
|
|
@ -198,17 +203,26 @@ class Two_Factor_Extended_Audit_Log {
|
|||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $filters Optional filters (action, user_id, date_from, date_to).
|
||||
* @param array<string, mixed> $filters Optional filters (action, user_id, date_from, date_to).
|
||||
*
|
||||
* @return array Array of log entries.
|
||||
* @return array<int, array<string, mixed>> Array of log entries.
|
||||
*/
|
||||
public function get_logs( array $filters = array() ): array {
|
||||
$logs = get_option( self::OPTION_KEY, array() );
|
||||
$raw = get_option( self::OPTION_KEY, array() );
|
||||
|
||||
if ( ! is_array( $logs ) ) {
|
||||
if ( ! is_array( $raw ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Validate that each entry is an array.
|
||||
$logs = array();
|
||||
|
||||
foreach ( $raw as $entry ) {
|
||||
if ( is_array( $entry ) ) {
|
||||
$logs[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply filters.
|
||||
if ( ! empty( $filters ) ) {
|
||||
$logs = $this->filter_logs( $logs, $filters );
|
||||
|
|
@ -222,36 +236,51 @@ class Two_Factor_Extended_Audit_Log {
|
|||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $logs Log entries.
|
||||
* @param array $filters Filter criteria.
|
||||
* @param array<int, array<string, mixed>> $logs Log entries.
|
||||
* @param array<string, mixed> $filters Filter criteria.
|
||||
*
|
||||
* @return array Filtered logs.
|
||||
* @return array<int, array<string, mixed>> Filtered logs.
|
||||
*/
|
||||
private function filter_logs( array $logs, array $filters ): array {
|
||||
return array_filter(
|
||||
$logs,
|
||||
function ( $log ) use ( $filters ) {
|
||||
// Filter by action.
|
||||
if ( ! empty( $filters['action'] ) && $log['action'] !== $filters['action'] ) {
|
||||
return false;
|
||||
}
|
||||
return array_values(
|
||||
array_filter(
|
||||
$logs,
|
||||
function ( array $log ) use ( $filters ): bool {
|
||||
// Filter by action.
|
||||
if ( ! empty( $filters['action'] ) && ( $log['action'] ?? '' ) !== $filters['action'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter by user_id.
|
||||
if ( ! empty( $filters['user_id'] ) && $log['user_id'] !== (int) $filters['user_id'] ) {
|
||||
return false;
|
||||
}
|
||||
// Filter by user_id.
|
||||
$filter_user_id = isset( $filters['user_id'] ) && is_numeric( $filters['user_id'] ) ? (int) $filters['user_id'] : 0;
|
||||
$log_user_id = isset( $log['user_id'] ) && is_numeric( $log['user_id'] ) ? (int) $log['user_id'] : 0;
|
||||
|
||||
// Filter by date range.
|
||||
if ( ! empty( $filters['date_from'] ) && $log['timestamp'] < strtotime( $filters['date_from'] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( ! empty( $filters['user_id'] ) && $log_user_id !== $filter_user_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! empty( $filters['date_to'] ) && $log['timestamp'] > strtotime( $filters['date_to'] ) ) {
|
||||
return false;
|
||||
}
|
||||
// Filter by date range.
|
||||
$timestamp = isset( $log['timestamp'] ) && is_numeric( $log['timestamp'] ) ? (int) $log['timestamp'] : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
if ( ! empty( $filters['date_from'] ) && is_string( $filters['date_from'] ) ) {
|
||||
$from = strtotime( $filters['date_from'] );
|
||||
|
||||
if ( false !== $from && $timestamp < $from ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $filters['date_to'] ) && is_string( $filters['date_to'] ) ) {
|
||||
$to = strtotime( $filters['date_to'] );
|
||||
|
||||
if ( false !== $to && $timestamp > $to ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -277,8 +306,10 @@ class Two_Factor_Extended_Audit_Log {
|
|||
|
||||
$filtered_logs = array_filter(
|
||||
$logs,
|
||||
function ( $log ) use ( $cutoff_time ) {
|
||||
return $log['timestamp'] >= $cutoff_time;
|
||||
function ( array $log ) use ( $cutoff_time ): bool {
|
||||
$ts = isset( $log['timestamp'] ) && is_numeric( $log['timestamp'] ) ? (int) $log['timestamp'] : 0;
|
||||
|
||||
return $ts >= $cutoff_time;
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -290,7 +321,7 @@ class Two_Factor_Extended_Audit_Log {
|
|||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $filters Optional filters.
|
||||
* @param array<string, mixed> $filters Optional filters.
|
||||
*
|
||||
* @return string CSV content.
|
||||
*/
|
||||
|
|
@ -301,16 +332,20 @@ class Two_Factor_Extended_Audit_Log {
|
|||
$csv[] = array( 'Timestamp', 'Action', 'Description', 'User', 'Actor', 'IP Address' );
|
||||
|
||||
foreach ( $logs as $log ) {
|
||||
$user = $log['user_id'] ? get_userdata( $log['user_id'] ) : null;
|
||||
$actor = $log['actor_id'] ? get_userdata( $log['actor_id'] ) : null;
|
||||
$log_user_id = isset( $log['user_id'] ) && is_int( $log['user_id'] ) ? $log['user_id'] : 0;
|
||||
$log_actor_id = isset( $log['actor_id'] ) && is_int( $log['actor_id'] ) ? $log['actor_id'] : 0;
|
||||
$log_ts = isset( $log['timestamp'] ) && is_int( $log['timestamp'] ) ? $log['timestamp'] : null;
|
||||
|
||||
$user = $log_user_id ? get_userdata( $log_user_id ) : null;
|
||||
$actor = $log_actor_id ? get_userdata( $log_actor_id ) : null;
|
||||
|
||||
$csv[] = array(
|
||||
gmdate( 'Y-m-d H:i:s', $log['timestamp'] ),
|
||||
$log['action'],
|
||||
$log['description'],
|
||||
$log_ts ? gmdate( 'Y-m-d H:i:s', $log_ts ) : '',
|
||||
isset( $log['action'] ) && is_string( $log['action'] ) ? $log['action'] : '',
|
||||
isset( $log['description'] ) && is_string( $log['description'] ) ? $log['description'] : '',
|
||||
$user ? $user->user_login : '-',
|
||||
$actor ? $actor->user_login : 'System',
|
||||
$log['ip_address'],
|
||||
isset( $log['ip_address'] ) && is_string( $log['ip_address'] ) ? $log['ip_address'] : '',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -318,13 +353,17 @@ class Two_Factor_Extended_Audit_Log {
|
|||
ob_start();
|
||||
$handle = fopen( 'php://output', 'w' );
|
||||
|
||||
foreach ( $csv as $row ) {
|
||||
fputcsv( $handle, $row );
|
||||
if ( false !== $handle ) {
|
||||
foreach ( $csv as $row ) {
|
||||
fputcsv( $handle, $row );
|
||||
}
|
||||
|
||||
fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Simple CSV export
|
||||
}
|
||||
|
||||
fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Simple CSV export
|
||||
$output = ob_get_clean();
|
||||
|
||||
return ob_get_clean();
|
||||
return false !== $output ? $output : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -337,11 +376,11 @@ class Two_Factor_Extended_Audit_Log {
|
|||
private function get_client_ip(): string {
|
||||
$ip = '';
|
||||
|
||||
if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
|
||||
if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) && is_string( $_SERVER['HTTP_CLIENT_IP'] ) ) {
|
||||
$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
|
||||
} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
||||
} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && is_string( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
||||
$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
|
||||
} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
|
||||
} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) && is_string( $_SERVER['REMOTE_ADDR'] ) ) {
|
||||
$ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +395,7 @@ class Two_Factor_Extended_Audit_Log {
|
|||
* @param mixed $old_value Old value.
|
||||
* @param mixed $new_value New value.
|
||||
*
|
||||
* @return array Changed keys.
|
||||
* @return array<int, string|int> Changed keys.
|
||||
*/
|
||||
private function get_changed_keys( $old_value, $new_value ): array {
|
||||
if ( ! is_array( $old_value ) || ! is_array( $new_value ) ) {
|
||||
|
|
@ -379,7 +418,7 @@ class Two_Factor_Extended_Audit_Log {
|
|||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @return array Statistics.
|
||||
* @return array{total: int, by_action: array<string, int>, recent_count: int} Statistics.
|
||||
*/
|
||||
public function get_statistics(): array {
|
||||
$logs = $this->get_logs();
|
||||
|
|
@ -393,14 +432,17 @@ class Two_Factor_Extended_Audit_Log {
|
|||
$recent_cutoff = current_time( 'timestamp' ) - ( 7 * DAY_IN_SECONDS );
|
||||
|
||||
foreach ( $logs as $log ) {
|
||||
$action = isset( $log['action'] ) && is_string( $log['action'] ) ? $log['action'] : '';
|
||||
$ts = isset( $log['timestamp'] ) && is_numeric( $log['timestamp'] ) ? (int) $log['timestamp'] : 0;
|
||||
|
||||
// Count by action.
|
||||
if ( ! isset( $stats['by_action'][ $log['action'] ] ) ) {
|
||||
$stats['by_action'][ $log['action'] ] = 0;
|
||||
if ( ! isset( $stats['by_action'][ $action ] ) ) {
|
||||
$stats['by_action'][ $action ] = 0;
|
||||
}
|
||||
$stats['by_action'][ $log['action'] ]++;
|
||||
$stats['by_action'][ $action ]++;
|
||||
|
||||
// Count recent logs (last 7 days).
|
||||
if ( $log['timestamp'] >= $recent_cutoff ) {
|
||||
if ( $ts >= $recent_cutoff ) {
|
||||
$stats['recent_count']++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue