AppRegisterController.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Mail\InAppRegisterEmailVerify;
  4. use App\Models\AppRegister;
  5. use App\Services\AccountService;
  6. use App\User;
  7. use App\Util\Lexer\RestrictedNames;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Hash;
  11. use Illuminate\Support\Facades\Mail;
  12. use Illuminate\Support\Str;
  13. use Laravel\Passport\RefreshTokenRepository;
  14. use Purify;
  15. class AppRegisterController extends Controller
  16. {
  17. public function index(Request $request)
  18. {
  19. abort_unless(config('auth.iar') == true, 404);
  20. $open = (bool) config_cache('pixelfed.open_registration');
  21. if (! $open || $request->user()) {
  22. return redirect('/');
  23. }
  24. return view('auth.iar');
  25. }
  26. public function store(Request $request)
  27. {
  28. abort_unless(config('auth.iar') == true, 404);
  29. $open = (bool) config_cache('pixelfed.open_registration');
  30. if (! $open || $request->user()) {
  31. return redirect('/');
  32. }
  33. $rules = [
  34. 'email' => 'required|email:rfc,dns,spoof,strict|unique:users,email|unique:app_registers,email',
  35. ];
  36. if ((bool) config_cache('captcha.enabled') && (bool) config_cache('captcha.active.register')) {
  37. $rules['h-captcha-response'] = 'required|captcha';
  38. }
  39. $this->validate($request, $rules);
  40. $email = $request->input('email');
  41. $code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
  42. $exists = AppRegister::whereEmail($email)->where('created_at', '>', now()->subHours(24))->count();
  43. if ($exists && $exists > 3) {
  44. $errorParams = http_build_query([
  45. 'status' => 'error',
  46. 'message' => 'Too many attempts, please try again later.',
  47. ]);
  48. return redirect()->away("pixelfed://verifyEmail?{$errorParams}");
  49. }
  50. DB::beginTransaction();
  51. $registration = AppRegister::create([
  52. 'email' => $email,
  53. 'verify_code' => $code,
  54. 'email_delivered_at' => now(),
  55. ]);
  56. try {
  57. Mail::to($email)->send(new InAppRegisterEmailVerify($code));
  58. } catch (\Exception $e) {
  59. DB::rollBack();
  60. $errorParams = http_build_query([
  61. 'status' => 'error',
  62. 'message' => 'Failed to send verification code',
  63. ]);
  64. return redirect()->away("pixelfed://verifyEmail?{$errorParams}");
  65. }
  66. DB::commit();
  67. $queryParams = http_build_query([
  68. 'email' => $request->email,
  69. 'expires_in' => 3600,
  70. 'status' => 'success',
  71. ]);
  72. return redirect()->away("pixelfed://verifyEmail?{$queryParams}");
  73. }
  74. public function verifyCode(Request $request)
  75. {
  76. abort_unless(config('auth.iar') == true, 404);
  77. $open = (bool) config_cache('pixelfed.open_registration');
  78. if (! $open || $request->user()) {
  79. return redirect('/');
  80. }
  81. $this->validate($request, [
  82. 'email' => 'required|email:rfc,dns,spoof,strict|unique:users,email',
  83. 'verify_code' => ['required', 'digits:6', 'numeric'],
  84. ]);
  85. $email = $request->input('email');
  86. $code = $request->input('verify_code');
  87. $exists = AppRegister::whereEmail($email)
  88. ->whereVerifyCode($code)
  89. ->where('created_at', '>', now()->subMinutes(60))
  90. ->exists();
  91. return response()->json([
  92. 'status' => $exists ? 'success' : 'error',
  93. ]);
  94. }
  95. public function onboarding(Request $request)
  96. {
  97. abort_unless(config('auth.iar') == true, 404);
  98. $open = (bool) config_cache('pixelfed.open_registration');
  99. if (! $open || $request->user()) {
  100. return redirect('/');
  101. }
  102. $this->validate($request, [
  103. 'email' => 'required|email:rfc,dns,spoof,strict|unique:users,email',
  104. 'verify_code' => ['required', 'digits:6', 'numeric'],
  105. 'username' => $this->validateUsernameRule(),
  106. 'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'),
  107. 'password' => 'required|string|min:'.config('pixelfed.min_password_length'),
  108. ]);
  109. $email = $request->input('email');
  110. $code = $request->input('verify_code');
  111. $username = $request->input('username');
  112. $name = $request->input('name');
  113. $password = $request->input('password');
  114. $exists = AppRegister::whereEmail($email)
  115. ->whereVerifyCode($code)
  116. ->where('created_at', '>', now()->subMinutes(60))
  117. ->exists();
  118. if (! $exists) {
  119. return response()->json([
  120. 'status' => 'error',
  121. 'message' => 'Invalid verification code, please try again later.',
  122. ]);
  123. }
  124. $user = User::create([
  125. 'name' => Purify::clean($name),
  126. 'username' => $username,
  127. 'email' => $email,
  128. 'password' => Hash::make($password),
  129. 'app_register_ip' => request()->ip(),
  130. 'register_source' => 'app',
  131. ]);
  132. sleep(random_int(5,10));
  133. $user = User::findOrFail($user->id);
  134. $token = $user->createToken('Pixelfed App', ['read', 'write', 'follow', 'push']);
  135. $tokenModel = $token->token;
  136. $clientId = $tokenModel->client_id;
  137. $clientSecret = DB::table('oauth_clients')->where('id', $clientId)->value('secret');
  138. $refreshTokenRepo = app(RefreshTokenRepository::class);
  139. $refreshToken = $refreshTokenRepo->create([
  140. 'id' => Str::random(80),
  141. 'access_token_id' => $tokenModel->id,
  142. 'revoked' => false,
  143. 'expires_at' => now()->addDays(config('instance.oauth.refresh_expiration', 400)),
  144. ]);
  145. $expiresAt = $tokenModel->expires_at ?? now()->addDays(config('instance.oauth.token_expiration', 356));
  146. $expiresIn = now()->diffInSeconds($expiresAt);
  147. return response()->json([
  148. 'status' => 'success',
  149. 'token_type' => 'Bearer',
  150. 'domain' => config('pixelfed.domain.app'),
  151. 'expires_in' => $expiresIn,
  152. 'access_token' => $token->accessToken,
  153. 'refresh_token' => $refreshToken->id,
  154. 'client_id' => $clientId,
  155. 'client_secret' => $clientSecret,
  156. 'scope' => ['read', 'write', 'follow', 'push'],
  157. 'user' => [
  158. 'pid' => (string) $user->profile_id,
  159. 'username' => $user->username,
  160. ],
  161. 'account' => AccountService::get($user->profile_id, true),
  162. ]);
  163. }
  164. protected function validateUsernameRule()
  165. {
  166. return [
  167. 'required',
  168. 'min:2',
  169. 'max:30',
  170. 'unique:users',
  171. function ($attribute, $value, $fail) {
  172. $dash = substr_count($value, '-');
  173. $underscore = substr_count($value, '_');
  174. $period = substr_count($value, '.');
  175. if (ends_with($value, ['.php', '.js', '.css'])) {
  176. return $fail('Username is invalid.');
  177. }
  178. if (($dash + $underscore + $period) > 1) {
  179. return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
  180. }
  181. if (! ctype_alnum($value[0])) {
  182. return $fail('Username is invalid. Must start with a letter or number.');
  183. }
  184. if (! ctype_alnum($value[strlen($value) - 1])) {
  185. return $fail('Username is invalid. Must end with a letter or number.');
  186. }
  187. $val = str_replace(['_', '.', '-'], '', $value);
  188. if (! ctype_alnum($val)) {
  189. return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
  190. }
  191. if (! preg_match('/[a-zA-Z]/', $value)) {
  192. return $fail('Username is invalid. Must contain at least one alphabetical character.');
  193. }
  194. $restricted = RestrictedNames::get();
  195. if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
  196. return $fail('Username cannot be used.');
  197. }
  198. },
  199. ];
  200. }
  201. }