Require signed PWA auth for findmydevice

This commit is contained in:
seo committed 2026-08-03 05:06:31 +09:00
1 parent 1ba8c257a3
commit 76f9c7c549
3 files changed
+346 -56

No files matched your search

+91 -28
View File
@@ -18,33 +18,24 @@ if (function_exists('set_time_limit')) {
@set_time_limit(0);
}
function findmydevice_is_pwa_request(): bool
{
if (($_SERVER['HTTP_X_CUSTOM_PWA'] ?? '') !== 'findmydevice') {
return false;
}
$allowedHosts = ['chaegeon.com', 'www.chaegeon.com'];
foreach (['HTTP_ORIGIN', 'HTTP_REFERER'] as $key) {
$value = (string)($_SERVER[$key] ?? '');
if ($value === '') {
continue;
}
$host = parse_url($value, PHP_URL_HOST);
if ($host !== null && $host !== false && !in_array(strtolower($host), $allowedHosts, true)) {
return false;
}
}
return true;
}
function findmydevice_require_auth(): void
{
if (function_exists('custom_block_login_denied')) {
custom_block_login_denied(true);
}
if (function_exists('custom_has_auth_cookie') && custom_has_auth_cookie()) {
return;
}
if (
($_SERVER['HTTP_X_CUSTOM_PWA'] ?? '') === 'findmydevice'
&& function_exists('custom_has_pwa_auth_cookie')
&& custom_has_pwa_auth_cookie('findmydevice')
) {
return;
}
if (function_exists('custom_is_site_admin') && custom_is_site_admin()) {
if (function_exists('custom_issue_auth_cookie')) {
custom_issue_auth_cookie(true);
@@ -52,10 +43,6 @@ function findmydevice_require_auth(): void
return;
}
if (findmydevice_is_pwa_request()) {
return;
}
custom_json(['result' => 'unauthorized'], 401);
}
@@ -503,8 +490,6 @@ if ($startParam && $endParam) {
$endTime = (new DateTime())->setTimezone(new DateTimeZone('UTC'))->format($haFormat);
}
// API URL 생성
$historyEndpoint = "/history/period/{$startTime}?filter_entity_id=" . rawurlencode($entities['device']) . "&end_time={$endTime}";
$includeHistory = isset($_GET['history']) && $_GET['history'] === '1' && !isset($_GET['hashonly']);
// 공통 요청 함수
@@ -545,6 +530,39 @@ function findmydevice_starts_with(string $value, string $prefix): bool
{
return strncmp($value, $prefix, strlen($prefix)) === 0;
}
function findmydevice_watch_pair_key(string $key): ?string
{
if (!findmydevice_starts_with($key, 'watch') || strlen($key) <= 5) {
return null;
}
return lcfirst(substr($key, 5));
}
function findmydevice_pair_watch_sensor_rows(array $rows): array
{
$ordered = [];
foreach ($rows as $key => $value) {
if (findmydevice_watch_pair_key((string)$key) !== null) {
continue;
}
$ordered[$key] = $value;
$watchKey = 'watch' . ucfirst((string)$key);
if (array_key_exists($watchKey, $rows)) {
$ordered[$watchKey] = $rows[$watchKey];
}
}
foreach ($rows as $key => $value) {
if (!array_key_exists($key, $ordered)) {
$ordered[$key] = $value;
}
}
return $ordered;
}
function readableTriggerCode(string $value, string $lang): string
{
@@ -674,11 +692,39 @@ function formatSensorValue($value, $definition, $lang, $maps) {
return $value;
}
function findmydevice_history_endpoints(string $startTime, string $endTime, string $entityId): array
{
$start = DateTimeImmutable::createFromFormat('!Y-m-d\TH:i:s\Z', $startTime, new DateTimeZone('UTC'));
$end = DateTimeImmutable::createFromFormat('!Y-m-d\TH:i:s\Z', $endTime, new DateTimeZone('UTC'));
if (!$start || !$end || $end <= $start) {
return [];
}
$endpoints = [];
$cursor = $start;
$chunkInterval = new DateInterval('P1D');
while ($cursor < $end) {
$chunkEnd = $cursor->add($chunkInterval);
if ($chunkEnd > $end) {
$chunkEnd = $end;
}
$chunkStartText = $cursor->format('Y-m-d\TH:i:s\Z');
$chunkEndText = $chunkEnd->format('Y-m-d\TH:i:s\Z');
$endpoints[] = "/history/period/{$chunkStartText}?filter_entity_id=" . rawurlencode($entityId) . "&end_time={$chunkEndText}";
$cursor = $chunkEnd;
}
return $endpoints;
}
// 데이터 불러오기
$deviceData = getData($deviceUrl);
$geoData = getData($geoUrl);
$batteryStateData = getData($batteryStateUrl);
$batteryLevelData = getData($batteryLevelUrl);
$watchBatteryStateData = getEntityData($entities, 'watch_battery_state');
$watchBatteryLevelData = getEntityData($entities, 'watch_battery_level');
$activityData = getData($activityUrl);
$connectionData = getData($connectionUrl);
$ascendedData = getData($fascendedUrl);
@@ -688,8 +734,16 @@ $distanceData = getData($distanceUrl);
$highAccuracyModeData = getData($highAccuracyModeUrl);
$historySets = [];
if ($includeHistory) {
$historyEndpoints = findmydevice_history_endpoints($startTime, $endTime, $entities['device']);
foreach ($findmydeviceHaProfiles as $profile) {
$historySets[(string)$profile] = custom_ha_request((string)$profile, 'GET', $historyEndpoint, null, 0) ?? [];
$profileHistory = [[]];
foreach ($historyEndpoints as $historyEndpoint) {
$historyData = custom_ha_request((string)$profile, 'GET', $historyEndpoint, null, 0) ?? [];
if (isset($historyData[0]) && is_array($historyData[0])) {
$profileHistory[0] = array_merge($profileHistory[0], $historyData[0]);
}
}
$historySets[(string)$profile] = $profileHistory;
}
}
$triggerData = getData($triggerUrl);
@@ -1240,6 +1294,9 @@ $activityState = $activityData['state'] ?? null;
$aaccuracyState = $activityData['attributes']['Confidence'] ?? $activityData['attributes']['confidence'] ?? null;
$connectionState = $connectionData['state'] ?? null;
$batteryState = $batteryStateData['state'] ?? null;
$watchBatteryState = stateValue($watchBatteryStateData);
$watchBatteryLevel = stateValue($watchBatteryLevelData);
$watchBatteryText = $batteryStateMap[$lang][$watchBatteryState] ?? $watchBatteryState ?? null;
$highAccuracyMode = strtolower((string)($highAccuracyModeData['state'] ?? '')) === 'on';
$address = null;
@@ -1277,6 +1334,8 @@ foreach (($config['findmydevice_sensor_sections'] ?? []) as $sectionKey => $defi
}
if (!$sensorSections[$sectionKey]) {
unset($sensorSections[$sectionKey]);
} else {
$sensorSections[$sectionKey] = findmydevice_pair_watch_sensor_rows($sensorSections[$sectionKey]);
}
}
@@ -1291,6 +1350,8 @@ if (isset($_GET['hashonly']) && $_GET['hashonly'] == '1') {
'address' => $address,
'state' => $batteryStateMap[$lang][$batteryState] ?? $batteryState ?? null,
'level' => $batteryLevelData['state'] ?? null,
'wstate' => $watchBatteryText,
'wlevel' => $watchBatteryLevel,
'ascended' => $ascendedData['state'] ?? null,
'descended' => $descendedData['state'] ?? null,
'steps' => $stepsData['state'] ?? null,
@@ -1317,6 +1378,8 @@ echo json_encode([
'address' => $address,
'state' => $batteryStateMap[$lang][$batteryState] ?? $batteryState ?? null,
'level' => $batteryLevelData['state'] ?? null,
'wstate' => $watchBatteryText,
'wlevel' => $watchBatteryLevel,
'ascended' => $ascendedData['state'] ?? null,
'descended' => $descendedData['state'] ?? null,
'steps' => $stepsData['state'] ?? null,