Add login test

This commit is contained in:
philipp lang 2022-02-18 17:56:04 +01:00
parent 829dd01358
commit b78b0a5b21
10 changed files with 222 additions and 206 deletions

0
.cookies/.gitkeep Normal file
View File

2
.gitignore vendored
View File

@ -3,3 +3,5 @@
*.swo *.swo
*.swm *.swm
tags tags
.cookies
/.phpunit.result.cache

View File

@ -5,30 +5,31 @@ namespace Zoomyboy\LaravelNami;
use App\Conf; use App\Conf;
use App\Nami\Exceptions\TooManyLoginAttemptsException; use App\Nami\Exceptions\TooManyLoginAttemptsException;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\LazyCollection; use Illuminate\Support\LazyCollection;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Log; use Log;
use Zoomyboy\LaravelNami\Authentication\Cookie;
use Zoomyboy\LaravelNami\Backend\Backend; use Zoomyboy\LaravelNami\Backend\Backend;
use Zoomyboy\LaravelNami\Concerns\IsNamiMember; use Zoomyboy\LaravelNami\Concerns\IsNamiMember;
use Zoomyboy\LaravelNami\Cookies\Cookie;
use Zoomyboy\LaravelNami\Exceptions\RightException; use Zoomyboy\LaravelNami\Exceptions\RightException;
use Zoomyboy\LaravelNami\NamiException; use Zoomyboy\LaravelNami\NamiException;
class Api { class Api {
private $cookie; public string $url = 'https://nami.dpsg.de';
public $loggedIn = null; private Cookie $cookie;
public static $url = 'https://nami.dpsg.de';
public function __construct($cookieStore) { public function __construct(Cookie $cookie)
$this->cookie = $cookieStore; {
$this->cookie = $cookie;
} }
public function http() { public function http(): PendingRequest {
return Backend::init($this->cookie); return Http::withOptions(['cookies' => $this->cookie->load()]);
} }
public function findNr(int $nr): Member public function findNr(int $nr): Member
@ -54,7 +55,7 @@ class Api {
$page = 1; $page = 1;
while (!isset ($totalEntries) || ($page-1) * 100 + 1 <= $totalEntries) { while (!isset ($totalEntries) || ($page-1) * 100 + 1 <= $totalEntries) {
$start = ($page-1) * 100; $start = ($page-1) * 100;
$url = self::$url.'/ica/rest/nami/search-multi/result-list?searchedValues='.rawurlencode(json_encode((object) $payload) ?: '{}').'&page='.$page.'&start='.$start.'&limit=100'; $url = $this->url.'/ica/rest/nami/search-multi/result-list?searchedValues='.rawurlencode(json_encode((object) $payload) ?: '{}').'&page='.$page.'&start='.$start.'&limit=100';
$response = $this->http()->get($url); $response = $this->http()->get($url);
$totalEntries = $response->json()['totalEntries']; $totalEntries = $response->json()['totalEntries'];
if ($response->json()['success'] !== true) { if ($response->json()['success'] !== true) {
@ -70,12 +71,8 @@ class Api {
}); });
} }
protected function loggedInAlready(): bool {
return $this->loggedIn !== null;
}
public function deleteMember($id) { public function deleteMember($id) {
$url = self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/mglschaft-beenden'; $url = $this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/mglschaft-beenden';
$payload = [ $payload = [
'id' => $id, 'id' => $id,
'isConfirmed' => 'true', 'isConfirmed' => 'true',
@ -88,19 +85,18 @@ class Api {
} }
} }
public function login($mglnr = null, $password = null, $groupid = null): self { public function login($mglnr = null, $password = null): self {
$resolved = $this->cookie->resolve($mglnr); if ($this->cookie->isLoggedIn()) {
if ($resolved && !$this->cookie->isExpired()) {
return $this; return $this;
} }
$this->cookie->beforeLogin();
$mglnr = $mglnr ?: config('nami.auth.mglnr'); $mglnr = $mglnr ?: config('nami.auth.mglnr');
$password = $password ?: config('nami.auth.password'); $password = $password ?: config('nami.auth.password');
$groupid = $groupid ?: config('nami.auth.groupid');
$this->http()->get(self::$url.'/ica/pages/login.jsp'); $this->http()->get($this->url.'/ica/pages/login.jsp');
$response = $this->http()->asForm()->post(self::$url.'/ica/rest/nami/auth/manual/sessionStartup', [ $response = $this->http()->asForm()->post($this->url.'/ica/rest/nami/auth/manual/sessionStartup', [
'Login' => 'API', 'Login' => 'API',
'redirectTo' => './app.jsp', 'redirectTo' => './app.jsp',
'username' => $mglnr, 'username' => $mglnr,
@ -113,14 +109,14 @@ class Api {
throw $e; throw $e;
} }
$this->cookie->store($mglnr);
$this->loggedIn = $mglnr; $this->loggedIn = $mglnr;
$this->cookie->afterLogin();
return $this; return $this;
} }
public function membersOf($groupId): Collection { public function membersOf($groupId): Collection {
return collect($this->http()->get(self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist')->json()['data']); return collect($this->http()->get($this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist')->json()['data']);
} }
public function putMember(array $attributes) { public function putMember(array $attributes) {
@ -130,14 +126,14 @@ class Api {
$payload = array_merge($existing, $member->toNami()); $payload = array_merge($existing, $member->toNami());
$payload['kontoverbindung'] = json_encode(data_get($payload, 'kontoverbindung', [])); $payload['kontoverbindung'] = json_encode(data_get($payload, 'kontoverbindung', []));
$response = $this->http()->put( $response = $this->http()->put(
self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$member->group_id.'/'.$member->id, $this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$member->group_id.'/'.$member->id,
$payload $payload
); );
if (data_get($response->json(), 'success') !== true) { if (data_get($response->json(), 'success') !== true) {
$this->exception('Update failed', $member->toNami(), $response->json()); $this->exception('Update failed', $member->toNami(), $response->json());
} }
} else { } else {
$response = $this->http()->post(self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$member->group_id, $member->toNami()); $response = $this->http()->post($this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$member->group_id, $member->toNami());
if (data_get($response->json(), 'success') !== true) { if (data_get($response->json(), 'success') !== true) {
$this->exception('Update failed', $member->toNami(), $response->json()); $this->exception('Update failed', $member->toNami(), $response->json());
} }
@ -151,10 +147,10 @@ class Api {
public function putMembership(int $memberId, array $data): int public function putMembership(int $memberId, array $data): int
{ {
if (data_get($data, 'id')) { if (data_get($data, 'id')) {
$url = self::$url."/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/{$memberId}/{$data['id']}"; $url = $this->url."/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/{$memberId}/{$data['id']}";
$response = $this->http()->put($url, $data); $response = $this->http()->put($url, $data);
} else { } else {
$url = self::$url."/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/{$memberId}"; $url = $this->url."/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/{$memberId}";
$response = $this->http()->post($url, $data); $response = $this->http()->post($url, $data);
} }
if (data_get($response->json(), 'success') !== true) { if (data_get($response->json(), 'success') !== true) {
@ -169,7 +165,7 @@ class Api {
} }
public function membershipsOf($memberId): Collection { public function membershipsOf($memberId): Collection {
$url = self::$url.'/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/'.$memberId.'/flist'; $url = $this->url.'/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/'.$memberId.'/flist';
$r = $this->http()->get($url); $r = $this->http()->get($url);
@ -183,13 +179,13 @@ class Api {
} }
public function subactivitiesOf($activityId) { public function subactivitiesOf($activityId) {
return collect($this->http()->get(self::$url.'/ica/rest/nami/untergliederungauftaetigkeit/filtered/untergliederung/taetigkeit/'.$activityId)->json()['data'])->map(function($subactivity) { return collect($this->http()->get($this->url.'/ica/rest/nami/untergliederungauftaetigkeit/filtered/untergliederung/taetigkeit/'.$activityId)->json()['data'])->map(function($subactivity) {
return Subactivity::fromNami($subactivity); return Subactivity::fromNami($subactivity);
});; });;
} }
public function membership($memberId, $membershipId) { public function membership($memberId, $membershipId) {
$url = self::$url.'/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/'.$memberId.'/'.$membershipId; $url = $this->url.'/ica/rest/nami/zugeordnete-taetigkeiten/filtered-for-navigation/gruppierung-mitglied/mitglied/'.$memberId.'/'.$membershipId;
$response = $this->http()->get($url); $response = $this->http()->get($url);
Logger::http($url, $response, 'Single Membership '.$membershipId.' from '.$memberId, ['memberId' => $memberId]); Logger::http($url, $response, 'Single Membership '.$membershipId.' from '.$memberId, ['memberId' => $memberId]);
@ -203,7 +199,7 @@ class Api {
public function courses(): Collection public function courses(): Collection
{ {
$url = self::$url.'/ica/rest/module/baustein'; $url = $this->url.'/ica/rest/module/baustein';
$response = $this->http()->get($url); $response = $this->http()->get($url);
return collect($response->json()['data'])->map(function($course) { return collect($response->json()['data'])->map(function($course) {
@ -213,7 +209,7 @@ class Api {
public function coursesFor(int $memberId): Collection public function coursesFor(int $memberId): Collection
{ {
$url = self::$url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/flist"; $url = $this->url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/flist";
$response = $this->http()->get($url); $response = $this->http()->get($url);
if (!$response->ok() || $response->json()['success'] === false) { if (!$response->ok() || $response->json()['success'] === false) {
@ -221,7 +217,7 @@ class Api {
} }
return collect($response->json()['data'])->map(function($course) use ($memberId) { return collect($response->json()['data'])->map(function($course) use ($memberId) {
$single = $this->http()->get(self::$url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$course['id']}")['data']; $single = $this->http()->get($this->url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$course['id']}")['data'];
return (object) [ return (object) [
'id' => $single['id'], 'id' => $single['id'],
@ -240,7 +236,7 @@ class Api {
*/ */
public function createCourse(int $memberId, array $payload): int public function createCourse(int $memberId, array $payload): int
{ {
$response = $this->http()->post(self::$url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}", [ $response = $this->http()->post($this->url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}", [
'bausteinId' => $payload['course_id'], 'bausteinId' => $payload['course_id'],
'vstgName' => $payload['event_name'], 'vstgName' => $payload['event_name'],
'vstgTag' => Carbon::parse($payload['completed_at'])->format('Y-m-d').'T00:00:00', 'vstgTag' => Carbon::parse($payload['completed_at'])->format('Y-m-d').'T00:00:00',
@ -262,7 +258,7 @@ class Api {
*/ */
public function updateCourse(int $memberId, int $courseId, array $payload): void public function updateCourse(int $memberId, int $courseId, array $payload): void
{ {
$response = $this->http()->put(self::$url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$courseId}", [ $response = $this->http()->put($this->url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$courseId}", [
'bausteinId' => $payload['course_id'], 'bausteinId' => $payload['course_id'],
'vstgName' => $payload['event_name'], 'vstgName' => $payload['event_name'],
'vstgTag' => Carbon::parse($payload['completed_at'])->format('Y-m-d').'T00:00:00', 'vstgTag' => Carbon::parse($payload['completed_at'])->format('Y-m-d').'T00:00:00',
@ -276,7 +272,7 @@ class Api {
public function deleteCourse(int $memberId, int $courseId): void public function deleteCourse(int $memberId, int $courseId): void
{ {
$response = $this->http()->delete(self::$url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$courseId}"); $response = $this->http()->delete($this->url."/ica/rest/nami/mitglied-ausbildung/filtered-for-navigation/mitglied/mitglied/{$memberId}/{$courseId}");
if ($response->json() !== null && data_get($response->json(), 'success') !== true) { if ($response->json() !== null && data_get($response->json(), 'success') !== true) {
$this->exception('Course deletion failed', [], $response->json()); $this->exception('Course deletion failed', [], $response->json());
@ -284,7 +280,7 @@ class Api {
} }
public function member($groupId, $memberId) { public function member($groupId, $memberId) {
$url = self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/'.$memberId; $url = $this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/'.$memberId;
$response = $this->http()->get($url); $response = $this->http()->get($url);
Logger::http($url, $response, 'Show member '.$memberId, ['memberId' => $memberId]); Logger::http($url, $response, 'Show member '.$memberId, ['memberId' => $memberId]);
@ -316,7 +312,7 @@ class Api {
} }
public function groups($parentGroupId = null): Collection { public function groups($parentGroupId = null): Collection {
return collect($this->http()->get(self::$url.'/ica/rest/nami/gruppierungen/filtered-for-navigation/gruppierung/node/'.($parentGroupId ?: 'root'))->json()['data'])->map(function($group) use ($parentGroupId) { return collect($this->http()->get($this->url.'/ica/rest/nami/gruppierungen/filtered-for-navigation/gruppierung/node/'.($parentGroupId ?: 'root'))->json()['data'])->map(function($group) use ($parentGroupId) {
return Group::fromResponse($group, $parentGroupId); return Group::fromResponse($group, $parentGroupId);
}); });
} }
@ -332,51 +328,51 @@ class Api {
} }
public function genders(): Collection { public function genders(): Collection {
return collect($this->http()->get(self::$url."/ica/rest/baseadmin/geschlecht")['data'])->map(function($gender) { return collect($this->http()->get($this->url."/ica/rest/baseadmin/geschlecht")['data'])->map(function($gender) {
return Gender::fromNami($gender); return Gender::fromNami($gender);
})->filter(fn($gender) => !$gender->isNull); })->filter(fn($gender) => !$gender->isNull);
} }
public function nationalities(): Collection { public function nationalities(): Collection {
return collect($this->http()->get(self::$url."/ica/rest/baseadmin/staatsangehoerigkeit")['data'])->map(function($gender) { return collect($this->http()->get($this->url."/ica/rest/baseadmin/staatsangehoerigkeit")['data'])->map(function($gender) {
return Nationality::fromNami($gender); return Nationality::fromNami($gender);
}); });
} }
public function countries() { public function countries() {
return collect($this->http()->get(self::$url."/ica/rest/baseadmin/land")['data'])->map(function($country) { return collect($this->http()->get($this->url."/ica/rest/baseadmin/land")['data'])->map(function($country) {
return Country::fromNami($country); return Country::fromNami($country);
}); });
} }
public function regions() { public function regions() {
return collect($this->http()->get(self::$url."/ica/rest/baseadmin/region")['data'])->map(function($region) { return collect($this->http()->get($this->url."/ica/rest/baseadmin/region")['data'])->map(function($region) {
return Region::fromNami($region); return Region::fromNami($region);
}); });
} }
public function feesOf($groupid) { public function feesOf($groupid) {
return collect($this->http()->get(self::$url."/ica/rest/namiBeitrag/beitragsartmgl/gruppierung/{$groupid}")['data'])->map(function($fee) { return collect($this->http()->get($this->url."/ica/rest/namiBeitrag/beitragsartmgl/gruppierung/{$groupid}")['data'])->map(function($fee) {
return Fee::fromNami($fee); return Fee::fromNami($fee);
}); });
} }
public function confessions(): Collection { public function confessions(): Collection {
return collect($this->http()->get(self::$url."/ica/rest/baseadmin/konfession")['data'])->map(function($gender) { return collect($this->http()->get($this->url."/ica/rest/baseadmin/konfession")['data'])->map(function($gender) {
return Confession::fromNami($gender); return Confession::fromNami($gender);
}); });
} }
public function activities($groupId) { public function activities($groupId) {
return collect($this->http()->get(self::$url."/ica/rest/nami/taetigkeitaufgruppierung/filtered/gruppierung/gruppierung/".$groupId)['data'])->map(function($activity) { return collect($this->http()->get($this->url."/ica/rest/nami/taetigkeitaufgruppierung/filtered/gruppierung/gruppierung/".$groupId)['data'])->map(function($activity) {
return Activity::fromNami($activity); return Activity::fromNami($activity);
}); });
} }
public function memberOverviewOf(int $groupId): Collection public function memberOverviewOf(int $groupId): Collection
{ {
$url = self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist'; $url = $this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist';
$response = $this->http()->get($url); $response = $this->http()->get($url);
return collect($response['data'])->map(function($member) use ($groupId) { return collect($response['data'])->map(function($member) use ($groupId) {
@ -390,7 +386,7 @@ class Api {
} }
private function singleMemberFallback($groupId, $memberId) { private function singleMemberFallback($groupId, $memberId) {
$url = self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist'; $url = $this->url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$groupId.'/flist';
$response = $this->http()->get($url); $response = $this->http()->get($url);
$member = collect($response['data'])->first(function($member) use ($memberId) { $member = collect($response['data'])->first(function($member) use ($memberId) {

View File

@ -0,0 +1,86 @@
<?php
namespace Zoomyboy\LaravelNami\Authentication;
use Carbon\Carbon;
use GuzzleHttp\Cookie\FileCookieJar;
class Cookie {
public string $path = __DIR__.'/../../.cookies';
private FileCookieJar $cookie;
/**
* Loads the cookie for a new request
*
* @return FileCookieJar
*/
public function load(): FileCookieJar
{
$cookieFile = $this->file() ?: $this->newFileName();
return $this->cookie = new FileCookieJar($cookieFile);
}
/**
* Clears all cookies before logging in
*
* @return void
*/
public function beforeLogin(): void
{
while ($file = $this->file()) {
unlink($file);
}
}
/**
* Set last login to now after login
*
* @return void
*/
public function afterLogin(): void
{
$this->cookie->save($this->newFileName());
}
public function isLoggedIn(): bool
{
if ($this->file() === null) {
return false;
}
return ! $this->isExpired();
}
private function newFileName(): string
{
return $this->path.'/'.time().'.txt';
}
private function isExpired(): bool
{
$lastLoginTime = Carbon::createFromTimestamp(pathinfo($this->file(), PATHINFO_FILENAME));
return $lastLoginTime->addMinutes(50)->isPast();
}
/**
* Get the cookie file if it exists
*
* @return ?string
*/
private function file(): ?string
{
$files = glob($this->path.'/*');
if (!count($files)) {
return null;
}
return $files[0];
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace Zoomyboy\LaravelNami\Backend;
use Illuminate\Support\Facades\Facade;
class Backend extends Facade {
protected static function getFacadeAccessor() { return 'nami.backend'; }
}

View File

@ -1,14 +0,0 @@
<?php
namespace Zoomyboy\LaravelNami\Backend;
use Illuminate\Support\Facades\Http;
use Zoomyboy\LaravelNami\Cookies\Cookie;
class LiveBackend {
public static function init($cookie) {
return Http::withOptions(['cookies' => $cookie->forBackend()]);
}
}

View File

@ -1,60 +0,0 @@
<?php
namespace Zoomyboy\LaravelNami\Cookies;
use GuzzleHttp\Cookie\SetCookie;
use Illuminate\Support\Facades\Cache;
class CacheCookie {
private $store;
private $createdAt;
public function __construct() {
$this->store = new \GuzzleHttp\Cookie\CookieJar();
}
public function forBackend() {
return $this->store;
return \GuzzleHttp\Cookie\CookieJar::fromArray(['JSESSIONID' => Cache::get("namicookie-{$mglnr}")], 'nami.dpsg.de');
}
/**
* Store the current cookie in the cache
*/
public function store($mglnr) {
Cache::forever("namicookie-{$mglnr}", [
'cookie' => $this->store->getCookieByName('JSESSIONID')->getValue(),
'created_at' => now(),
]);
$this->createdAt = now();
}
public function isExpired() {
return $this->createdAt->addHour(1)->isPast();
}
/**
* Set a cookie by string
*/
public function set($mglnr, $cookie) {
$this->store->setCookie(tap(SetCookie::fromString('JSESSIONID='.$cookie.'; path=/ica'), function($cookie) {
$cookie->setDomain('nami.dpsg.de');
}));
}
/**
* Get the stored cookie from the cache
*/
public function resolve($mglnr) {
$cookie = Cache::get("namicookie-{$mglnr}");
if ($cookie === null) {
return false;
}
$this->set($mglnr, $cookie['cookie']);
$this->createdAt = $cookie['created_at'];
return true;
}
}

View File

@ -3,6 +3,7 @@
namespace Zoomyboy\LaravelNami\Providers; namespace Zoomyboy\LaravelNami\Providers;
use GuzzleHttp\Client as GuzzleClient; use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Cookie\CookieJarInterface;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Zoomyboy\LaravelNami\Api; use Zoomyboy\LaravelNami\Api;
@ -21,14 +22,8 @@ class NamiServiceProvider extends ServiceProvider
} }
public function register() { public function register() {
$this->app->singleton('nami.api', function() { $this->app->bind('nami.api', function() {
return new Api($this->app['nami.cookie']); return app(Api::class);
});
$this->app->bind('nami.backend', function() {
return new LiveBackend();
});
$this->app->singleton('nami.cookie', function() {
return new CacheCookie();
}); });
} }
} }

View File

@ -18,7 +18,7 @@ class TestCase extends \Orchestra\Testbench\TestCase
public function setUp(): void { public function setUp(): void {
parent::setUp(); parent::setUp();
Cookie::swap(new FakeCookie()); $this->clearCookies();
} }
protected function getPackageProviders($app) protected function getPackageProviders($app)
@ -30,19 +30,6 @@ class TestCase extends \Orchestra\Testbench\TestCase
return []; return [];
} }
protected function setCredentials() {
Config::set('nami.auth.mglnr', '11223');
Config::set('nami.auth.password', 'secret');
Config::set('nami.auth.groupid', '55555');
}
public function login() {
return [
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::response('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::response($this->successJson, 200)
];
}
public function fakeJson(string $file, array $data = []): string { public function fakeJson(string $file, array $data = []): string {
ob_start(); ob_start();
include(__DIR__.'/json/'.$file); include(__DIR__.'/json/'.$file);
@ -55,4 +42,11 @@ class TestCase extends \Orchestra\Testbench\TestCase
]; ];
} }
private function clearCookies(): void
{
foreach (glob(__DIR__.'/../.cookies/*') as $file) {
unlink($file);
}
}
} }

View File

@ -10,80 +10,106 @@ use Zoomyboy\LaravelNami\Tests\TestCase;
class LoginTest extends TestCase class LoginTest extends TestCase
{ {
/** public function test_first_successful_login(): void
* A basic unit test example.
*
* @return void
*/
public function test_first_successful_login()
{ {
Http::fake($this->login()); Http::fake($this->fakeSuccessfulLogin());
$this->setCredentials();
Nami::login(); Nami::login(12345, 'secret');
Http::assertSent(function($request) {
return $request->url() == 'https://nami.dpsg.de/ica/pages/login.jsp';
});
Http::assertSent(function($request) {
return $request->url() == 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup'
&& $request['username'] == '11223' && $request['password'] == 'secret' && $request['redirectTo'] == './app.jsp' && $request['Login'] == 'API';
});
Http::assertSentCount(2); Http::assertSentCount(2);
Http::assertSent(fn ($request) => $request->url() == 'https://nami.dpsg.de/ica/pages/login.jsp');
Http::assertSent(fn ($request) => $request->url() == 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup'
&& $request['username'] == '12345' && $request['password'] == 'secret'
&& $request['redirectTo'] == './app.jsp' && $request['Login'] == 'API'
);
} }
public function test_first_login_fails_because_of_bruteforce_protection() public function test_it_throws_exception_when_login_failed(): void
{ {
Http::fake([ Http::fake($this->fakeFailedLogin());
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::response('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::response($this->bruteJson, 200)
]);
$this->setCredentials();
try { try {
Nami::login(); Nami::login(12345, 'wrongpassword');
} catch (LoginException $e) {
$this->assertEquals(LoginException::WRONG_CREDENTIALS, $e->reason);
}
}
public function test_first_login_fails_because_of_bruteforce_protection(): void
{
Http::fake($this->fakeBruteforceFailure());
try {
Nami::login(12345, 'secret');
} catch (LoginException $e) { } catch (LoginException $e) {
$this->assertEquals(LoginException::TOO_MANY_FAILED_LOGINS, $e->reason); $this->assertEquals(LoginException::TOO_MANY_FAILED_LOGINS, $e->reason);
} }
} }
public function test_login_once_on_second_login() public function test_store_cookie_after_login(): void
{ {
Http::fake([ Http::fake($this->fakeSuccessfulLogin());
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::response('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::response($this->successJson, 200)
]);
$this->setCredentials(); Nami::login(12345, 'secret');
Nami::login(); $this->assertFileExists(__DIR__.'/../../.cookies/'.time().'.txt');
Nami::login(); }
public function test_dont_login_if_cookie_exists(): void
{
touch(__DIR__.'/../../.cookies/'.time().'.txt');
Nami::login(12345, 'secret');
Http::assertSentCount(0);
}
public function test_delete_expired_cookie_before_login(): void
{
$lastLogin = now()->subHour(2)->timestamp;
touch(__DIR__."/../../.cookies/{$lastLogin}.txt");
Http::fake($this->fakeSuccessfulLogin());
Nami::login(12345, 'secret');
Http::assertSentCount(2);
$this->assertFileDoesNotExist(__DIR__."/../../.cookies/{$lastLogin}.txt");
}
public function test_login_once_if_cookie_is_expired(): void
{
$lastLogin = now()->subHour()->subMinutes(10)->timestamp;
touch(__DIR__."/../../.cookies/{$lastLogin}.txt");
Http::fake($this->fakeSuccessfulLogin());
Nami::login(12345, 'secret');
Nami::login(12345, 'secret');
Http::assertSent(function($request) {
return $request->url() == 'https://nami.dpsg.de/ica/pages/login.jsp';
});
Http::assertSent(function($request) {
return $request->url() == 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup'
&& $request['username'] == '11223' && $request['password'] == 'secret' && $request['redirectTo'] == './app.jsp' && $request['Login'] == 'API';
});
Http::assertSentCount(2); Http::assertSentCount(2);
} }
public function test_login_check() private function fakeSuccessfulLogin(): array
{ {
Http::fake([ return [
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::response('<html></html>', 200), 'https://nami.dpsg.de/ica/pages/login.jsp' => Http::sequence()->push('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::sequence()->push($this->successJson, 200),
];
}
private function fakeFailedLogin(): array
{
return [
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::sequence()->push('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::sequence()->push($this->wrongCredentialsJson, 200) 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::sequence()->push($this->wrongCredentialsJson, 200)
]); ];
$this->setCredentials();
try {
Nami::login();
} catch(LoginException $e) {
$this->assertEquals(LoginException::WRONG_CREDENTIALS, $e->reason);
} }
Http::assertSentCount(2); private function fakeBruteforceFailure(): array
{
return [
'https://nami.dpsg.de/ica/pages/login.jsp' => Http::sequence()->push('<html></html>', 200),
'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup' => Http::sequence()->push($this->bruteJson, 200)
];
} }
} }