Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions lib/GaletteOAuth2/Authorization/UserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
use DI\Attribute\Inject;
use Galette\Core\Db;
use Galette\Core\History;
use Galette\Core\I18n;
use Galette\Core\Login;
use Galette\Core\Preferences;
use Galette\Entity\Adherent;
use Galette\Entity\Social;
use Galette\Entity\Status;
use GaletteOAuth2\Tools\Config;
use GaletteOAuth2\Tools\Debug;
use RKA\Session;
Expand All @@ -40,6 +42,7 @@ public function __construct(
private readonly Login $login,
private readonly History $history,
private readonly Preferences $preferences,
private readonly I18n $i18n,
private readonly Messages $flash,
#[Inject('oauth_session')]
private readonly Session $session
Expand Down Expand Up @@ -129,6 +132,34 @@ public function getUserData(int $id, string $acl, array $scopes, bool $legacy =
throw new UserAuthorizationException(_T('User not found.', 'oauth2'));
}

//data are sent to client applications, that do not share member's session
$current_lang = $this->i18n->getID();
if ($member->language && $member->language !== $current_lang) {
$this->i18n->changeLanguage($member->language);
}
try {
return $this->buildUserData($member, $acl, $scopes, $legacy);
} finally {
if ($this->i18n->getID() !== $current_lang) {
$this->i18n->changeLanguage($current_lang);
}
}
}

