システムアカウントの発見
守矢諏訪子 <suwako@076.moe>、2026-11-11 15:15
コミット 90fc1533582f542e192f5fecf3a62253635ecdcc
・親 b928dd1
・ファイルを見る
4個のファイル、+61 -5
| @@ -142,6 +142,7 @@ | ||
| 142 | 142 | $url = (string)($_GET['actor'] ?? ''); |
| 143 | 143 | $local = Fedi::localUsername($url); |
| 144 | 144 | if ($local !== null) $this->redirect("/microblog/user/{$local}"); |
| 145 | + if ($url === Fedi::blogActorUrl()) $this->blogActor(); | |
| 145 | 146 | if (!Remote::isSafeUrl($url) || Actor::isDomainBlocked($url)) $this->notFound(); |
| 146 | 147 | // 他のサーバーへの問い合わせはログインしたユーザーだけ(誰でも取得させられると、踏み台にされる) |
| 147 | 148 | $doc = $this->me !== null ? Remote::actor($url, isset($_GET['refresh'])) : Remote::cachedActor($url); |
| @@ -171,6 +172,39 @@ | ||
| 171 | 172 | 'items' => $this->present($items), |
| 172 | 173 | 'next' => null, |
| 173 | 174 | ]); |
| 175 | + } | |
| 176 | + | |
| 177 | + /** | |
| 178 | + * このサイトのブログのアカウント(FEDIINFOのactor)のプロフィール | |
| 179 | + * 記事はブログで読むので投稿は表示せず、マイクロブログからはフォロー出来ない(Social::follow) | |
| 180 | + */ | |
| 181 | + private function blogActor(): never { | |
| 182 | + try { | |
| 183 | + $doc = Remote::actor(Fedi::blogActorUrl()); | |
| 184 | + } catch (\Exception $e) { | |
| 185 | + $doc = null; // 公開鍵が読めない等 | |
| 186 | + } | |
| 187 | + if (!$doc) $this->notFound(); | |
| 188 | + $this->render('microblog/profile', (string)($doc['name'] ?? $doc['preferredUsername'] ?? ''), [ | |
| 189 | + 'profile' => [ | |
| 190 | + 'url' => $doc['id'], | |
| 191 | + 'name' => $name = (string)(($doc['name'] ?? '') ?: ($doc['preferredUsername'] ?? '')), | |
| 192 | + 'color' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'), | |
| 193 | + 'handle' => Remote::handle($doc), | |
| 194 | + 'avatar' => self::safeUrl(is_string($doc['icon']['url'] ?? null) ? $doc['icon']['url'] : '') ?: '/static/user/noicon.png', | |
| 195 | + 'bio' => Html::sanitize((string)($doc['summary'] ?? '')), | |
| 196 | + 'local' => true, | |
| 197 | + 'profilePage' => '', | |
| 198 | + 'locked' => false, | |
| 199 | + 'followers' => null, | |
| 200 | + 'following' => null, | |
| 201 | + ], | |
| 202 | + 'relation' => ['self' => false, 'can' => false], | |
| 203 | + 'blogActor' => true, | |
| 204 | + 'items' => [], | |
| 205 | + 'next' => null, | |
| 206 | + ]); | |
| 207 | + exit; | |
| 174 | 208 | } |
| 175 | 209 | |
| 176 | 210 | //////// 投稿のページ |
| @@ -145,9 +145,7 @@ | ||
| 145 | 145 | */ |
| 146 | 146 | public static function webfinger(string $user, string $host): ?string { |
| 147 | 147 | if (!preg_match('/^[A-Za-z0-9_.-]+$/', $user) || !preg_match('/^[A-Za-z0-9.-]+$/', $host)) return null; |
| 148 | - if (strtolower($host) === strtolower(Fedi::host())) { | |
| 149 | - return Fedi::localActor($user) ? Fedi::actorUrl($user) : null; | |
| 150 | - } | |
| 148 | + if (strtolower($host) === strtolower(Fedi::host())) return self::localHandle($user); | |
| 151 | 149 | |
| 152 | 150 | // 同じ人を何度も探さない様に、結果を1日キャッシュする |
| 153 | 151 | // 見つからなかった場合は短くする(以前は1日で、相手のサーバーが一時的に応答しないと1日探せなかった) |
| @@ -221,8 +219,17 @@ | ||
| 221 | 219 | return $doc ? $doc['id'] : null; |
| 222 | 220 | } |
| 223 | 221 | if (preg_match('/^@?([A-Za-z0-9_.-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,}(?::\d+)?)$/', $q, $m)) return self::webfinger($m[1], $m[2]); |
| 224 | - if (preg_match('/^@([A-Za-z0-9]+)$/', $q, $m)) return Fedi::localActor($m[1]) ? Fedi::actorUrl($m[1]) : null; | |
| 222 | + if (preg_match('/^@([A-Za-z0-9_.-]+)$/', $q, $m)) return self::localHandle($m[1]); | |
| 225 | 223 | return null; |
| 224 | + } | |
| 225 | + | |
| 226 | + /** | |
| 227 | + * このサイトのアカウント名からアクターのURL | |
| 228 | + * ブログのアカウント(FEDIINFOのactor)も含める。同じ名前のユーザーより優先する(WebFingerと同じ) | |
| 229 | + */ | |
| 230 | + private static function localHandle(string $user): ?string { | |
| 231 | + if (ACTIVITYPUB_ENABLED && strcasecmp($user, (string)FEDIINFO['actor']) === 0) return Fedi::blogActorUrl(); | |
| 232 | + return Fedi::localActor($user) ? Fedi::actorUrl($user) : null; | |
| 226 | 233 | } |
| 227 | 234 | |
| 228 | 235 | /** |
| @@ -277,6 +277,19 @@ | ||
| 277 | 277 | $test->assertNull(Fedi::localActor('dave')); |
| 278 | 278 | }); |
| 279 | 279 | |
| 280 | + $test->it('このサイトのアカウントを検索出来るはず(ブログのアカウントも)', function($test) use ($use, $alice): void { | |
| 281 | + $use('A'); | |
| 282 | + // テストのサーバーはIPアドレスなので、「@user@host」はWebFingerで確認する | |
| 283 | + $test->assertEquals($alice, Remote::webfinger('alice', '1.1.1.1')); | |
| 284 | + $test->assertEquals($alice, Remote::resolve('@alice')); | |
| 285 | + $blog = FEDIINFO['actor']; | |
| 286 | + $test->assertEquals(Fedi::blogActorUrl(), Remote::webfinger($blog, '1.1.1.1')); | |
| 287 | + $test->assertEquals(Fedi::blogActorUrl(), Remote::webfinger(strtoupper($blog), '1.1.1.1')); | |
| 288 | + $test->assertEquals(Fedi::blogActorUrl(), Remote::resolve("@{$blog}")); | |
| 289 | + $test->assertNull(Remote::webfinger('nobody', '1.1.1.1')); | |
| 290 | + $test->assertNull(Remote::resolve('@dave'), 'BANされたユーザー'); | |
| 291 | + }); | |
| 292 | + | |
| 280 | 293 | $test->it('リモートのユーザーをフォローして承認されるはず', function($test) use ($use, $pump, $alice, $bob): void { |
| 281 | 294 | $use('A'); |
| 282 | 295 | $test->assertTrue(Social::follow('alice', $bob)->isSuccess); |
| @@ -76,7 +76,9 @@ | ||
| 76 | 76 | {@ endif @} |
| 77 | 77 | </div> |
| 78 | 78 | </section> |
| 79 | -{@ if (empty($items)) @} | |
| 79 | +{@ if (!empty($blogActor)) @} | |
| 80 | + <p class="paragraph">このサイトのブログのアカウントです。新しい記事を自動で投稿します。記事は<a href="/">ブログ</a>でお読み下さい(マイクロブログからはフォロー出来ません)。</p> | |
| 81 | +{@ elif (empty($items)) @} | |
| 80 | 82 | <p class="paragraph">表示出来る投稿はありません。</p> |
| 81 | 83 | {@ endif @} |
| 82 | 84 | {@ foreach ($items as $it) @} |