2
0

ChecksChannelEditPower.php 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. <?php
  2. namespace App\Http\Controllers\Concerns;
  3. use App\Http\Api\ShareApi;
  4. use App\Models\AccessToken;
  5. use App\Models\Channel;
  6. use Firebase\JWT\JWT;
  7. use Firebase\JWT\Key;
  8. /**
  9. * channel 编辑权的统一判定:owner → 协作者 → access token。
  10. *
  11. * 第三条分支是外部客户端(如 wikipali write Skill)唯一的入口:AI 模型
  12. * 有自己的 uid,既不是 owner 也不在 share 表里,只能靠人类用户为它签出的
  13. * access token 代持权限。
  14. */
  15. trait ChecksChannelEditPower
  16. {
  17. /**
  18. * @param string $userId 当前身份的 uuid(人类用户或 AI 模型)
  19. * @param int $book 本次写入涉及的 book;无 book 概念的资源传 0
  20. * @param string|null $accessToken 由 AccessTokenController 签出的 JWT
  21. */
  22. protected function userCanEditChannel(string $userId, string $channelId, int $book, $accessToken = null): bool
  23. {
  24. $channel = Channel::where('uid', $channelId)->first();
  25. if (! $channel) {
  26. return false;
  27. }
  28. if ($channel->owner_uid !== $userId) {
  29. // 判断是否为协作
  30. $power = ShareApi::getResPower($userId, $channel->uid, 2);
  31. if ($power < 20) {
  32. // 判断token
  33. if (! $accessToken) {
  34. return false;
  35. }
  36. $key = AccessToken::where('res_id', $channelId)->value('token');
  37. if (! $key) {
  38. return false;
  39. }
  40. try {
  41. // access token 现在带 exp,过期会抛 ExpiredException;
  42. // 伪造/损坏的 token 同样抛异常。一律当作无权,不要冒泡成 500。
  43. $jwt = JWT::decode($accessToken, new Key($key.$key, 'HS512'));
  44. } catch (\Exception $e) {
  45. return false;
  46. }
  47. if (isset($jwt->book) && $jwt->book !== 0 && $jwt->book !== $book) {
  48. return false;
  49. }
  50. }
  51. }
  52. return true;
  53. }
  54. }