Initial in-app registration logic

This commit is contained in:
Daniel Supernault 2022-11-25 20:49:06 -07:00
parent b79d808c3b
commit 772cfb9cee
No known key found for this signature in database
GPG key ID: 0DEF1C662C9033F7
4 changed files with 191 additions and 0 deletions

View file

@ -14,12 +14,18 @@ use App\EmailVerification;
use App\Status;
use App\Report;
use App\Profile;
use App\User;
use App\Services\AccountService;
use App\Services\StatusService;
use App\Services\ProfileStatusService;
use App\Util\Lexer\RestrictedNames;
use App\Services\EmailService;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Jenssegers\Agent\Agent;
use Mail;
use App\Mail\PasswordChange;
use App\Mail\ConfirmAppEmail;
class ApiV1Dot1Controller extends Controller
{
@ -402,4 +408,145 @@ class ApiV1Dot1Controller extends Controller
return $this->json($res);
}
public function inAppRegistrationPreFlightCheck(Request $request)
{
return [
'open' => config('pixelfed.open_registration'),
'iara' => config('pixelfed.allow_app_registration')
];
}
public function inAppRegistration(Request $request)
{
abort_if($request->user(), 404);
abort_unless(config('pixelfed.open_registration'), 404);
abort_unless(config('pixelfed.allow_app_registration'), 404);
abort_unless($request->hasHeader('X-PIXELFED-APP'), 403);
$this->validate($request, [
'email' => [
'required',
'string',
'email',
'max:255',
'unique:users',
function ($attribute, $value, $fail) {
$banned = EmailService::isBanned($value);
if($banned) {
return $fail('Email is invalid.');
}
},
],
'username' => [
'required',
'min:2',
'max:15',
'unique:users',
function ($attribute, $value, $fail) {
$dash = substr_count($value, '-');
$underscore = substr_count($value, '_');
$period = substr_count($value, '.');
if(ends_with($value, ['.php', '.js', '.css'])) {
return $fail('Username is invalid.');
}
if(($dash + $underscore + $period) > 1) {
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
}
if (!ctype_alnum($value[0])) {
return $fail('Username is invalid. Must start with a letter or number.');
}
if (!ctype_alnum($value[strlen($value) - 1])) {
return $fail('Username is invalid. Must end with a letter or number.');
}
$val = str_replace(['_', '.', '-'], '', $value);
if(!ctype_alnum($val)) {
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
}
$restricted = RestrictedNames::get();
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
return $fail('Username cannot be used.');
}
},
],
'password' => 'required|string|min:8',
// 'avatar' => 'required|mimetypes:image/jpeg,image/png|max:15000',
// 'bio' => 'required|max:140'
]);
$email = $request->input('email');
$username = $request->input('username');
$password = $request->input('password');
if(config('database.default') == 'pgsql') {
$username = strtolower($username);
$email = strtolower($email);
}
$user = new User;
$user->name = $username;
$user->username = $username;
$user->email = $email;
$user->password = Hash::make($password);
$user->register_source = 'app';
$user->app_register_ip = $request->ip();
$user->app_register_token = Str::random(32);
$user->save();
$rtoken = Str::random(mt_rand(64, 70));
$verify = new EmailVerification();
$verify->user_id = $user->id;
$verify->email = $user->email;
$verify->user_token = $user->app_register_token;
$verify->random_token = $rtoken;
$verify->save();
$appUrl = 'pixelfed://confirm-account/'. $user->app_register_token . '?rt=' . $rtoken;
Mail::to($user->email)->send(new ConfirmAppEmail($verify, $appUrl));
return response()->json([
'success' => true,
]);
}
public function inAppRegistrationConfirm(Request $request)
{
abort_if($request->user(), 404);
abort_unless(config('pixelfed.open_registration'), 404);
abort_unless(config('pixelfed.allow_app_registration'), 404);
abort_unless($request->hasHeader('X-PIXELFED-APP'), 403);
$this->validate($request, [
'user_token' => 'required',
'random_token' => 'required',
'email' => 'required'
]);
$verify = EmailVerification::whereEmail($request->input('email'))
->whereUserToken($request->input('user_token'))
->whereRandomToken($request->input('random_token'))
->first();
if(!$verify) {
return response()->json(['error' => 'Invalid tokens'], 403);
}
$user = User::findOrFail($verify->user_id);
$user->email_verified_at = now();
$user->save();
$verify->delete();
$token = $user->createToken('Pixelfed');
return response()->json([
'access_token' => $token->access_token
]);
}
}

View file

@ -276,4 +276,6 @@ return [
'media_fast_process' => env('PF_MEDIA_FAST_PROCESS', true),
'max_altext_length' => env('PF_MEDIA_MAX_ALTTEXT_LENGTH', 1000),
'allow_app_registration' => env('PF_ALLOW_APP_REGISTRATION', true),
];

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('register_source')->default('web')->nullable()->index();
$table->string('app_register_token')->nullable();
$table->string('app_register_ip')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('register_source');
$table->dropColumn('app_register_token');
$table->dropColumn('app_register_ip');
});
}
};

View file

@ -149,6 +149,12 @@ Route::group(['prefix' => 'api'], function() use($middleware) {
Route::group(['prefix' => 'directory'], function () use($middleware) {
Route::get('listing', 'PixelfedDirectoryController@get');
});
Route::group(['prefix' => 'auth'], function () use($middleware) {
Route::get('iarpfc', 'Api\ApiV1Dot1Controller@inAppRegistrationPreFlightCheck');
Route::post('iar', 'Api\ApiV1Dot1Controller@inAppRegistration');
Route::post('iarc', 'Api\ApiV1Dot1Controller@inAppRegistrationConfirm');
});
});
Route::group(['prefix' => 'live'], function() use($middleware) {