Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1353,17 +1353,18 @@ public function createMySpeaker()
'notes'
];

// set data from current member ...
$aux_payload = [
'member_id' => $current_member->getId(),
// Member fields are used as defaults only — user-submitted values take precedence.
// member_id is always forced from the authenticated user for security.
$member_defaults = [
'first_name' => $current_member->getFirstName(),
'last_name' => $current_member->getLastName(),
'bio' => $current_member->getBio(),
'twitter' => $current_member->getTwitterHandle(),
'irc' => $current_member->getIrcHandle(),
'last_name' => $current_member->getLastName(),
'bio' => $current_member->getBio(),
'twitter' => $current_member->getTwitterHandle(),
'irc' => $current_member->getIrcHandle(),
];

$payload = array_merge($payload, $aux_payload);
$payload = array_merge($member_defaults, $payload);
$payload['member_id'] = $current_member->getId();

$speaker = $this->service->addSpeaker(HTMLCleaner::cleanData($payload, $fields), $current_member);

Expand Down
138 changes: 138 additions & 0 deletions tests/oauth2/OAuth2SummitSpeakersApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
use models\summit\PresentationSpeaker;
use utils\FilterParser;
use models\summit\SpeakersSummitRegistrationPromoCode;
use models\main\Member;
use LaravelDoctrine\ORM\Facades\Registry;
use models\utils\SilverstripeBaseModel;

final class OAuth2SummitSpeakersApiTest extends ProtectedApiTestCase
{
Expand Down Expand Up @@ -2016,4 +2019,139 @@ public function testGetCurrentSummitSpeakersActivitiesCountWithAcceptedPresentat
$this->assertEquals($baseline + 1, $data->count);
}

private function resetEmIfNeeded(): void
{
if (!self::$em->isOpen()) {
self::$em = Registry::resetManager(SilverstripeBaseModel::EntityManager);
}
}

/**
* Regression test for: createMySpeaker must honour the bio submitted in the
* payload and must NOT silently overwrite it with the member's bio.
*
* Previously array_merge($payload, $aux_payload) put $aux_payload last so
* member->getBio() always won regardless of what the user submitted.
*/
public function testCreateMySpeakerPayloadBioTakesPrecedenceOverMemberBio()
{
// Create a fresh member (no speaker attached) with a known bio
$prefix = str_random(10);
$memberBio = "This is the OLD bio from the member/FNid profile.";
$payloadBio = "This is the NEW bio submitted by the user in the portal.";

$newMember = new Member();
$newMember->setEmail("test_bio_precedence_{$prefix}@example.com");
$newMember->setFirstName("Bio");
$newMember->setLastName("Precedence");
$newMember->setActive(true);
$newMember->setEmailVerified(true);
$newMember->setUserExternalId(mt_rand());
$newMember->setBio($memberBio);
self::$em->persist($newMember);
self::$em->flush();

// Authenticate as the new member
self::$service->setUserId($newMember->getUserExternalId());
self::$service->setUserExternalId($newMember->getUserExternalId());
self::$service->setUserEmail($newMember->getEmail());
self::$service->setUserFirstName($newMember->getFirstName());
self::$service->setUserLastName($newMember->getLastName());

$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json",
];

$response = $this->action(
"POST",
"OAuth2SummitSpeakersApiController@createMySpeaker",
[],
[],
[],
[],
$headers,
json_encode(['bio' => $payloadBio])
);

// Restore authenticated member so tearDown works correctly
self::$service->setUserId(self::$member->getUserExternalId());
self::$service->setUserExternalId(self::$member->getUserExternalId());
self::$service->setUserEmail(self::$member->getEmail());
self::$service->setUserFirstName(self::$member->getFirstName());
self::$service->setUserLastName(self::$member->getLastName());

$this->assertResponseStatus(201);
$speaker = json_decode($response->getContent());
$this->assertNotNull($speaker);
$this->assertEquals($payloadBio, $speaker->bio,
"Speaker bio must match what the user submitted, not the member/FNid bio.");

// Cleanup — EM may have been closed by the BrowserKit HTTP simulation
$this->resetEmIfNeeded();
self::$em->remove(self::$em->find(Member::class, $newMember->getId()));
self::$em->flush();
}

/**
* When no bio is submitted in the payload, createMySpeaker should fall back
* to the member's bio so that a first-time speaker gets a sensible default.
*/
public function testCreateMySpeakerFallsBackToMemberBioWhenNoneSubmitted()
{
$prefix = str_random(10);
$memberBio = "Default bio coming from the FNid member profile.";

$newMember = new Member();
$newMember->setEmail("test_bio_fallback_{$prefix}@example.com");
$newMember->setFirstName("Bio");
$newMember->setLastName("Fallback");
$newMember->setActive(true);
$newMember->setEmailVerified(true);
$newMember->setUserExternalId(mt_rand());
$newMember->setBio($memberBio);
self::$em->persist($newMember);
self::$em->flush();

self::$service->setUserId($newMember->getUserExternalId());
self::$service->setUserExternalId($newMember->getUserExternalId());
self::$service->setUserEmail($newMember->getEmail());
self::$service->setUserFirstName($newMember->getFirstName());
self::$service->setUserLastName($newMember->getLastName());

$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json",
];

// No 'bio' key in the payload — member bio should be used as default
$response = $this->action(
"POST",
"OAuth2SummitSpeakersApiController@createMySpeaker",
[],
[],
[],
[],
$headers,
json_encode(['title' => 'Engineer'])
);

self::$service->setUserId(self::$member->getUserExternalId());
self::$service->setUserExternalId(self::$member->getUserExternalId());
self::$service->setUserEmail(self::$member->getEmail());
self::$service->setUserFirstName(self::$member->getFirstName());
self::$service->setUserLastName(self::$member->getLastName());

$this->assertResponseStatus(201);
$speaker = json_decode($response->getContent());
$this->assertNotNull($speaker);
$this->assertEquals($memberBio, $speaker->bio,
"When no bio is submitted, speaker bio must default to the member/FNid bio.");

// Cleanup — EM may have been closed by the BrowserKit HTTP simulation
$this->resetEmIfNeeded();
self::$em->remove(self::$em->find(Member::class, $newMember->getId()));
self::$em->flush();
}

}
Loading