Compare commits

...

9 Commits

Author SHA1 Message Date
philipp lang 0dd2c64c38 Add separate login for mglnr 2024-02-24 01:25:24 +01:00
philipp lang cf50705d5a Add login test 2024-02-24 01:05:18 +01:00
philipp lang 5802ccf332 Add Login test 2024-02-24 00:37:25 +01:00
philipp lang e897d37dbd Remove first login request 2024-02-24 00:37:02 +01:00
philipp lang 6fd6e9ead5 Lint 2024-02-24 00:36:55 +01:00
philipp lang 8125e664b1 Lint 2024-02-24 00:36:21 +01:00
philipp lang 95985a00e5 Lint 2024-02-24 00:09:06 +01:00
philipp lang 8e0c56641a Lint 2024-02-23 23:32:48 +01:00
philipp lang b558766cdc Lint 2024-02-23 23:29:32 +01:00
7 changed files with 193 additions and 66 deletions

View File

@ -72,7 +72,8 @@ class Api
{
$this->assertLoggedIn();
return app(Paginator::class)->startResult(100,
return app(Paginator::class)->startResult(
100,
fn ($page, $start) => $this->http()->get($this->url . '/ica/rest/nami/search-multi/result-list?searchedValues=' . rawurlencode(json_encode((object) $payload) ?: '{}') . '&page=' . $page . '&start=' . $start . '&limit=100'),
function ($response) {
if (true !== $response->json()['success']) {

View File

@ -6,6 +6,8 @@ use Illuminate\Http\Client\PendingRequest;
abstract class Authenticator
{
protected static string $path = __DIR__ . '/../../.cookies';
abstract public function login(int $mglnr, string $password): self;
abstract public function purge(): void;
@ -16,8 +18,6 @@ abstract class Authenticator
abstract public function refresh(): void;
protected static string $path = __DIR__.'/../../.cookies';
public static function setPath(string $path): void
{
static::$path = $path;

View File

@ -12,8 +12,8 @@ class MainCookie extends Authenticator
{
private CookieJar $cookie;
private string $url = 'https://nami.dpsg.de';
private ?int $mglnr = null;
private ?string $password = null;
private int $mglnr;
private string $password;
public function login(int $mglnr, string $password): self
{
@ -28,7 +28,6 @@ class MainCookie extends Authenticator
unlink($file);
}
$this->http()->get($this->url.'/ica/pages/login.jsp');
$response = $this->http()->asForm()->post($this->url . '/ica/rest/nami/auth/manual/sessionStartup', [
'Login' => 'API',
'redirectTo' => './app.jsp',
@ -65,10 +64,8 @@ class MainCookie extends Authenticator
public function refresh(): void
{
if ($this->mglnr && $this->password) {
$this->login($this->mglnr, $this->password);
}
}
public function http(): PendingRequest
{
@ -77,12 +74,12 @@ class MainCookie extends Authenticator
private function newFileName(): string
{
return parent::$path.'/'.time().'.txt';
return parent::$path . '/' . $this->mglnr . '_' . now()->timestamp . '.txt';
}
private function isExpired(): bool
{
$lastLoginTime = Carbon::createFromTimestamp(pathinfo($this->file(), PATHINFO_FILENAME));
$lastLoginTime = Carbon::createFromTimestamp(str(pathinfo($this->file(), PATHINFO_FILENAME))->replace($this->mglnr . '_', '')->toString());
return $lastLoginTime->addMinutes(50)->isPast();
}
@ -94,7 +91,7 @@ class MainCookie extends Authenticator
*/
private function file(): ?string
{
$files = glob(parent::$path.'/*');
$files = glob(parent::$path . '/' . $this->mglnr . '_*');
if (!count($files)) {
return null;

53
src/Fakes/LoginFake.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace Zoomyboy\LaravelNami\Fakes;
use Illuminate\Support\Facades\Http;
class LoginFake extends Fake
{
public function succeeds(string $token): void
{
$this->fakeLogin($token, 0);
}
public function fails(string $token): void
{
$this->fakeLogin($token, 500);
}
protected function fakeLogin(string $token, int $statusCode): void
{
Http::fake(function ($request) use ($token, $statusCode) {
if ($request->url() !== 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup') {
return;
}
return Http::response(json_encode([
"servicePrefix" => null,
"methodCall" => null,
"response" => null,
"statusCode" => $statusCode,
"statusMessage" => "",
"apiSessionName" => "JSESSIONID",
"apiSessionToken" => $token,
"minorNumber" => 2,
"majorNumber" => 1,
]), 200, [
'Set-Cookie' => 'JSESSIONID=' . $token . '.srv-nami06; path=/ica; secure; HttpOnly; Max-Age=9000; Expires=Sat, 24-Feb-2024 01:18:00 GMT'
]);
});
}
public function assertSent(int $mglnr, string $password): void
{
Http::assertSent(function ($request) use ($mglnr, $password) {
return $request->url() === 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup'
&& $request->header('Content-Type')[0] === 'application/x-www-form-urlencoded'
&& $request['Login'] === 'API'
&& $request['redirectTo'] === './app.jsp'
&& $request['username'] === $mglnr
&& $request['password'] === $password;
});
}
}

View File

@ -69,13 +69,6 @@ class CourseTest extends TestCase
$this->login()->coursesOf(11111);
}
public function testItNeedsLoginToGetCourses(): void
{
$this->expectException(NotAuthenticatedException::class);
Nami::coursesOf(11111);
}
public function testStoreACourse(): void
{
app(CourseFake::class)->createsSuccessfully(123, 999);
@ -94,17 +87,6 @@ class CourseTest extends TestCase
]);
}
public function testNeedsLoginToStoreACourse(): void
{
$this->expectException(NotAuthenticatedException::class);
Nami::createCourse(123, [
'event_name' => '::event::',
'completed_at' => '2021-01-02 00:00:00',
'organizer' => '::org::',
'course_id' => 456,
]);
}
public function testUpdateACourse(): void
{
app(CourseFake::class)->updatesSuccessfully(123, 999);
@ -140,17 +122,6 @@ class CourseTest extends TestCase
]);
}
public function testItNeedsValidCredentialsToStoreACourse(): void
{
$this->expectException(NotAuthenticatedException::class);
Nami::createCourse(123, [
'event_name' => '::event::',
'completed_at' => '2021-01-02 00:00:00',
'organizer' => '::org::',
'course_id' => 456,
]);
}
public function testItThrowsLoginExceptionWhenFetchingWithWrongCredentials(): void
{
$this->expectException(LoginException::class);

105
tests/Unit/LoginTest.php Normal file
View File

@ -0,0 +1,105 @@
<?php
namespace Zoomyboy\LaravelNami\Tests\Unit;
use Carbon\Carbon;
use Illuminate\Support\Facades\Http;
use Zoomyboy\LaravelNami\Authentication\Authenticator;
use Zoomyboy\LaravelNami\Authentication\MainCookie;
use Zoomyboy\LaravelNami\Fakes\CourseFake;
use Zoomyboy\LaravelNami\Fakes\LoginFake;
use Zoomyboy\LaravelNami\LoginException;
use Zoomyboy\LaravelNami\Nami;
use Zoomyboy\LaravelNami\Tests\TestCase;
class LoginTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
Carbon::setTestNow(Carbon::parse('2024-02-23 23:48:00'));
}
public function testItLoggsInAndSavesCookie(): void
{
app(LoginFake::class)->succeeds('lala-testsession');
app(MainCookie::class)->login(12345, 'secret');
Http::assertSentCount(1);
$cookie = file_get_contents(__DIR__ . '/../../.cookies_test/12345_' . now()->timestamp . '.txt');
$this->assertEquals('lala-testsession.srv-nami06', $cookie);
app(LoginFake::class)->assertSent(12345, 'secret');
}
public function testItThrowsExceptionWhenLoginFails(): void
{
app(LoginFake::class)->fails('::token::');
$this->expectException(LoginException::class);
app(MainCookie::class)->login(12345, 'secret');
}
public function testItDoesntSaveCookieWhenLoginFails(): void
{
app(LoginFake::class)->fails('::token::');
try {
app(MainCookie::class)->login(12345, 'secret');
} catch (LoginException $e) {
}
Http::assertSentCount(1);
$this->assertEmpty(glob(__DIR__ . '/../../.cookies_test/*'));
}
public function testItDoesntResetCookieWhenAlreadyLoggedIn(): void
{
app(CourseFake::class)->fetches(103, []);
file_put_contents(__DIR__ . '/../../.cookies_test/90100_' . now()->subMinutes(5)->timestamp . '.txt', 'cook-testsession.srv-nami06');
Nami::login(90100, 'secret');
Http::assertSentCount(0);
$this->assertFileExists(__DIR__ . '/../../.cookies_test/90100_' . now()->subMinutes(5)->timestamp . '.txt');
$this->assertCount(1, glob(__DIR__ . '/../../.cookies_test/*'));
}
public function testItIgnoresExpiredCookie(): void
{
app(CourseFake::class)->fetches(103, []);
app(LoginFake::class)->succeeds('newlogin');
file_put_contents(__DIR__ . '/../../.cookies_test/90100_' . now()->subHour()->timestamp . '.txt', 'oldlogin-testsession.srv-nami06');
Nami::login(90100, 'secret');
Http::assertSentCount(1);
$cookie = file_get_contents(__DIR__ . '/../../.cookies_test/90100_' . now()->timestamp . '.txt');
$this->assertEquals('newlogin.srv-nami06', $cookie);
$this->assertFileExists(__DIR__ . '/../../.cookies_test/90100_' . now()->timestamp . '.txt');
$this->assertCount(1, glob(__DIR__ . '/../../.cookies_test/*'));
}
public function testItDoesntUseOtherCookieForLogin(): void
{
app(CourseFake::class)->fetches(103, []);
app(LoginFake::class)->succeeds('newlogin');
file_put_contents(__DIR__ . '/../../.cookies_test/11111_' . now()->timestamp . '.txt', 'oldlogin-testsession.srv-nami06');
app(Authenticator::class)->login(12345, 'secret');
app(LoginFake::class)->assertSent(12345, 'secret');
$this->assertFileExists(__DIR__ . '/../../.cookies_test/11111_' . now()->timestamp . '.txt');
$this->assertFileExists(__DIR__ . '/../../.cookies_test/12345_' . now()->timestamp . '.txt');
}
public function testItRefreshesLogin(): void
{
app(LoginFake::class)->succeeds('newlogin');
$auth = app(Authenticator::class)->login(12345, 'secret');
rename(
__DIR__ . '/../../.cookies_test/12345_' . now()->timestamp . '.txt',
__DIR__ . '/../../.cookies_test/12345_' . now()->subMinutes(51)->timestamp . '.txt'
);
$auth->refresh();
Http::assertSentCount(2);
}
}