Skip to content

Commit

Permalink
Multiple ESI Tokens #32
Browse files Browse the repository at this point in the history
- Implement login callback to add the new token.
  • Loading branch information
tkhamez committed Aug 29, 2021
1 parent cc62623 commit f6668d9
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 26 deletions.
36 changes: 28 additions & 8 deletions backend/src/Controller/User/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ public function callback(
}

// handle login
switch ($this->getLoginIdFromState($state)) {
$loginId = $this->getLoginIdFromState($state);
$success = false;
switch ($loginId) {
case EveLogin::ID_DEFAULT:
case EveLogin::ID_MANAGED:
$success = $userAuth->authenticate($eveAuth);
Expand All @@ -196,8 +198,6 @@ public function callback(
case EveLogin::ID_MAIL:
if (in_array(Role::SETTINGS, $userAuth->getRoles())) {
$success = $mailService->storeMailCharacter($eveAuth);
} else {
$success = false;
}
$successMessage = 'Mail character authenticated.';
$errorMessage = 'Failed to store character.';
Expand All @@ -208,10 +208,22 @@ public function callback(
$success = $memberTrackingService->verifyAndStoreDirector($eveAuth);
break;
default:
# TODO in-game roles (scopes were already checked above) and add token to character
$success = false;
$successMessage = 'TODO';
$errorMessage = 'TODO';
$successMessage = 'ESI token added.';
$errorMessage = '';
$eveLogin = $this->repositoryFactory->getEveLoginRepository()->find($loginId);
if (!$eveLogin) {
$errorMessage = 'Invalid login link.';
} else {
# TODO check in-game roles (scopes were already checked above)
if ($userAuth->addToken($eveLogin, $eveAuth)) {
$success = true;
} else {
// Not logged in or
// character not found on this account or
// failed to save ESI token
$errorMessage = 'Error adding the ESI token to a character on the logged in account.';
}
}
}

$this->session->set(self::SESS_AUTH_RESULT, [
Expand Down Expand Up @@ -345,7 +357,15 @@ private function getLoginScopes(string $state): array
return [EveLogin::SCOPE_ROLES, EveLogin::SCOPE_TRACKING, EveLogin::SCOPE_STRUCTURES];
}

$scopes = $this->config['eve']['scopes'];
$scopes = '';
if (in_array($loginId, [EveLogin::ID_DEFAULT, EveLogin::ID_ALT])) {
$scopes = $this->config['eve']['scopes'];
} else {
$eveLogin = $this->repositoryFactory->getEveLoginRepository()->find($loginId);
if ($eveLogin) {
$scopes = $eveLogin->getEsiScopes();
}
}
if (trim($scopes) !== '') {
$scopes = explode(' ', $scopes);
} else {
Expand Down
10 changes: 10 additions & 0 deletions backend/src/Entity/Player.php
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,16 @@ public function hasCharacter(int $charId): bool
return false;
}

public function getCharacter(int $characterId): ?Character
{
foreach ($this->getCharacters() as $c) {
if ($c->getId() === $characterId) {
return $c;
}
}
return null;
}

/**
* @param int[] $alliances
* @param int[] $corporations
Expand Down
40 changes: 40 additions & 0 deletions backend/src/Service/UserAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Eve\Sso\EveAuthentication;
use Neucore\Entity\Character;
use Neucore\Entity\EsiToken;
use Neucore\Entity\EveLogin;
use Neucore\Entity\RemovedCharacter;
use Neucore\Entity\Role;
use Neucore\Exception\RuntimeException;
Expand Down Expand Up @@ -34,6 +36,11 @@ class UserAuth implements RoleProviderInterface
*/
private $accountService;

/**
* @var ObjectManager
*/
private $objectManager;

/**
* @var RepositoryFactory
*/
Expand All @@ -52,11 +59,13 @@ class UserAuth implements RoleProviderInterface
public function __construct(
SessionData $session,
Account $charService,
ObjectManager $objectManager,
RepositoryFactory $repositoryFactory,
LoggerInterface $log
) {
$this->session = $session;
$this->accountService = $charService;
$this->objectManager = $objectManager;
$this->repositoryFactory = $repositoryFactory;
$this->log = $log;
}
Expand Down Expand Up @@ -183,6 +192,37 @@ public function addAlt(EveAuthentication $eveAuth): bool
return $this->accountService->updateAndStoreCharacterWithPlayer($alt, $eveAuth, true);
}

/**
* @param EveLogin $eveLogin An instance attached to the entity manager.
* @return bool False if user is not logged in, character was not found on the account or if save failed.
*/
public function addToken(EveLogin $eveLogin, EveAuthentication $eveAuth): bool
{
$user = $this->getUser();
$character = $user ? $user->getPlayer()->getCharacter($eveAuth->getCharacterId()) : null;
if (!$user || !$character) {
return false;
}

$esiToken = $this->repositoryFactory->getEsiTokenRepository()->findOneBy([
'character' => $character,
'eveLogin' => $eveLogin
]);
if (!$esiToken) {
$esiToken = new EsiToken();
$esiToken->setEveLogin($eveLogin);
$esiToken->setCharacter($character);
$this->objectManager->persist($esiToken);
}

$token = $eveAuth->getToken();
$esiToken->setAccessToken($token->getToken());
$esiToken->setRefreshToken((string)$token->getRefreshToken());
$esiToken->setExpires((int)$token->getExpires());

return $this->objectManager->flush();
}

/**
* @return void
*/
Expand Down
160 changes: 153 additions & 7 deletions backend/tests/Functional/Controller/User/AuthControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Neucore\Entity\EveLogin;
use Neucore\Entity\Role;
use Neucore\Entity\SystemVariable;
use Neucore\Factory\RepositoryFactory;
use Neucore\Service\SessionData;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Response;
Expand Down Expand Up @@ -53,8 +54,7 @@ public function testLogin_CustomLogin()
{
// add EveLogin
$loginId = 'custom1';
$eveLogin = new EveLogin();
$eveLogin->setId($loginId);
$eveLogin = (new EveLogin())->setId($loginId);
$this->helper->getObjectManager()->persist($eveLogin);
$this->helper->getObjectManager()->flush();

Expand Down Expand Up @@ -183,14 +183,160 @@ public function testCallback_InvalidStateException()
);
}

public function testCallback_CustomLogin()
/**
* @throws \Exception
*/
public function testCallback_CustomLogin_MissingEveLogin()
{
# TODO
// add EveLogin
$charId = 123; // the ID used in Helper::generateToken
$this->helper->addCharacterMain('Test User', $charId, [Role::USER], [], false); // without ESI token
$this->loginUser($charId);

list($token, $keySet) = Helper::generateToken([]);
$state = AuthController::getStatePrefix('custom1') . self::$state;
$_SESSION['auth_state'] = $state;

$this->client->setResponse(
new Response(200, [], // for getAccessToken()
'{"access_token": ' . json_encode($token) . ',
"expires_in": 1200,
"refresh_token": "gEy...fM0"}'),
new Response(200, [], '{"keys": ' . \json_encode($keySet) . '}') // for JWT key set
);

$response = $this->runApp(
'GET',
'/login-callback?state='.$state,
null,
null,
[ClientInterface::class => $this->client]
);

$this->assertSame(302, $response->getStatusCode());
$this->assertSame(['success' => false, 'message' => 'Invalid login link.'], $_SESSION['auth_result']);
}

public function testCallback_CustomLoginRoleError()
/**
* @throws \Exception
*/
public function testCallback_CustomLogin_NotLoggedIn()
{
# TODO
// add EveLogin
$loginId = 'custom1';
$charId = 123; // the ID used in Helper::generateToken
$this->helper->getEm()->persist((new EveLogin())->setId($loginId)->setEsiScopes('scope1'));
$this->helper->addCharacterMain('Test User', $charId, [Role::USER], [], false); // without ESI token
// not logged in

list($token, $keySet) = Helper::generateToken(['scope1']);
$state = AuthController::getStatePrefix($loginId) . self::$state;
$_SESSION['auth_state'] = $state;

$this->client->setResponse(
new Response(200, [], // for getAccessToken()
'{"access_token": ' . json_encode($token) . ',
"expires_in": 1200,
"refresh_token": "gEy...fM0"}'),
new Response(200, [], '{"keys": ' . \json_encode($keySet) . '}') // for JWT key set
);

$response = $this->runApp(
'GET',
'/login-callback?state='.$state,
null,
null,
[ClientInterface::class => $this->client]
);

$this->assertSame(302, $response->getStatusCode());
$this->assertSame(
['success' => false, 'message' => 'Error adding the ESI token to a character on the logged in account.'],
$_SESSION['auth_result']
);
}

/**
* @throws \Exception
*/
public function testCallback_CustomLogin_CharacterNotOnAccount()
{
// add EveLogin
$loginId = 'custom1';
$charId = 456; // Not the ID used in Helper::generateToken
$this->helper->getEm()->persist((new EveLogin())->setId($loginId)->setEsiScopes('scope1'));
$this->helper->addCharacterMain('Test User', $charId, [Role::USER], [], false); // without ESI token
$this->loginUser($charId);

list($token, $keySet) = Helper::generateToken(['scope1']);
$state = AuthController::getStatePrefix($loginId) . self::$state;
$_SESSION['auth_state'] = $state;

$this->client->setResponse(
new Response(200, [], // for getAccessToken()
'{"access_token": ' . json_encode($token) . ',
"expires_in": 1200,
"refresh_token": "gEy...fM0"}'),
new Response(200, [], '{"keys": ' . \json_encode($keySet) . '}') // for JWT key set
);

$response = $this->runApp(
'GET',
'/login-callback?state='.$state,
null,
null,
[ClientInterface::class => $this->client]
);

$this->assertSame(302, $response->getStatusCode());
$this->assertSame(
['success' => false, 'message' => 'Error adding the ESI token to a character on the logged in account.'],
$_SESSION['auth_result']
);
}

/**
* @throws \Exception
*/
public function testCallback_CustomLogin_Success()
{
// add EveLogin
$loginId = 'custom1';
$charId = 123; // the ID used in Helper::generateToken
$this->helper->getEm()->persist((new EveLogin())->setId($loginId)->setEsiScopes('scope1'));
$this->helper->addCharacterMain('Test User', $charId, [Role::USER], [], false); // without ESI token
$this->loginUser($charId);

list($token, $keySet) = Helper::generateToken(['scope1']);
$state = AuthController::getStatePrefix($loginId) . self::$state;
$_SESSION['auth_state'] = $state;

$this->client->setResponse(
new Response(200, [], // for getAccessToken()
'{"access_token": ' . json_encode($token) . ',
"expires_in": 1200,
"refresh_token": "gEy...fM0"}'),
new Response(200, [], '{"keys": ' . \json_encode($keySet) . '}') // for JWT key set
);

$response = $this->runApp(
'GET',
'/login-callback?state='.$state,
null,
null,
[ClientInterface::class => $this->client]
);

$this->assertSame(302, $response->getStatusCode());
$this->assertSame(['success' => true, 'message' => 'ESI token added.'], $_SESSION['auth_result']);

$esiTokens = (new RepositoryFactory($this->helper->getObjectManager()))->getEsiTokenRepository()->findBy([]);
$this->assertSame(1, count($esiTokens));
$this->assertSame($charId, $esiTokens[0]->getCharacter()->getId());
$this->assertSame($loginId, $esiTokens[0]->getEveLogin()->getId());
$this->assertSame('gEy...fM0', $esiTokens[0]->getRefreshToken());
$this->assertSame($token, $esiTokens[0]->getAccessToken());
$this->assertLessThanOrEqual(time() + 1200, $esiTokens[0]->getExpires());
}

/**
Expand Down Expand Up @@ -257,7 +403,7 @@ public function testCallback_DefaultSuccess()
null,
null,
[ClientInterface::class => $this->client],
['NEUCORE_EVE_SCOPES=read-this and-this', 'NEUCORE_EVE_DATASOURCE=tranquility']
['NEUCORE_EVE_SCOPES=read-this and-this', 'NEUCORE_EVE_DATASOURCE=tranquility']
);
$this->assertSame(302, $response->getStatusCode());

Expand Down
16 changes: 12 additions & 4 deletions backend/tests/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ public function getAccountService(Logger $logger, Client $client): Account
public function getUserAuthService(Logger $logger, Client $client): UserAuth
{
$repoFactory = RepositoryFactory::getInstance($this->getObjectManager());
$objectManager = new \Neucore\Service\ObjectManager($this->getObjectManager(), $logger);
$accountService = $this->getAccountService($logger, $client);
return new UserAuth(new SessionData(), $accountService, $repoFactory, $logger);
return new UserAuth(new SessionData(), $accountService, $objectManager, $repoFactory, $logger);
}

public function getDbName(): string
Expand Down Expand Up @@ -278,8 +279,13 @@ public function addGroups(array $groups): array
return $groupEntities;
}

public function addCharacterMain(string $name, int $charId, array $roles = [], array $groups = []): Character
{
public function addCharacterMain(
string $name,
int $charId,
array $roles = [],
array $groups = [],
bool $withEsiToken = true
): Character {
$om = $this->getObjectManager();

$player = new Player();
Expand All @@ -296,7 +302,9 @@ public function addCharacterMain(string $name, int $charId, array $roles = [], a
$char->setPlayer($player);
$player->addCharacter($char);

$this->createOrUpdateEsiToken($char);
if ($withEsiToken) {
$this->createOrUpdateEsiToken($char);
}

foreach ($this->addRoles($roles) as $role) {
$player->addRole($role);
Expand Down
Loading

0 comments on commit f6668d9

Please sign in to comment.