WOL 전용 토큰 API 추가
This commit is contained in:
@@ -41,6 +41,8 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
|
|||||||
- `public/api.php?action=fan`: 팬 모드와 PWM 저장/적용
|
- `public/api.php?action=fan`: 팬 모드와 PWM 저장/적용
|
||||||
- `public/api.php?action=wifi`: 허용된 WiFi/DHCP service 제어
|
- `public/api.php?action=wifi`: 허용된 WiFi/DHCP service 제어
|
||||||
- `public/api.php?action=wifi_alias`: MAC별 WiFi 호스트명 저장
|
- `public/api.php?action=wifi_alias`: MAC별 WiFi 호스트명 저장
|
||||||
|
- `public/api.php?action=wake_lan_status`: LAN 클라이언트 WOL 대상 상태 조회
|
||||||
|
- `public/api.php?action=wake_lan`: LAN 클라이언트에 WOL magic packet 전송
|
||||||
- `public/api.php?action=settings`: 설정 가능한 Control 동작값 조회
|
- `public/api.php?action=settings`: 설정 가능한 Control 동작값 조회
|
||||||
- `public/api.php?action=settings_save`: 설정 모달 값 저장
|
- `public/api.php?action=settings_save`: 설정 모달 값 저장
|
||||||
- `public/api.php?action=settings_reset`: 설정 모달 값을 기본값으로 초기화
|
- `public/api.php?action=settings_reset`: 설정 모달 값을 기본값으로 초기화
|
||||||
@@ -74,7 +76,7 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
|
|||||||
- `wifi_observed_sessions`: 5G WiFi/LAN client의 최초/마지막 감지 시간
|
- `wifi_observed_sessions`: 5G WiFi/LAN client의 최초/마지막 감지 시간
|
||||||
- `wifi_client_aliases`: MAC별 수동 WiFi 호스트명
|
- `wifi_client_aliases`: MAC별 수동 WiFi 호스트명
|
||||||
- `app_settings`: 설정 모달에서 저장한 Control 동작값 오버라이드
|
- `app_settings`: 설정 모달에서 저장한 Control 동작값 오버라이드
|
||||||
- `/home/seo/secret/control.php`: 앱 비밀번호, DB 설정, 선택적 배터리 설정
|
- `/home/seo/secret/control.php`: 앱 비밀번호, DB 설정, 선택적 배터리 설정, WOL 전용 API 토큰
|
||||||
- UPS/배터리 기능은 `/home/seo/secret/control.php`의 `battery.enabled`가 명시적으로 true일 때만 동작합니다. 현재 운영 기준은 UPS 제거 상태이므로 기본값은 false입니다.
|
- UPS/배터리 기능은 `/home/seo/secret/control.php`의 `battery.enabled`가 명시적으로 true일 때만 동작합니다. 현재 운영 기준은 UPS 제거 상태이므로 기본값은 false입니다.
|
||||||
|
|
||||||
## UPS/배터리 상태
|
## UPS/배터리 상태
|
||||||
@@ -216,6 +218,7 @@ control-wifi-observe.service
|
|||||||
## 보안
|
## 보안
|
||||||
|
|
||||||
- 로그인 세션과 CSRF token을 사용합니다.
|
- 로그인 세션과 CSRF token을 사용합니다.
|
||||||
|
- `/custom/wakeonlan` 서버 프록시가 쓰는 `wake_lan`, `wake_lan_status`만 전용 API 토큰으로 호출할 수 있습니다. 이 토큰은 해당 두 액션에만 유효하며, 일반 POST 제어는 기존 로그인 세션과 CSRF token을 계속 요구합니다.
|
||||||
- Rhymix 사이트 관리자 세션 또는 `/custom/common` 관리자 인증 쿠키가 확인되면 Control 내부 로그인 세션을 발급합니다.
|
- Rhymix 사이트 관리자 세션 또는 `/custom/common` 관리자 인증 쿠키가 확인되면 Control 내부 로그인 세션을 발급합니다.
|
||||||
- Control 로그아웃은 Control remember token과 세션을 지우고, `/custom/common` 공통 인증 쿠키도 함께 만료합니다.
|
- Control 로그아웃은 Control remember token과 세션을 지우고, `/custom/common` 공통 인증 쿠키도 함께 만료합니다.
|
||||||
- POST 조작 API는 CSRF 검증을 통과해야 합니다.
|
- POST 조작 API는 CSRF 검증을 통과해야 합니다.
|
||||||
|
|||||||
@@ -696,6 +696,26 @@ function signed_in(): bool
|
|||||||
return auto_login_from_cookie();
|
return auto_login_from_cookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function control_api_token_valid(string $scope): bool
|
||||||
|
{
|
||||||
|
global $controlSecretConfig;
|
||||||
|
|
||||||
|
$provided = (string)(
|
||||||
|
$_SERVER['HTTP_X_CONTROL_API_TOKEN']
|
||||||
|
?? $_POST['api_token']
|
||||||
|
?? ''
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($provided === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens = is_array($controlSecretConfig['api_tokens'] ?? null) ? $controlSecretConfig['api_tokens'] : [];
|
||||||
|
$expected = (string)($tokens[$scope] ?? '');
|
||||||
|
|
||||||
|
return $expected !== '' && hash_equals($expected, $provided);
|
||||||
|
}
|
||||||
|
|
||||||
function control_admin_session_from_rhymix_or_custom(): bool
|
function control_admin_session_from_rhymix_or_custom(): bool
|
||||||
{
|
{
|
||||||
static $checked = null;
|
static $checked = null;
|
||||||
|
|||||||
+47
-3
@@ -9,7 +9,11 @@ if (!$controlApiLibrary && session_status() !== PHP_SESSION_ACTIVE) {
|
|||||||
|
|
||||||
require_once __DIR__ . '/../config/config.php';
|
require_once __DIR__ . '/../config/config.php';
|
||||||
|
|
||||||
if (!$controlApiLibrary && !signed_in()) {
|
$controlApiAction = $_GET['action'] ?? $_POST['action'] ?? 'status';
|
||||||
|
$controlApiTokenScope = in_array($controlApiAction, ['wake_lan', 'wake_lan_status'], true) ? 'wake_lan' : '';
|
||||||
|
$controlApiTokenAuthenticated = $controlApiTokenScope !== '' && control_api_token_valid($controlApiTokenScope);
|
||||||
|
|
||||||
|
if (!$controlApiLibrary && !$controlApiTokenAuthenticated && !signed_in()) {
|
||||||
json_out([
|
json_out([
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'error' => 'login_required',
|
'error' => 'login_required',
|
||||||
@@ -2862,6 +2866,35 @@ function send_wake_on_lan(string $mac, string $broadcast = '192.168.50.255', int
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wake_lan_status(string $mac): array
|
||||||
|
{
|
||||||
|
$mac = strtolower(trim($mac));
|
||||||
|
if (!wifi_valid_mac($mac)) {
|
||||||
|
throw new InvalidArgumentException('bad_mac');
|
||||||
|
}
|
||||||
|
|
||||||
|
$leases = dnsmasq_leases();
|
||||||
|
$activeLanMacs = array_fill_keys(lan_fdb_macs(), true);
|
||||||
|
$observedLanMacs = array_fill_keys(observed_lan_macs($leases), true);
|
||||||
|
$known = isset($leases[$mac]) || isset($activeLanMacs[$mac]) || isset($observedLanMacs[$mac]);
|
||||||
|
|
||||||
|
if (!$known) {
|
||||||
|
throw new InvalidArgumentException('unknown_dhcp_client');
|
||||||
|
}
|
||||||
|
|
||||||
|
$lease = $leases[$mac] ?? [];
|
||||||
|
$isOnline = isset($activeLanMacs[$mac]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'mac' => $mac,
|
||||||
|
'ip' => $lease['ip'] ?? null,
|
||||||
|
'hostname' => $lease['hostname'] ?? null,
|
||||||
|
'state' => $isOnline ? 'on' : 'off',
|
||||||
|
'presence' => $isOnline ? 'fdb' : (isset($observedLanMacs[$mac]) ? 'observed' : 'lease'),
|
||||||
|
'wol_available' => !$isOnline,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function wifi_time_ms(string $value): int
|
function wifi_time_ms(string $value): int
|
||||||
{
|
{
|
||||||
if (preg_match('/(\d+)/', $value, $m)) {
|
if (preg_match('/(\d+)/', $value, $m)) {
|
||||||
@@ -3630,9 +3663,11 @@ function collect_snapshot(bool $applyFan = true): array
|
|||||||
|
|
||||||
function control_api_dispatch(): void
|
function control_api_dispatch(): void
|
||||||
{
|
{
|
||||||
$action = $_GET['action'] ?? $_POST['action'] ?? 'status';
|
global $controlApiAction, $controlApiTokenAuthenticated;
|
||||||
|
|
||||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
$action = $controlApiAction;
|
||||||
|
|
||||||
|
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && !$controlApiTokenAuthenticated) {
|
||||||
require_csrf();
|
require_csrf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3839,6 +3874,15 @@ function control_api_dispatch(): void
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($action === 'wake_lan_status') {
|
||||||
|
json_out([
|
||||||
|
'ok' => true,
|
||||||
|
'data' => [
|
||||||
|
'wake' => wake_lan_status((string)($_GET['mac'] ?? $_POST['mac'] ?? '')),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
if ($action === 'collect') {
|
if ($action === 'collect') {
|
||||||
json_out([
|
json_out([
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
|
|||||||
Reference in New Issue
Block a user