diff --git a/src/Api.php b/src/Api.php index 911b332..5635f3d 100644 --- a/src/Api.php +++ b/src/Api.php @@ -11,25 +11,21 @@ use Illuminate\Support\Collection; use Zoomyboy\LaravelNami\Backend\Backend; use Zoomyboy\LaravelNami\Concerns\IsNamiMember; use Zoomyboy\LaravelNami\NamiException; +use Illuminate\Support\Facades\Cache; +use Zoomyboy\LaravelNami\Cookies\Cookie; class Api { - public $cookie; + private $cookie; public $loggedIn = null; public static $url = 'https://nami.dpsg.de'; - public function __construct() { - $this->cookie = new \GuzzleHttp\Cookie\CookieJar(); + public function __construct($cookieStore) { + $this->cookie = $cookieStore; } public function http() { - return Backend::cookie($this->cookie); - } - - public function setUser(NamiUser $user) { - $this->user = $user; - - return $this; + return Backend::init($this->cookie); } public function findNr($nr) { @@ -51,6 +47,8 @@ class Api { return Member::fromNami($data); } + + $this->exception('Search failed', ['url' => $url], $response->json()); } protected function loggedInAlready(): bool { @@ -58,7 +56,9 @@ class Api { } public function login($mglnr = null, $password = null, $groupid = null): self { - if ($this->loggedIn) { return $this; } + if ($this->cookie->resolve($mglnr)) { + return $this; + } $mglnr = $mglnr ?: config('nami.auth.mglnr'); $password = $password ?: config('nami.auth.password'); @@ -78,6 +78,7 @@ class Api { throw $e; } + $this->cookie->store($mglnr); $this->loggedIn = $mglnr; return $this; @@ -92,7 +93,7 @@ class Api { $response = $this->http()->put(self::$url.'/ica/rest/nami/mitglied/filtered-for-navigation/gruppierung/gruppierung/'.$member->group_id.'/'.$member->id, $member->toNami()); if (!data_get($response->json(), 'id')) { - throw new NamiException('update failed'); + $this->exception('Update failed', $response->json(), $member->toNami()); } } @@ -246,5 +247,9 @@ class Api { return $member->toArray(); } + private function exception($message, $request, $response) { + throw (new NamiException($message))->response($response)->request($request); + } + } diff --git a/src/Authentication/NamiGuard.php b/src/Authentication/NamiGuard.php index 1a005c4..bf8da36 100644 --- a/src/Authentication/NamiGuard.php +++ b/src/Authentication/NamiGuard.php @@ -47,7 +47,7 @@ class NamiGuard { return; } - return new NamiUser($cache); + return NamiUser::fromPayload($cache); } public function attempt(array $credentials = [], $remember = false) { @@ -57,7 +57,7 @@ class NamiGuard { 'credentials' => $credentials ]; - $this->setUser(new NamiUser($payload)); + $this->setUser(NamiUser::fromPayload($payload)); $key = $this->newCacheKey(); Cache::forever("namiauth-{$key}", $payload); $this->updateSession($key); diff --git a/src/Backend/FakeBackend.php b/src/Backend/FakeBackend.php index 325f0ed..903d2e0 100644 --- a/src/Backend/FakeBackend.php +++ b/src/Backend/FakeBackend.php @@ -5,7 +5,8 @@ namespace Zoomyboy\LaravelNami\Backend; use Illuminate\Http\Client\Response; use Illuminate\Support\Facades\Http; use GuzzleHttp\Psr7\Response as GuzzleResponse; -use GuzzleHttp\Cookie\SetCookie; +use Illuminate\Support\Str; +use Illuminate\Support\Facades\Cache; class FakeBackend { @@ -19,10 +20,11 @@ class FakeBackend { } public function addMember(array $member) { + $member['mitgliedsNummer'] = $member['id']; $this->members->push($member); } - public function cookie($cookie) { + public function init($cookie) { $this->cookie = $cookie; return $this; } @@ -104,9 +106,35 @@ class FakeBackend { ]); } + if (Str::contains($url, 'search-multi/result-list')) { + $query = parse_url($url)['query']; + parse_str($query, $q); + $params = json_decode($q['searchedValues'], true); + if (array_keys($params) === ['mitgliedsNummber']) { + return $this->findNr($params['mitgliedsNummber']); + } + } + $this->urlNotFoundException($url); } + public function findNr($nr) { + $found = $this->members->first(fn($m) => $m['id'] === $nr); + + $found = [ + 'entries_mitgliedsNummer' => $found['mitgliedsNummer'], + 'entries_vorname' => $found['vorname'], + 'entries_nachname' => $found['nachname'], + ]; + + return $this->response([ + "success" => true, + "data" => [$found], + "responseType" => "OK", + "totalEntries" => 1 + ]); + } + public function post($url, $data) { if ($url === 'https://nami.dpsg.de/ica/rest/nami/auth/manual/sessionStartup') { if (!data_get($data, 'username') || !data_get($data, 'password')) { @@ -115,9 +143,7 @@ class FakeBackend { if ($this->passwords[data_get($data, 'username')] === data_get($data, 'password')) { $this->loggedInAs = data_get($data, 'username'); - $this->cookie->setCookie(tap(SetCookie::fromString('JSESSIONID=rZMBv1McDAJ-KukQ6BboJBTq.srv-nami06; path=/ica'), function($cookie) { - $cookie->setDomain('nami.dpsg.de'); - })); + $this->cookie->set($data['username'], 'rZMBv1McDAJ-KukQ6BboJBTq.srv-nami06'); return $this->response([ "statusCode" => 0, ]); diff --git a/src/Backend/LiveBackend.php b/src/Backend/LiveBackend.php index e20f196..d3dd727 100644 --- a/src/Backend/LiveBackend.php +++ b/src/Backend/LiveBackend.php @@ -3,11 +3,12 @@ namespace Zoomyboy\LaravelNami\Backend; use Illuminate\Support\Facades\Http; +use Zoomyboy\LaravelNami\Cookies\Cookie; class LiveBackend { - public static function cookie($cookie) { - return Http::withOptions(['cookies' => $cookie]); + public static function init($cookie) { + return Http::withOptions(['cookies' => $cookie->forBackend()]); } } diff --git a/src/Cookies/CacheCookie.php b/src/Cookies/CacheCookie.php new file mode 100644 index 0000000..98f7a43 --- /dev/null +++ b/src/Cookies/CacheCookie.php @@ -0,0 +1,50 @@ +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}", $this->store->getCookieByName('JSESSIONID')->getValue()); + } + + /** + * 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); + return true; + } + +} diff --git a/src/Cookies/Cookie.php b/src/Cookies/Cookie.php new file mode 100644 index 0000000..a139233 --- /dev/null +++ b/src/Cookies/Cookie.php @@ -0,0 +1,13 @@ +loggedIn = true; + } + + public function resolve($mglnr) { + return $this->loggedIn; + } + +} diff --git a/src/Member.php b/src/Member.php index d20eb4d..f1853f6 100644 --- a/src/Member.php +++ b/src/Member.php @@ -46,6 +46,7 @@ class Member extends Model { 'regionId' => 'region_id', 'landId' => 'country_id', 'beitragsartId' => 'fee_id', + 'version' => 'version', ]; protected $casts = []; @@ -73,6 +74,16 @@ class Member extends Model { 'vorname' => $this->firstname, 'nachname' => $this->lastname, 'spitzname' => $this->nickname ?: '', + 'strasse' => $this->address, + 'plz' => $this->zip, + 'ort' => $this->location, + 'eintrittsdatum' => $this->joined_at.'T00:00:00', + 'version' => $this->version, + 'beitragsartId' => $this->fee_id, + 'regionId' => $this->region_id, + 'landId' => $this->country_id, + 'staatsangehoerigkeitId' => $this->nationality_id, + 'geburtsDatum' => $this->birthday, 'geschlechtId' => $this->gender_id ?: Gender::getNullValue(), 'gruppierungId' => $this->group_id, 'id' => $this->id, diff --git a/src/NamiException.php b/src/NamiException.php index 68a6aaf..2d2517f 100644 --- a/src/NamiException.php +++ b/src/NamiException.php @@ -7,6 +7,8 @@ use Illuminate\Support\Str; class NamiException extends \Exception { private $data; + public $response; + public $request; public function setData($data) { $this->data = $data; @@ -15,4 +17,16 @@ class NamiException extends \Exception { public function getData() { return $this->data; } + + public function request($request) { + $this->request = $request; + + return $this; + } + + public function response($response) { + $this->response = $response; + + return $this; + } } diff --git a/src/NamiUser.php b/src/NamiUser.php index 2f2eda8..e3e1bb8 100644 --- a/src/NamiUser.php +++ b/src/NamiUser.php @@ -4,15 +4,25 @@ namespace Zoomyboy\LaravelNami; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; +use Cache; -class NamiUser extends Model implements Authenticatable { +class NamiUser implements Authenticatable { public $mglnr; public $password; - public function __construct($payload) { - $this->mglnr = data_get($payload, 'credentials.mglnr'); - $this->password = data_get($payload, 'credentials.password'); + public function __construct($attributes) { + $this->mglnr = $attributes['mglnr']; + $this->password = $attributes['password']; + } + + public static function fromPayload($payload) { + $user = new static([ + 'mglnr' => data_get($payload, 'credentials.mglnr'), + 'password' => data_get($payload, 'credentials.password'), + ]); + + return $user; } public function api() { @@ -27,12 +37,20 @@ class NamiUser extends Model implements Authenticatable { return 'mglnr'; } - public function getFirstnameAttribute() { - return $this->api()->findNr($this->mglnr)->vorname; + public function getMglnr() { + return $this->mglnr; } - public function getLastnameAttribute() { - return $this->api()->findNr($this->mglnr)->nachname; + public function getFirstname() { + return Cache::remember('member-'.$this->mglnr.'-firstname', 3600, function() { + return $this->api()->findNr($this->mglnr)->firstname; + }); + } + + public function getLastname() { + return Cache::remember('member-'.$this->mglnr.'-lastname', 3600, function() { + return $this->api()->findNr($this->mglnr)->lastname; + }); } public function getAuthIdentifier() { @@ -52,4 +70,5 @@ class NamiUser extends Model implements Authenticatable { public function getRememberTokenName() { return null; } + } diff --git a/src/Providers/NamiServiceProvider.php b/src/Providers/NamiServiceProvider.php index 622e56b..eab48bc 100644 --- a/src/Providers/NamiServiceProvider.php +++ b/src/Providers/NamiServiceProvider.php @@ -7,6 +7,7 @@ use GuzzleHttp\Client as GuzzleClient; use Illuminate\Support\ServiceProvider; use Zoomyboy\LaravelNami\Backend\LiveBackend; use Zoomyboy\LaravelNami\Api; +use Zoomyboy\LaravelNami\Cookies\CacheCookie; use Zoomyboy\LaravelNami\Authentication\NamiGuard; class NamiServiceProvider extends ServiceProvider @@ -20,10 +21,13 @@ class NamiServiceProvider extends ServiceProvider public function register() { $this->app->singleton('nami.api', function() { - return new Api(); + return new Api($this->app['nami.cookie']); }); $this->app->bind('nami.backend', function() { return new LiveBackend(); }); + $this->app->singleton('nami.cookie', function() { + return new CacheCookie(); + }); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 73d4009..c4696d2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,6 +6,8 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Http; use Zoomyboy\LaravelNami\Tests\Stub\Member; use Zoomyboy\LaravelNami\Providers\NamiServiceProvider; +use Zoomyboy\LaravelNami\Cookies\Cookie; +use Zoomyboy\LaravelNami\Cookies\FakeCookie; class TestCase extends \Orchestra\Testbench\TestCase { @@ -13,6 +15,12 @@ class TestCase extends \Orchestra\Testbench\TestCase public $bruteJson = '{"servicePrefix":null,"methodCall":null,"response":null,"statusCode":3000,"statusMessage":"Die höchste Anzahl von Login-Versuchen wurde erreicht. Ihr Konto ist für 15 Minuten gesperrt worden. Nach Ablauf dieser Zeitspanne wird ihr Zugang wieder freigegeben.","apiSessionName":"JSESSIONID","apiSessionToken":"tGlSpMMij9ruHfeiUYjO7SD2","minorNumber":0,"majorNumber":0}'; public $wrongCredentialsJson = '{"servicePrefix":null,"methodCall":null,"response":null,"statusCode":3000,"statusMessage":"Benutzer nicht gefunden oder Passwort falsch.","apiSessionName":"JSESSIONID","apiSessionToken":"v7lrjgPBbXInJR57qJzVIJ05","minorNumber":0,"majorNumber":0}'; + public function setUp(): void { + parent::setUp(); + + Cookie::swap(new FakeCookie()); + } + protected function getPackageProviders($app) { return [ NamiServiceProvider::class ]; diff --git a/tests/Unit/PushMemberTest.php b/tests/Unit/PushMemberTest.php index 6737ef8..19f03df 100644 --- a/tests/Unit/PushMemberTest.php +++ b/tests/Unit/PushMemberTest.php @@ -24,6 +24,8 @@ class PushMemberTest extends TestCase 'group_id' => 103, 'nickname' => 'spitz1', 'gender_id' => 17, + 'joined_at' => '2021-02-02T00:00:00', + 'birthday' => '2021-02-02', 'id' => 16, ], [ 'firstname' => 'Jane', @@ -31,6 +33,8 @@ class PushMemberTest extends TestCase 'nickname' => null, 'group_id' => 103, 'gender_id' => null, + 'joined_at' => '2021-02-02T00:00:00', + 'birthday' => '2021-02-02', 'id' => 17, ] ];