wpbridge/includes/API/ApiKeyManager.php
wenpai e9d28817fe style: phpcbf 自动格式化 — 全量 WPCS 3.x 规范对齐
74 文件 14,082 处自动修复:空格→Tab 缩进、括号间距、
函数声明空格、前置自增、尾逗号等纯格式化改动。
零逻辑变更,php -l + token 级对比验证通过。

新增 phpcs.xml.dist / phpstan.neon.dist 项目配置。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 00:43:46 +08:00

296 lines
6.6 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* API Key 管理器
*
* @package WPBridge
*/
namespace WPBridge\API;
use WPBridge\Core\Settings;
use WPBridge\Core\Logger;
use WPBridge\Security\Encryption;
// 防止直接访问
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* API Key 管理器类
*/
class ApiKeyManager {
/**
* 设置实例
*
* @var Settings
*/
private Settings $settings;
/**
* 构造函数
*
* @param Settings $settings 设置实例
*/
public function __construct( Settings $settings ) {
$this->settings = $settings;
}
/**
* 生成新的 API Key
*
* @param string $name Key 名称
* @param string|null $expires_at 过期时间
* @param array $permissions 权限列表
* @return array
*/
public function generate( string $name, ?string $expires_at = null, array $permissions = [] ): array {
// 权限检查
if ( ! current_user_can( 'manage_options' ) ) {
throw new \Exception( __( '权限不足', 'wpbridge' ) );
}
$api_key = Encryption::generate_token( 32 );
$key_id = 'key_' . wp_generate_uuid4();
$key_data = [
'id' => $key_id,
'name' => sanitize_text_field( $name ),
'key_hash' => password_hash( $api_key, PASSWORD_DEFAULT ), // 存储哈希而非明文
'key_prefix' => substr( $api_key, 0, 4 ) . '...' . substr( $api_key, -4 ), // 显示前4后4
'permissions' => $permissions,
'expires_at' => $expires_at,
'created_at' => current_time( 'mysql' ),
'created_by' => get_current_user_id(),
'last_used' => null,
'usage_count' => 0,
];
// 保存到设置
$api_settings = $this->settings->get( 'api', [] );
$api_settings['keys'] = $api_settings['keys'] ?? [];
$api_settings['keys'][] = $key_data;
$this->settings->set( 'api', $api_settings );
Logger::info(
'API Key 已创建',
[
'id' => $key_id,
'name' => $name,
]
);
return [
'id' => $key_id,
'name' => $name,
'key' => $api_key, // 只在创建时返回完整 key
'expires_at' => $expires_at,
'created_at' => $key_data['created_at'],
];
}
/**
* 获取所有 API Keys不含完整 key
*
* @return array
*/
public function get_all(): array {
$api_settings = $this->settings->get( 'api', [] );
$keys = $api_settings['keys'] ?? [];
return array_map(
function ( $key ) {
return [
'id' => $key['id'],
'name' => $key['name'],
'key_prefix' => $key['key_prefix'],
'permissions' => $key['permissions'] ?? [],
'expires_at' => $key['expires_at'],
'created_at' => $key['created_at'],
'last_used' => $key['last_used'],
'usage_count' => $key['usage_count'] ?? 0,
'is_expired' => $this->is_expired( $key ),
];
},
$keys
);
}
/**
* 获取单个 API Key
*
* @param string $key_id Key ID
* @return array|null
*/
public function get( string $key_id ): ?array {
$api_settings = $this->settings->get( 'api', [] );
$keys = $api_settings['keys'] ?? [];
foreach ( $keys as $key ) {
if ( $key['id'] === $key_id ) {
return [
'id' => $key['id'],
'name' => $key['name'],
'key_prefix' => $key['key_prefix'],
'permissions' => $key['permissions'] ?? [],
'expires_at' => $key['expires_at'],
'created_at' => $key['created_at'],
'last_used' => $key['last_used'],
'usage_count' => $key['usage_count'] ?? 0,
'is_expired' => $this->is_expired( $key ),
];
}
}
return null;
}
/**
* 删除 API Key
*
* @param string $key_id Key ID
* @return bool
*/
public function delete( string $key_id ): bool {
$api_settings = $this->settings->get( 'api', [] );
$keys = $api_settings['keys'] ?? [];
$new_keys = [];
foreach ( $keys as $key ) {
if ( $key['id'] !== $key_id ) {
$new_keys[] = $key;
}
}
if ( count( $new_keys ) === count( $keys ) ) {
return false;
}
$api_settings['keys'] = $new_keys;
$this->settings->set( 'api', $api_settings );
Logger::info( 'API Key 已删除', [ 'id' => $key_id ] );
return true;
}
/**
* 更新 API Key
*
* @param string $key_id Key ID
* @param array $data 更新数据
* @return bool
*/
public function update( string $key_id, array $data ): bool {
$api_settings = $this->settings->get( 'api', [] );
$keys = $api_settings['keys'] ?? [];
$found = false;
foreach ( $keys as $index => $key ) {
if ( $key['id'] === $key_id ) {
if ( isset( $data['name'] ) ) {
$keys[ $index ]['name'] = sanitize_text_field( $data['name'] );
}
if ( isset( $data['expires_at'] ) ) {
$keys[ $index ]['expires_at'] = $data['expires_at'];
}
if ( isset( $data['permissions'] ) ) {
$keys[ $index ]['permissions'] = $data['permissions'];
}
$found = true;
break;
}
}
if ( ! $found ) {
return false;
}
$api_settings['keys'] = $keys;
$this->settings->set( 'api', $api_settings );
Logger::info( 'API Key 已更新', [ 'id' => $key_id ] );
return true;
}
/**
* 记录 API Key 使用
*
* @param string $api_key API Key
*/
public function record_usage( string $api_key ): void {
$api_settings = $this->settings->get( 'api', [] );
$keys = $api_settings['keys'] ?? [];
foreach ( $keys as $index => $key ) {
if ( isset( $key['key_hash'] ) && password_verify( $api_key, $key['key_hash'] ) ) {
$keys[ $index ]['last_used'] = current_time( 'mysql' );
$keys[ $index ]['usage_count'] = ( $key['usage_count'] ?? 0 ) + 1;
break;
}
}
$api_settings['keys'] = $keys;
$this->settings->set( 'api', $api_settings );
}
/**
* 检查 Key 是否过期
*
* @param array $key Key 数据
* @return bool
*/
private function is_expired( array $key ): bool {
if ( empty( $key['expires_at'] ) ) {
return false;
}
return strtotime( $key['expires_at'] ) < time();
}
/**
* 撤销所有 API Keys
*
* @return int 撤销的数量
*/
public function revoke_all(): int {
$api_settings = $this->settings->get( 'api', [] );
$count = count( $api_settings['keys'] ?? [] );
$api_settings['keys'] = [];
$this->settings->set( 'api', $api_settings );
Logger::info( '所有 API Keys 已撤销', [ 'count' => $count ] );
return $count;
}
/**
* 获取统计信息
*
* @return array
*/
public function get_stats(): array {
$keys = $this->get_all();
$total = count( $keys );
$active = 0;
$expired = 0;
foreach ( $keys as $key ) {
if ( $key['is_expired'] ) {
++$expired;
} else {
++$active;
}
}
return [
'total' => $total,
'active' => $active,
'expired' => $expired,
];
}
}