Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix not hiding tokens for clients via realtime #7870

Merged
merged 9 commits into from May 24, 2024
70 changes: 22 additions & 48 deletions app/controllers/api/account.php
Expand Up @@ -1852,24 +1852,19 @@
->setRecipient($email)
->trigger();

$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $tokenSecret),
Response::MODEL_TOKEN
)
);

// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);

if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}

// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');

$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});

App::post('/v1/account/tokens/email')
Expand Down Expand Up @@ -2081,24 +2076,19 @@
->setRecipient($email)
->trigger();

$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $tokenSecret),
Response::MODEL_TOKEN
)
);

// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);

if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}

// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $tokenSecret : '');

$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});

App::put('/v1/account/sessions/magic-url')
Expand Down Expand Up @@ -2315,20 +2305,15 @@
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);

$queueForEvents->setPayload(
$response->output(
$token->setAttribute('secret', $secret),
Response::MODEL_TOKEN
)
);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);

// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');

$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($token, Response::MODEL_TOKEN)
;
->dynamic($token, Response::MODEL_TOKEN);
});

App::post('/v1/account/jwt')
Expand Down Expand Up @@ -2980,11 +2965,7 @@
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setUser($profile)
->setPayload($response->output(
$recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);

// Hide secret for clients
$recovery->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : '');
Expand Down Expand Up @@ -3158,6 +3139,7 @@
->setParam('{{footer}}', $locale->getText("emails.verification.footer"))
->setParam('{{thanks}}', $locale->getText("emails.verification.thanks"))
->setParam('{{signature}}', $locale->getText("emails.verification.signature"));

$body = $message->render();

$smtp = $project->getAttribute('smtp', []);
Expand Down Expand Up @@ -3227,10 +3209,7 @@
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output(
$verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
));
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);

// Hide secret for clients
$verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
Expand Down Expand Up @@ -3283,7 +3262,7 @@

$user->setAttributes($profile->getArrayCopy());

$verificationDocument = $dbForProject->getDocument('tokens', $verifiedToken->getId());
$verification = $dbForProject->getDocument('tokens', $verifiedToken->getId());

/**
* We act like we're updating and validating
Expand All @@ -3294,10 +3273,9 @@

$queueForEvents
->setParam('userId', $userId)
->setParam('tokenId', $verificationDocument->getId())
;
->setParam('tokenId', $verification->getId());

$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
$response->dynamic($verification, Response::MODEL_TOKEN);
});

App::post('/v1/account/verification/phone')
Expand Down Expand Up @@ -3398,11 +3376,7 @@
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output(
$verification->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);

// Hide secret for clients
$verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : '');
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/shared/api.php
Expand Up @@ -590,7 +590,7 @@

Realtime::send(
projectId: $target['projectId'] ?? $project->getId(),
payload: $queueForEvents->getPayload(),
payload: $queueForEvents->getRealtimePayload(),
events: $allEvents,
channels: $target['channels'],
roles: $target['roles'],
Expand Down
29 changes: 28 additions & 1 deletion src/Appwrite/Event/Event.php
Expand Up @@ -52,6 +52,7 @@ class Event
protected string $class = '';
protected string $event = '';
protected array $params = [];
protected array $sensitive = [];
protected array $payload = [];
protected array $context = [];
protected ?Document $project = null;
Expand Down Expand Up @@ -161,12 +162,17 @@ public function getUser(): ?Document
* Set payload for this event.
*
* @param array $payload
* @param array $sensitive
* @return self
*/
public function setPayload(array $payload): self
public function setPayload(array $payload, array $sensitive = []): self
{
$this->payload = $payload;

foreach ($sensitive as $key) {
$this->sensitive[$key] = true;
}

abnegate marked this conversation as resolved.
Show resolved Hide resolved
return $this;
}

Expand All @@ -180,6 +186,19 @@ public function getPayload(): array
return $this->payload;
}

public function getRealtimePayload(): array
{
$payload = [];

foreach ($this->payload as $key => $value) {
if (!isset($this->sensitive[$key])) {
$payload[$key] = $value;
}
}

return $payload;
}

/**
* Set context for this event.
*
Expand Down Expand Up @@ -242,6 +261,13 @@ public function setParam(string $key, mixed $value): self
return $this;
}

public function setParamSensitive(string $key): self
{
$this->sensitive[$key] = true;

return $this;
}

/**
* Get param of event.
*
Expand Down Expand Up @@ -294,6 +320,7 @@ public function trigger(): string|bool
public function reset(): self
{
$this->params = [];
$this->sensitive = [];

return $this;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/Services/Realtime/RealtimeCustomClientTest.php
Expand Up @@ -290,7 +290,6 @@ public function testChannelAccount()

$this->assertEquals($name, $response['data']['payload']['name']);


/**
* Test Account Password Event
*/
Expand Down Expand Up @@ -376,6 +375,7 @@ public function testChannelAccount()
$this->assertNotEmpty($response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertArrayNotHasKey('secret', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertContains("users.{$userId}.verification.{$verificationId}.create", $response['data']['events']);
Expand Down