diff --git a/src/c2dl/app/Http/Controllers/Auth/LoginController.php b/src/c2dl/app/Http/Controllers/Auth/LoginController.php index feac36c..c4b4729 100644 --- a/src/c2dl/app/Http/Controllers/Auth/LoginController.php +++ b/src/c2dl/app/Http/Controllers/Auth/LoginController.php @@ -3,10 +3,11 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; +use App\Models\User; use Illuminate\Foundation\Auth\AuthenticatesUsers; use \Illuminate\Http\Request; -use Illuminate\Support\Facades\Validator; -use mysql_xdevapi\Exception; +use Illuminate\Http\Response as HttpResponse; +use Illuminate\Support\Facades\Auth; class LoginController extends Controller { @@ -80,7 +81,7 @@ public function logout(Request $request) */ public function username() { - return 'user'; + return 'username'; } /** @@ -98,13 +99,49 @@ protected function validateLogin(Request $request) $this->username() => 'required|string', 'password' => 'required|string', ]); - } - catch (\Illuminate\Validation\ValidationException $err) { + } catch (\Illuminate\Validation\ValidationException $err) { $err->redirectTo = $this->redirectToFailed; throw $err; } } + /** + * Login through /api/login. + * + * @param \Illuminate\Http\Request + * + * @return \Illuminate\Http\JsonResponse + */ + public function apiLogin(Request $request) + { + if ($this->attemptLogin($request)) { + $user = Auth::user(); + $token = $user->createToken("{$request->username}_token"); + $user->current_token = $token->accessToken->token; + $user->save(); + + return response()->json([ + 'name' => "{$request->username}_token", + 'token' => $token->accessToken->token + ]); + } else { + return response()->json([ + 'error' => 'Something went wrong.' + ], HttpResponse::HTTP_FORBIDDEN); + } + } + + /** + * Get a user by their token using /api/validate. + * + * @param \Illuminate\Http\Request + */ + public function userByToken(Request $request) + { + $user = User::where('current_token', $request->get('token'))->first(); + return $user->toJson(); + } + /** * Handle a login request to the application. * @@ -123,8 +160,10 @@ public function login(Request $request) // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. - if (method_exists($this, 'hasTooManyLoginAttempts') && - $this->hasTooManyLoginAttempts($request)) { + if ( + method_exists($this, 'hasTooManyLoginAttempts') && + $this->hasTooManyLoginAttempts($request) + ) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); diff --git a/src/c2dl/app/Http/Controllers/Auth/RegisterController.php b/src/c2dl/app/Http/Controllers/Auth/RegisterController.php index bba6e46..dd1e341 100644 --- a/src/c2dl/app/Http/Controllers/Auth/RegisterController.php +++ b/src/c2dl/app/Http/Controllers/Auth/RegisterController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; -use App\User; +use App\Models\User; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; @@ -59,7 +59,7 @@ public function showRegistrationForm() protected function validator(array $data) { return Validator::make($data, [ - 'name' => ['required', 'string', 'max:255'], + 'username' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); @@ -69,14 +69,15 @@ protected function validator(array $data) * Create a new user instance after a valid registration. * * @param array $data - * @return \App\User + * @return \App\Models\User */ protected function create(array $data) { return User::create([ - 'name' => $data['name'], + 'username' => $data['username'], 'email' => $data['email'], 'password' => Hash::make($data['password']), + 'active' => 1 ]); } } diff --git a/src/c2dl/app/Http/Kernel.php b/src/c2dl/app/Http/Kernel.php index f9fd6b8..ade77c9 100644 --- a/src/c2dl/app/Http/Kernel.php +++ b/src/c2dl/app/Http/Kernel.php @@ -32,7 +32,7 @@ class Kernel extends HttpKernel \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, @@ -42,7 +42,7 @@ class Kernel extends HttpKernel \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, + \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, diff --git a/src/c2dl/app/Models/User.php b/src/c2dl/app/Models/User.php index e7866fa..45b9370 100644 --- a/src/c2dl/app/Models/User.php +++ b/src/c2dl/app/Models/User.php @@ -2,18 +2,18 @@ namespace App\Models; -use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { protected $connection = 'acc'; - protected $table = 'acc_user'; + protected $table = 'users'; protected $primaryKey = 'user_id'; - use HasFactory, Notifiable; + use HasApiTokens, HasFactory, Notifiable; /** * The attributes that are mass assignable. @@ -21,7 +21,9 @@ class User extends Authenticatable * @var array */ protected $fillable = [ - 'name', + 'username', + 'email', + 'password', ]; /** @@ -30,6 +32,7 @@ class User extends Authenticatable * @var array */ protected $hidden = [ + 'password', 'remember_token', ]; @@ -41,4 +44,9 @@ class User extends Authenticatable protected $casts = [ 'validate_at' => 'datetime', ]; + + public function currentAccessToken() + { + return $this->current_token; + } } diff --git a/src/c2dl/app/Providers/AuthServiceProvider.php b/src/c2dl/app/Providers/AuthServiceProvider.php index fd082d2..debe44d 100644 --- a/src/c2dl/app/Providers/AuthServiceProvider.php +++ b/src/c2dl/app/Providers/AuthServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Auth\Notifications\ResetPassword; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; @@ -27,6 +28,9 @@ public function boot() { $this->registerPolicies(); - // + // Properly format the button link in the email. + ResetPassword::createUrlUsing(function ($_user, string $token) { + return route('password.request', $token); + }); } } diff --git a/src/c2dl/composer.json b/src/c2dl/composer.json index 118244c..b21e96d 100644 --- a/src/c2dl/composer.json +++ b/src/c2dl/composer.json @@ -12,7 +12,7 @@ "ext-dom": "*", "fruitcake/laravel-cors": "^3.0", "guzzlehttp/guzzle": "^7.2", - "laravel/framework": "^9.2", + "laravel/framework": "^9.3", "laravel/tinker": "^2.7", "laravel/sanctum": "^2.14.1", "laravel/ui": "^3.0", diff --git a/src/c2dl/database/factories/UserFactory.php b/src/c2dl/database/factories/UserFactory.php index 3c0e33d..938acab 100644 --- a/src/c2dl/database/factories/UserFactory.php +++ b/src/c2dl/database/factories/UserFactory.php @@ -23,8 +23,11 @@ class UserFactory extends Factory public function definition() { return [ - 'name' => $this->faker->name, + 'name' => $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), 'active' => false, + 'validate_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ]; } diff --git a/src/c2dl/database/migrations/2022_11_12_145506_create_password_resets_table.php b/src/c2dl/database/migrations/2022_11_12_145506_create_password_resets_table.php new file mode 100644 index 0000000..fcacb80 --- /dev/null +++ b/src/c2dl/database/migrations/2022_11_12_145506_create_password_resets_table.php @@ -0,0 +1,32 @@ +string('email')->index(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('password_resets'); + } +}; diff --git a/src/c2dl/database/migrations/2022_11_13_092303_restructure_users_table.php b/src/c2dl/database/migrations/2022_11_13_092303_restructure_users_table.php new file mode 100644 index 0000000..0cf9a1b --- /dev/null +++ b/src/c2dl/database/migrations/2022_11_13_092303_restructure_users_table.php @@ -0,0 +1,48 @@ +rename('acc_user', 'users'); + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->dropColumn('created_at', 'updated_at'); + + $table->string('email')->unique()->comment('Unique user email'); + $table->string('password')->comment('User chosen password'); + }); + + // Had to split this into another call to `table` due to "duplicate column" errors. + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->dropColumn('email', 'password', 'created_at', 'updated_at'); + }); + + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->dateTime('created_at')->useCurrent()->comment('Created'); + $table->dateTime('updated_at')->nullable()->comment('Updated, null if not'); + }); + + Schema::connection('acc')->rename('users', 'acc_user'); + } +}; diff --git a/src/c2dl/database/migrations/2022_11_13_203900_add_token_column.php b/src/c2dl/database/migrations/2022_11_13_203900_add_token_column.php new file mode 100644 index 0000000..e62f1b4 --- /dev/null +++ b/src/c2dl/database/migrations/2022_11_13_203900_add_token_column.php @@ -0,0 +1,33 @@ +table('users', function (Blueprint $table) { + $table->string('current_token', 64)->unique()->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->dropColumn('current_token'); + }); + } +}; diff --git a/src/c2dl/database/migrations/2022_11_23_205918_change_name_table.php b/src/c2dl/database/migrations/2022_11_23_205918_change_name_table.php new file mode 100644 index 0000000..8e23cad --- /dev/null +++ b/src/c2dl/database/migrations/2022_11_23_205918_change_name_table.php @@ -0,0 +1,36 @@ +table('users', function (Blueprint $table) { + $table->dropColumn('name'); + + $table->string('username', 64)->unique()->comment('Unique username'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::connection('acc')->table('users', function (Blueprint $table) { + $table->dropColumn('username'); + + $table->string('name', 64)->unique()->comment('Unique user name'); + }); + } +}; diff --git a/src/c2dl/resources/views/auth/login.blade.php b/src/c2dl/resources/views/auth/login.blade.php index 4d929b3..9b0c62c 100644 --- a/src/c2dl/resources/views/auth/login.blade.php +++ b/src/c2dl/resources/views/auth/login.blade.php @@ -16,7 +16,7 @@ @csrf