/**
* Build user data, in current language
*
* @param Adherent $member Member
* @param string $acl Requested authorization
* @param string[] $scopes Scopes
* @param bool $legacy Legacy mode for data
*
* @return array<string, mixed>
* @throws UserAuthorizationException
* @throws \Throwable
*/
private function buildUserData(Adherent $member, string $acl, array $scopes, bool $legacy): array
{
//check active member? Covers $acl === self::AUTH_ACTIVE
if (!$member->isActive()) {
throw new UserAuthorizationException(_T("Sorry, you can't login because you are not an active member.", "oauth2"));
Expand Down Expand Up @@ -209,7 +240,8 @@ public function getUserData(int $id, string $acl, array $scopes, bool $legacy =

//member:personal
if (in_array('member:personal', $scopes)) {
$oauth_data['birthdate'] = $member->birthdate;
//raw value: formatted dates follow current language
$oauth_data['birthdate'] = $member->rbirthdate;
$oauth_data['birthplace'] = $member->birth_place;
$oauth_data['job'] = $member->job;
$oauth_data['gender'] = $member->sgender;
Expand Down Expand Up @@ -272,12 +304,12 @@ public function getUserData(int $id, string $acl, array $scopes, bool $legacy =

//member:groups
if (in_array('member:groups', $scopes)) {
$oauth_data['groups'] = self::getUserGroups($member, $legacy);
$oauth_data['groups'] = $this->getUserGroups($member, $legacy);
}

//member:due_date
if (in_array('member:due_date', $scopes)) {
$oauth_data['due_date'] = $member->due_date;
$oauth_data['due_date'] = $member->rdue_date;
}

return $oauth_data;
Expand All @@ -291,7 +323,7 @@ public function getUserData(int $id, string $acl, array $scopes, bool $legacy =
*
* @return string[]
*/
protected static function getUserGroups(Adherent $member, bool $legacy = false): array
private function getUserGroups(Adherent $member, bool $legacy = false): array
{
$groups = array_map(
function ($group) {
Expand All @@ -300,7 +332,9 @@ function ($group) {
array_values($member->getGroups())
);

$groups[] = $member->sstatus;
//untranslated: groups are mapped by client applications, they must not depend on language
$status = new Status($this->zdb);
$groups[] = (string)$status->getLabel($member->status, false);

if ($member->isAdmin()) {
$groups[] = 'admin';
Expand Down
91 changes: 90 additions & 1 deletion tests/GaletteOAuth2/Authorization/tests/units/UserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public function testGetUserData(): void

$this->assertSame(
$expected_base + [
'birthdate' => $user_data['birthdate'],
'birthdate' => $this->dataAdherentOne()['ddn_adh'],
'birthplace' => 'Gonzalez-sur-Meunier',
'job' => 'Chef de fabrication',
'gender' => 'Unspecified',
Expand Down Expand Up @@ -277,6 +277,95 @@ public function testGetUserData(): void
);
}

/**
* Store a member speaking french
*
* @return \Galette\Entity\Adherent
*/
private function getFrenchMember(): \Galette\Entity\Adherent
{
$this->logSuperAdmin();
$adh = new \Galette\Entity\Adherent($this->zdb);
$adh->setDependencies(
$this->preferences,
$this->members_fields,
$this->history
);

$data = $this->dataAdherentOne();
$data['pref_lang'] = 'fr_FR';
$data['sexe_adh'] = \Galette\Entity\Adherent::WOMAN;
$check = $adh->check($data, [], []);
if (is_array($check)) {
var_dump($check);
}
$this->assertTrue($check);

$this->assertTrue($adh->store());
$this->login->logout();

return $adh;
}

/**
* Test user data are translated in member's language, whatever the current one is
*
* @return void
*/
public function testGetUserDataInMemberLanguage(): void
{
$member = $this->getFrenchMember();
$data = $this->dataAdherentOne();
$this->assertSame('en_US', $this->i18n->getID());

$user_data = $this->container->get(\GaletteOAuth2\Authorization\UserHelper::class)->getUserData(
$member->id,
'',
['member', 'member:personal', 'member:groups', 'member:due_date']
);

$this->assertSame('fr_FR', $user_data['locale']);
$this->assertSame('Femme', $user_data['gender']);
//dates are not localized
$this->assertSame($data['ddn_adh'], $user_data['birthdate']);
$this->assertNull($user_data['due_date']);
//status in groups is not translated: client applications rely on it
$this->assertSame(['Non-member'], $user_data['groups']);

//current language is restored
$this->assertSame('en_US', $this->i18n->getID());
$this->assertSame('Woman', _T('Woman'));
}

/**
* Test refusal messages are in member's language, and current language is restored
*
* @return void
*/
public function testRefusalInMemberLanguage(): void
{
$member = $this->getFrenchMember();

$this->i18n->changeLanguage('fr_FR');
$expected = _T("Sorry, you can't login because you are not a team member.", 'oauth2');
$this->i18n->changeLanguage('en_US');
$this->assertNotSame("Sorry, you can't login because you are not a team member.", $expected);

$exception_thrown = false;
try {
$this->container->get(\GaletteOAuth2\Authorization\UserHelper::class)->getUserData(
$member->id,
\GaletteOAuth2\Authorization\UserHelper::AUTH_TEAMONLY,
['member']
);
} catch (\GaletteOAuth2\Authorization\UserAuthorizationException $e) {
$exception_thrown = true;
$this->assertSame($expected, $e->getMessage());
}
$this->assertTrue($exception_thrown);
$this->assertSame('en_US', $this->i18n->getID());
}

/**
* Test requireAdmin
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,46 @@ public function testUserData(): void
$this->assertArrayHasKey('due_date', $user);
}

/**
* Test user data are sent in member's language, not in the one of client's request
*
* @return void
*/
public function testUserDataInMemberLanguage(): void
{
$this->logSuperAdmin();
$member = clone $this->getMemberOne();
$check = $member->check(
[
'bool_admin_adh' => '1',
'pref_lang' => 'fr_FR',
'sexe_adh' => \Galette\Entity\Adherent::MAN,
],
[],
[]
);
if (is_array($check)) {
var_dump($check);
}
$this->assertTrue($check);
$this->assertTrue($member->store());
$this->login->logout();

$tokens = $this->exchangeCode(
$this->getAuthorizationCode($member->id, ['member', 'member:personal', 'member:groups'])
);
$this->assertSame('en_US', $this->i18n->getID());
$test_response = $this->requestUser($tokens['access_token']);
$this->assertSame(200, $test_response->getStatusCode());
$user = json_decode((string)$test_response->getBody(), true);

$this->assertSame('fr_FR', $user['locale']);
$this->assertSame('Homme', $user['gender']);
$this->assertSame($this->dataAdherentOne()['ddn_adh'], $user['birthdate']);
$this->assertSame(['Non-member', 'admin'], $user['groups']);
$this->assertSame('en_US', $this->i18n->getID());
}

/**
* Test user data are refused when member is no longer authorized
*
Expand Down
Loading