diff --git a/app/Course/Controllers/CourseController.php b/app/Course/Controllers/CourseController.php index 18c45df3..25dbd0ed 100644 --- a/app/Course/Controllers/CourseController.php +++ b/app/Course/Controllers/CourseController.php @@ -8,14 +8,15 @@ use App\Course\Requests\StoreRequest; use App\Course\Requests\UpdateRequest; use App\Http\Controllers\Controller; use App\Member\Member; +use App\Setting\NamiSettings; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class CourseController extends Controller { - public function store(Member $member, StoreRequest $request): RedirectResponse + public function store(Member $member, StoreRequest $request, NamiSettings $settings): RedirectResponse { - $request->persist($member); + $request->persist($member, $settings); return redirect()->back()->success('Ausbildung erstellt'); } diff --git a/app/Course/Requests/StoreRequest.php b/app/Course/Requests/StoreRequest.php index b28b4d9b..09b94ee9 100644 --- a/app/Course/Requests/StoreRequest.php +++ b/app/Course/Requests/StoreRequest.php @@ -4,13 +4,16 @@ namespace App\Course\Requests; use App\Course\Models\Course; use App\Member\Member; +use App\Setting\NamiSettings; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Arr; use Illuminate\Validation\ValidationException; +use Zoomyboy\LaravelNami\Nami; use Zoomyboy\LaravelNami\NamiException; class StoreRequest extends FormRequest { + /** * Determine if the user is authorized to make this request. * @@ -36,20 +39,21 @@ class StoreRequest extends FormRequest ]; } - public function persist(Member $member): void + public function persist(Member $member, NamiSettings $settings): void { $course = Course::where('id', $this->input('course_id'))->firstOrFail(); - $payload = array_merge( - $this->only(['event_name', 'completed_at', 'organizer']), - ['course_id' => $course->nami_id], - ); + + $payload = collect($this->input())->only(['event_name', 'completed_at', 'organizer'])->merge([ + 'course_id' => $course->nami_id, + ])->toArray(); try { - $namiId = auth()->user()->api()->createCourse($member->nami_id, $payload); + $namiId = Nami::login($settings->mglnr, $settings->password)->createCourse($member->nami_id, $payload); } catch(NamiException $e) { throw ValidationException::withMessages(['id' => 'Unbekannter Fehler']); } $member->courses()->create($this->safe()->collect()->put('nami_id', $namiId)->toArray()); } + } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 7f0218b4..c9fc9315 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -3,7 +3,9 @@ namespace App\Exceptions; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; +use Illuminate\Validation\ValidationException; use Throwable; +use Zoomyboy\LaravelNami\LoginException; use Zoomyboy\LaravelNami\NamiException; class Handler extends ExceptionHandler @@ -58,6 +60,10 @@ class Handler extends ExceptionHandler */ public function render($request, Throwable $exception) { + if ($exception instanceof LoginException) { + throw ValidationException::withMessages(['nami' => 'NaMi Login fehlgeschlagen.']); + } + return parent::render($request, $exception); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index dabd5e7b..1beefe13 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -8,7 +8,6 @@ use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; use Laravel\Telescope\Telescope; -use Zoomyboy\LaravelNami\Authentication\NamiGuard; class AppServiceProvider extends ServiceProvider { @@ -38,10 +37,6 @@ class AppServiceProvider extends ServiceProvider */ public function boot() { - NamiGuard::beforeLogin(function(array $credentials) { - return in_array($credentials['mglnr'], app(GeneralSettings::class)->allowed_nami_accounts) - ? null - : false; - }); + // } } diff --git a/app/Setting/NamiSettings.php b/app/Setting/NamiSettings.php new file mode 100644 index 00000000..7c1fdb78 --- /dev/null +++ b/app/Setting/NamiSettings.php @@ -0,0 +1,19 @@ + [ 'web' => [ - 'driver' => 'nami', - 'other_providers' => ['database'], + 'driver' => 'session', + 'provider' => 'database', ], 'api' => [ diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index fa5fafce..491c2f9d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -4,6 +4,7 @@ namespace Database\Factories; use App\User; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\Hash; /** * @extends Factory @@ -21,7 +22,9 @@ class UserFactory extends Factory public function definition() { return [ - // + 'email' => $this->faker->safeEmail, + 'password' => Hash::make('password'), + 'name' => $this->faker->firstName, ]; } } diff --git a/database/migrations/2021_11_23_173033_create_users_table.php b/database/migrations/2021_11_23_173033_create_users_table.php index 42e4944b..47aa68da 100644 --- a/database/migrations/2021_11_23_173033_create_users_table.php +++ b/database/migrations/2021_11_23_173033_create_users_table.php @@ -16,6 +16,7 @@ class CreateUsersTable extends Migration Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('email'); + $table->string('name'); $table->string('password'); $table->timestamps(); }); diff --git a/database/settings/2022_02_19_230152_create_nami_settings.php b/database/settings/2022_02_19_230152_create_nami_settings.php new file mode 100644 index 00000000..3a832c89 --- /dev/null +++ b/database/settings/2022_02_19_230152_create_nami_settings.php @@ -0,0 +1,13 @@ +migrator->add('nami.mglnr', -1); + $this->migrator->add('nami.password', ''); + } +} diff --git a/packages/laravel-nami b/packages/laravel-nami index 829dd013..b8d2a04d 160000 --- a/packages/laravel-nami +++ b/packages/laravel-nami @@ -1 +1 @@ -Subproject commit 829dd01358858c887dddaeadd6e4ddebd8f9fb0b +Subproject commit b8d2a04d43dd7634ac6e653b26b07b317c5566db diff --git a/tests/Feature/Course/StoreTest.php b/tests/Feature/Course/StoreTest.php index 319be838..7257cc52 100644 --- a/tests/Feature/Course/StoreTest.php +++ b/tests/Feature/Course/StoreTest.php @@ -55,9 +55,9 @@ class StoreTest extends TestCase */ public function testItValidatesInput(array $payload, array $errors): void { - $this->login()->init(); - $member = Member::factory()->defaults()->inNami(123)->createOne(); - $course = Course::factory()->inNami(456)->createOne(); + $this->login(); + $member = Member::factory()->defaults()->createOne(); + $course = Course::factory()->createOne(); $response = $this->post("/member/{$member->id}/course", array_merge([ 'course_id' => $course->id, @@ -71,8 +71,7 @@ class StoreTest extends TestCase public function testItCreatesACourse(): void { - $this->withoutExceptionHandling(); - $this->login()->init(); + $this->withoutExceptionHandling()->login()->loginNami(); $member = Member::factory()->defaults()->inNami(123)->createOne(); $course = Course::factory()->inNami(456)->createOne(); app(CourseFake::class)->createsSuccessful(123, 999); @@ -100,9 +99,29 @@ class StoreTest extends TestCase ]); } + public function testItThrowsErrorWhenLoginIsWrong(): void + { + $this->login()->failedNami(); + $member = Member::factory()->defaults()->inNami(123)->createOne(); + $course = Course::factory()->inNami(456)->createOne(); + + $response = $this->post("/member/{$member->id}/course", [ + 'course_id' => $course->id, + 'completed_at' => '2021-01-02', + 'event_name' => '::event::', + 'organizer' => '::org::', + ]); + + $this->assertErrors(['nami' => 'NaMi Login fehlgeschlagen.'], $response); + + $this->assertDatabaseMissing('course_members', [ + 'member_id' => $member->id, + ]); + } + public function testItReceivesUnknownErrors(): void { - $this->login()->init(); + $this->login()->loginNami(); $member = Member::factory()->defaults()->inNami(123)->createOne(); $course = Course::factory()->inNami(456)->createOne(); app(CourseFake::class)->doesntCreateWithError(123); diff --git a/tests/Feature/Course/UpdateTest.php b/tests/Feature/Course/UpdateTest.php index 3daf513c..71ab5ccf 100644 --- a/tests/Feature/Course/UpdateTest.php +++ b/tests/Feature/Course/UpdateTest.php @@ -56,7 +56,7 @@ class UpdateTest extends TestCase */ public function testItValidatesInput(array $payload, array $errors): void { - $this->login()->init(); + $this->login(); $member = Member::factory()->defaults()->inNami(123)->has(CourseMember::factory()->for(Course::factory()), 'courses')->createOne(); $newCourse = Course::factory()->inNami(789)->create(); diff --git a/tests/Feature/NamiLoginTest.php b/tests/Feature/NamiLoginTest.php deleted file mode 100644 index f29012c5..00000000 --- a/tests/Feature/NamiLoginTest.php +++ /dev/null @@ -1,140 +0,0 @@ -withoutExceptionHandling(); - $this->setLoginId(123); - app(FakeBackend::class) - ->fakeLogin('123') - ->addSearch(123, ['entries_vorname' => '::firstname::', 'entries_nachname' => '::lastname::', 'entries_gruppierungId' => 1000]); - - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ]); - - $key = session()->get('auth_key'); - $cache = Cache::get("namiauth-{$key}"); - $this->assertEquals('secret', data_get($cache, 'credentials.password')); - $this->assertEquals('::firstname::', data_get($cache, 'firstname')); - $this->assertEquals('::lastname::', data_get($cache, 'lastname')); - $this->assertEquals(1000, data_get($cache, 'group_id')); - $this->assertEquals(123, data_get($cache, 'credentials.mglnr')); - $this->assertTrue(auth()->check()); - } - - public function testItDoesntLoginTwoTimes(): void - { - $this->withoutExceptionHandling(); - $this->setLoginId(123); - app(FakeBackend::class) - ->fakeLogin('123') - ->addSearch(123, ['entries_vorname' => '::firstname::', 'entries_nachname' => '::lastname::', 'entries_gruppierungId' => 1000]); - - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ]); - auth()->logout(); - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ]); - - $this->assertTrue(auth()->check()); - - Http::assertSentCount(4); - } - - public function testItResolvesTheLoginFromTheCache(): void - { - $this->withoutExceptionHandling(); - $this->setLoginId(123); - app(FakeBackend::class) - ->fakeLogin('123') - ->addSearch(123, ['entries_vorname' => '::firstname::', 'entries_nachname' => '::lastname::', 'entries_gruppierungId' => 1000]); - - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ]); - app(NamiGuard::class)->setUser(null); - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ]); - - $this->assertTrue(auth()->check()); - - Http::assertSentCount(3); - } - - public function testItThrowsExceptionWhenLoginFailed(): void - { - $this->setLoginId(123); - app(FakeBackend::class)->fakeFailedLogin(); - - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ])->assertRedirect('/'); - - $this->assertFalse(auth()->check()); - - Http::assertSentCount(2); - } - - public function testItCannotLoginWithAWrongNamiId(): void - { - app(FakeBackend::class) - ->fakeLogin('123') - ->addSearch(123, ['entries_vorname' => '::firstname::', 'entries_nachname' => '::lastname::', 'entries_gruppierungId' => 1000]); - - $this->post('/login', [ - 'mglnr' => 123, - 'provider' => 'nami', - 'password' => 'secret' - ])->assertRedirect('/'); - - $this->assertTrue(auth()->guest()); - - Http::assertSentCount(0); - } - - private function setLoginId(int $mglNr): self - { - GeneralSettings::fake([ - 'allowed_nami_accounts' => [$mglNr] - ]); - - return $this; - } - -} diff --git a/tests/Feature/UserLoginTest.php b/tests/Feature/UserLoginTest.php index 320874b9..5b1d2353 100644 --- a/tests/Feature/UserLoginTest.php +++ b/tests/Feature/UserLoginTest.php @@ -10,7 +10,6 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Http; use Tests\TestCase; -use Zoomyboy\LaravelNami\Authentication\NamiGuard; use Zoomyboy\LaravelNami\Backend\FakeBackend; class UserLoginTest extends TestCase diff --git a/tests/TestCase.php b/tests/TestCase.php index fed9948f..11722ddc 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,9 +4,13 @@ namespace Tests; use App\Member\Member; use App\Setting\GeneralSettings; +use App\Setting\NamiSettings; +use App\User; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; +use Illuminate\Http\RedirectResponse; use Illuminate\Testing\TestResponse; use Tests\Lib\TestsInertia; +use Zoomyboy\LaravelNami\Authentication\Auth; use Zoomyboy\LaravelNami\Backend\FakeBackend; use Zoomyboy\LaravelNami\Nami; use Zoomyboy\LaravelNami\NamiUser; @@ -16,20 +20,40 @@ abstract class TestCase extends BaseTestCase use CreatesApplication; use TestsInertia; - public function fakeAuthUser(): void + protected User $me; + + public function setUp(): void { - app(FakeBackend::class) - ->fakeLogin('123') - ->addSearch(123, ['entries_vorname' => '::firstname::', 'entries_nachname' => '::lastname::', 'entries_gruppierungId' => 1000]); + parent::setUp(); + Auth::fake(); + } + + public function loginNami(int $mglnr = 12345, string $password = 'password'): self + { + Auth::success($mglnr, $password); + NamiSettings::fake([ + 'mglnr' => $mglnr, + 'password' => $password, + ]); + + return $this; + } + + public function failedNami(int $mglnr = 12345, string $password = 'password'): self + { + Auth::failed($mglnr, $password); + NamiSettings::fake([ + 'mglnr' => $mglnr, + 'password' => $password, + ]); + + return $this; } public function login(): self { - $this->fakeAuthUser(); - auth()->loginNami([ - 'mglnr' => 123, - 'password' => 'secret', - ]); + $this->be($user = User::factory()->create()); + $this->me = $user; return $this; } @@ -40,5 +64,20 @@ abstract class TestCase extends BaseTestCase return $this; } + public function assertErrors(array $errors, TestResponse $response) { + $response->assertSessionHas('errors'); + $this->assertInstanceOf(RedirectResponse::class, $response->baseResponse); + /** @var RedirectResponse */ + $response = $response; + + $sessionErrors = $response->getSession()->get('errors')->getBag('default'); + + foreach ($errors as $key => $value) { + $this->assertTrue($sessionErrors->has($key), "Cannot find key {$key} in errors '".print_r($sessionErrors, true)); + $this->assertEquals($value, $sessionErrors->get($key)[0], "Failed to validate value for session error key {$key}. Actual value: ".print_r($sessionErrors, true)); + } + + return $this; + } }