You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
4.1 KiB

  1. <?php
  2. use Symfony\Component\HttpFoundation\Request;
  3. use Symfony\Component\HttpFoundation\Response;
  4. class Certbot {
  5. private $mc;
  6. private $http;
  7. public function index(Request $request, Response $response) {
  8. session_start();
  9. $state = mt_rand(10000,99999);
  10. $_SESSION['state'] = $state;
  11. $response->setContent(view('certbot', [
  12. 'title' => 'X-Ray',
  13. 'state' => $state
  14. ]));
  15. return $response;
  16. }
  17. public function start_auth(Request $request, Response $response) {
  18. session_start();
  19. $_SESSION['client_id'] = $request->get('client_id');
  20. $_SESSION['redirect_uri'] = $request->get('redirect_uri');
  21. $query = http_build_query([
  22. 'me' => $request->get('me'),
  23. 'client_id' => $request->get('client_id'),
  24. 'redirect_uri' => $request->get('redirect_uri'),
  25. 'state' => $request->get('state'),
  26. ]);
  27. $response->headers->set('Location', 'https://indieauth.com/auth?'.$query);
  28. $response->setStatusCode(302);
  29. return $response;
  30. }
  31. public function redirect(Request $request, Response $response) {
  32. session_start();
  33. $this->http = new p3k\HTTP();
  34. if(!isset($_SESSION['state']) || $_SESSION['state'] != $request->get('state')) {
  35. $response->headers->set('Location', '/cert?error=invalid_state');
  36. $response->setStatusCode(302);
  37. return $response;
  38. }
  39. if($code = $request->get('code')) {
  40. $res = $this->http->post('https://indieauth.com/auth', http_build_query([
  41. 'code' => $code,
  42. 'client_id' => $_SESSION['client_id'],
  43. 'redirect_uri' => $_SESSION['redirect_uri'],
  44. 'state' => $_SESSION['state']
  45. ]), [
  46. 'Accept: application/json'
  47. ]);
  48. $verify = json_decode($res['body'], true);
  49. unset($_SESSION['state']);
  50. if(isset($verify['me'])) {
  51. if(in_array($verify['me'], Config::$admins)) {
  52. $_SESSION['me'] = $verify['me'];
  53. $response->headers->set('Location', '/cert');
  54. } else {
  55. $response->headers->set('Location', '/cert?error=invalid_user');
  56. }
  57. } else {
  58. $response->headers->set('Location', '/cert?error=invalid');
  59. }
  60. } else {
  61. $response->headers->set('Location', '/cert?error=missing_code');
  62. }
  63. $response->setStatusCode(302);
  64. return $response;
  65. }
  66. public function save_challenge(Request $request, Response $response) {
  67. session_start();
  68. if(!isset($_SESSION['me']) || !in_array($_SESSION['me'], Config::$admins)) {
  69. $response->headers->set('Location', '/cert?error=forbidden');
  70. $response->setStatusCode(302);
  71. return $response;
  72. }
  73. $token = $request->get('token');
  74. $challenge = $request->get('challenge');
  75. if(preg_match('/acme-challenge\/(.+)/', $token, $match)) {
  76. $token = $match[1];
  77. } elseif(!preg_match('/^[_a-zA-Z0-9]+$/', $token)) {
  78. echo "Invalid token format\n";
  79. die();
  80. }
  81. $this->_mc();
  82. $this->mc->set('acme-challenge-'.$token, json_encode([
  83. 'token' => $token,
  84. 'challenge' => $challenge
  85. ]), 0, 600);
  86. $response->setContent(view('certbot', [
  87. 'title' => 'X-Ray',
  88. 'challenge' => $challenge,
  89. 'token' => $token,
  90. 'verified' => true
  91. ]));
  92. return $response;
  93. }
  94. public function logout(Request $request, Response $response) {
  95. session_start();
  96. unset($_SESSION['me']);
  97. unset($_SESSION['client_id']);
  98. unset($_SESSION['redirect_uri']);
  99. unset($_SESSION['state']);
  100. session_destroy();
  101. $response->headers->set('Location', '/cert');
  102. $response->setStatusCode(302);
  103. return $response;
  104. }
  105. public function challenge(Request $request, Response $response, array $args) {
  106. $this->_mc();
  107. $token = $args['token'];
  108. if($cache = $this->mc->get('acme-challenge-'.$token)) {
  109. $acme = json_decode($cache, true);
  110. $response->setContent($acme['challenge']);
  111. } else {
  112. $response->setStatusCode(404);
  113. $response->setContent("Not Found\n");
  114. }
  115. $response->headers->set('Content-Type', 'text/plain');
  116. return $response;
  117. }
  118. private function _mc() {
  119. $this->mc = new Memcache();
  120. $this->mc->addServer('127.0.0.1');
  121. }
  122. }