Merge pull request #15172 from nextcloud/techdebt/noid/request-headers

feat(request-headers): Document request headers
This commit is contained in:
Joas Schilling 2025-06-06 11:14:37 +02:00 committed by GitHub
commit 7919b07fdc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 98 additions and 28 deletions

View file

@ -63,7 +63,7 @@ abstract class AEnvironmentAwareOCSController extends OCSController {
// if none is given try the first Accept header
if ($format === null) {
$headers = $this->request->getHeader('Accept');
$headers = $this->request->getHeader('accept');
/**
* Default value of
* @see OCSController::buildResponse()

View file

@ -25,6 +25,7 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Federation\ICloudIdManager;
@ -129,6 +130,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[NoCSRFRequired]
#[AllowWithoutParticipantWhenPendingInvitation]
#[RequireParticipantOrLoggedInAndListedConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getAvatar(bool $darkTheme = false): FileDisplayResponse {
// Cache for 1 day
$cacheDuration = 60 * 60 * 24;
@ -162,6 +164,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[NoCSRFRequired]
#[AllowWithoutParticipantWhenPendingInvitation]
#[RequireParticipantOrLoggedInAndListedConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getAvatarDark(): FileDisplayResponse {
return $this->getAvatar(true);
}
@ -181,6 +184,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
#[NoAdminRequired]
#[NoCSRFRequired]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getUserProxyAvatarWithoutRoom(int $size, string $cloudId, bool $darkTheme = false): FileDisplayResponse {
return $this->getUserProxyAvatar($size, $cloudId, $darkTheme);
}
@ -199,6 +203,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
#[NoAdminRequired]
#[NoCSRFRequired]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getUserProxyAvatarDarkWithoutRoom(int $size, string $cloudId): FileDisplayResponse {
return $this->getUserProxyAvatar($size, $cloudId, true);
}
@ -221,6 +226,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[NoCSRFRequired]
#[AllowWithoutParticipantWhenPendingInvitation]
#[RequireLoggedInParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getUserProxyAvatar(int $size, string $cloudId, bool $darkTheme = false): FileDisplayResponse {
try {
$resolvedCloudId = $this->cloudIdManager->resolveCloudId($cloudId);
@ -281,6 +287,7 @@ class AvatarController extends AEnvironmentAwareOCSController {
#[NoCSRFRequired]
#[AllowWithoutParticipantWhenPendingInvitation]
#[RequireLoggedInParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getUserProxyAvatarDark(int $size, string $cloudId): FileDisplayResponse {
return $this->getUserProxyAvatar($size, $cloudId, true);
}

View file

@ -36,6 +36,7 @@ use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\MessageTooLongException;
@ -73,13 +74,15 @@ class BotController extends AEnvironmentAwareOCSController {
* @return Bot
* @throws \InvalidArgumentException When the request could not be linked with a bot
*/
#[RequestHeader(name: 'x-nextcloud-talk-bot-random', description: 'Random seed used to generate the request signature')]
#[RequestHeader(name: 'x-nextcloud-talk-bot-signature', description: 'Signature over the request body to verify authenticity')]
protected function getBotFromHeaders(string $token, string $message): Bot {
$random = $this->request->getHeader('X-Nextcloud-Talk-Bot-Random');
$random = $this->request->getHeader('x-nextcloud-talk-bot-random');
if (empty($random) || strlen($random) < 32) {
$this->logger->error('Invalid Random received from bot response');
throw new \InvalidArgumentException('Invalid Random received from bot response', Http::STATUS_BAD_REQUEST);
}
$checksum = $this->request->getHeader('X-Nextcloud-Talk-Bot-Signature');
$checksum = $this->request->getHeader('x-nextcloud-talk-bot-signature');
if (empty($checksum)) {
$this->logger->error('Invalid Signature received from bot response');
throw new \InvalidArgumentException('Invalid Signature received from bot response', Http::STATUS_BAD_REQUEST);

View file

@ -33,7 +33,9 @@ use OCA\Talk\Service\SIPDialOutService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\Response;
@ -77,6 +79,7 @@ class CallController extends AEnvironmentAwareOCSController {
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getPeersForCall(): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\CallController $proxy */
@ -129,7 +132,7 @@ class CallController extends AEnvironmentAwareOCSController {
*/
#[PublicPage]
#[RequireModeratorParticipant]
#[Http\Attribute\NoCSRFRequired]
#[NoCSRFRequired]
public function downloadParticipantsForCall(string $format = 'csv'): DataDownloadResponse|Response {
$callStart = $this->room->getActiveSince()?->getTimestamp() ?? 0;
if ($callStart === 0) {
@ -223,6 +226,7 @@ class CallController extends AEnvironmentAwareOCSController {
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function joinCall(?int $flags = null, bool $silent = false, bool $recordingConsent = false, array $silentFor = []): DataResponse {
try {
$this->validateRecordingConsent($recordingConsent);
@ -344,6 +348,7 @@ class CallController extends AEnvironmentAwareOCSController {
#[RequireCallEnabled]
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::START_CALL)]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function ringAttendee(int $attendeeId): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\CallController $proxy */
@ -424,6 +429,7 @@ class CallController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function updateCallFlags(int $flags): DataResponse {
$session = $this->participant->getSession();
if (!$session instanceof Session) {
@ -496,6 +502,7 @@ class CallController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function leaveCall(bool $all = false): DataResponse {
$session = $this->participant->getSession();
if (!$session instanceof Session) {

View file

@ -53,6 +53,7 @@ use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\Attribute\UserRateLimit;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Services\IAppConfig;
@ -215,6 +216,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function sendMessage(string $message, string $actorDisplayName = '', string $referenceId = '', int $replyTo = 0, bool $silent = false): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
@ -392,6 +394,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function receiveMessages(int $lookIntoFuture,
int $limit = 100,
int $lastKnownMessageId = 0,
@ -749,6 +752,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getMessageContext(
int $messageId,
int $limit = 50): DataResponse {
@ -839,6 +843,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireAuthenticatedParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function deleteMessage(int $messageId): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
@ -931,6 +936,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireAuthenticatedParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function editMessage(int $messageId, string $message): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
@ -1037,6 +1043,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
#[UserRateLimit(limit: 60, period: 3600)]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function setReminder(int $messageId, int $timestamp): DataResponse {
try {
// FIXME fail 400 when reminder is after expiration
@ -1071,6 +1078,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getReminder(int $messageId): DataResponse {
try {
$this->validateMessageExists($messageId);
@ -1104,6 +1112,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[NoAdminRequired]
#[RequireModeratorOrNoLobby]
#[RequireLoggedInParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function deleteReminder(int $messageId): DataResponse {
try {
$this->validateMessageExists($messageId);
@ -1290,6 +1299,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireAuthenticatedParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function setReadMarker(?int $lastReadMessage = null): DataResponse {
$setToMessage = $lastReadMessage ?? $this->room->getLastMessageId();
if ($setToMessage === 0) {
@ -1339,6 +1349,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireAuthenticatedParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function markUnread(): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
@ -1515,6 +1526,7 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function mentions(string $search, int $limit = 20, bool $includeStatus = false): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */

View file

@ -52,7 +52,7 @@ class FederationController extends OCSController {
// if none is given try the first Accept header
if ($format === null) {
$headers = $this->request->getHeader('Accept');
$headers = $this->request->getHeader('accept');
/**
* Default value of
* @see OCSController::buildResponse()

View file

@ -28,6 +28,7 @@ use OCA\Talk\Service\PollService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IRequest;
@ -73,6 +74,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function createPoll(string $question, array $options, int $resultMode, int $maxVotes, bool $draft = false): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
@ -158,6 +160,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function updateDraftPoll(int $pollId, string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
@ -220,6 +223,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireModeratorParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getAllDraftPolls(): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
@ -250,6 +254,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function showPoll(int $pollId): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
@ -292,6 +297,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function votePoll(int $pollId, array $optionIds = []): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */
@ -358,6 +364,7 @@ class PollController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function closePoll(int $pollId): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\PollController $proxy */

View file

@ -20,6 +20,7 @@ use OCA\Talk\Middleware\Attribute\RequireReadWriteConversation;
use OCA\Talk\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\Comments\NotFoundException;
use OCP\IRequest;
@ -56,6 +57,7 @@ class ReactionController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function react(int $messageId, string $reaction): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ReactionController $proxy */
@ -102,6 +104,7 @@ class ReactionController extends AEnvironmentAwareOCSController {
#[RequireParticipant]
#[RequirePermission(permission: RequirePermission::CHAT)]
#[RequireReadWriteConversation]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function delete(int $messageId, string $reaction): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ReactionController $proxy */
@ -143,6 +146,7 @@ class ReactionController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getReactions(int $messageId, ?string $reaction): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ReactionController $proxy */

View file

@ -27,6 +27,7 @@ use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Http\Client\IClientService;
@ -136,12 +137,12 @@ class RecordingController extends AEnvironmentAwareOCSController {
* @return bool
*/
private function validateBackendRequest(string $data): bool {
$random = $this->request->getHeader('Talk-Recording-Random');
$random = $this->request->getHeader('talk-recording-random');
if (empty($random) || strlen($random) < 32) {
$this->logger->debug('Missing random');
return false;
}
$checksum = $this->request->getHeader('Talk-Recording-Checksum');
$checksum = $this->request->getHeader('talk-recording-checksum');
if (empty($checksum)) {
$this->logger->debug('Missing checksum');
return false;
@ -174,6 +175,8 @@ class RecordingController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[BruteForceProtection(action: 'talkRecordingSecret')]
#[BruteForceProtection(action: 'talkRecordingStatus')]
#[RequestHeader(name: 'talk-recording-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-recording-checksum', description: 'Checksum over the request body to verify authenticity from the recording backend', indirect: true)]
public function backend(): DataResponse {
$json = $this->getInputStream();
if (!$this->validateBackendRequest($json)) {
@ -380,6 +383,8 @@ class RecordingController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkRecordingSecret')]
#[OpenAPI(scope: 'backend-recording')]
#[RequireRoom]
#[RequestHeader(name: 'talk-recording-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-recording-checksum', description: 'Checksum over the request body to verify authenticity from the recording backend', indirect: true)]
public function store(?string $owner): DataResponse {
$data = $this->room->getToken();
if (!$this->validateBackendRequest($data)) {

View file

@ -77,6 +77,7 @@ use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Utility\ITimeFactory;
@ -392,6 +393,9 @@ class RoomController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function getSingleRoom(string $token): DataResponse {
try {
$isSIPBridgeRequest = $this->validateSIPBridgeRequest($token);
@ -412,7 +416,7 @@ class RoomController extends AEnvironmentAwareOCSController {
$action = 'talkRoomToken';
$participant = null;
$isTalkFederation = $this->request->getHeader('X-Nextcloud-Federation');
$isTalkFederation = $this->request->getHeader('x-nextcloud-federation');
if (!$isTalkFederation) {
$sessionId = $this->session->getSessionForRoom($token);
@ -510,8 +514,8 @@ class RoomController extends AEnvironmentAwareOCSController {
* @throws UnauthorizedException when the request tried to sign as SIP bridge but is not valid
*/
private function validateSIPBridgeRequest(string $token): bool {
$random = $this->request->getHeader('TALK_SIPBRIDGE_RANDOM');
$checksum = $this->request->getHeader('TALK_SIPBRIDGE_CHECKSUM');
$random = $this->request->getHeader('talk-sipbridge-random');
$checksum = $this->request->getHeader('talk-sipbridge-checksum');
$secret = $this->talkConfig->getSIPSharedSecret();
return $this->checksumVerificationService->validateRequest($random, $checksum, $secret, $token);
}
@ -860,6 +864,7 @@ class RoomController extends AEnvironmentAwareOCSController {
* 200: Call notification level updated successfully
* 400: Updating call notification level is not possible
*/
#[FederationSupported]
#[NoAdminRequired]
#[RequireLoggedInParticipant]
public function setNotificationCalls(int $level): DataResponse {
@ -980,6 +985,7 @@ class RoomController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[RequireModeratorOrNoLobby]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getParticipants(bool $includeStatus = false): DataResponse {
if ($this->room->isFederatedConversation()) {
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\RoomController $proxy */
@ -1454,6 +1460,7 @@ class RoomController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[NoAdminRequired]
#[RequireLoggedInParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function removeSelfFromRoom(): DataResponse {
return $this->removeSelfFromRoomLogic($this->room, $this->participant);
}
@ -1976,6 +1983,7 @@ class RoomController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[BruteForceProtection(action: 'talkRoomToken')]
#[BruteForceProtection(action: 'talkFederationAccess')]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function joinFederatedRoom(string $token, ?string $sessionId): DataResponse {
if (!$this->federationAuthenticator->isFederationRequest()) {
$response = new DataResponse(null, Http::STATUS_NOT_FOUND);
@ -2028,6 +2036,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequireRoom]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function verifyDialInPin(string $pin): DataResponse {
if (!$this->talkConfig->isSIPConfigured()) {
return new DataResponse(null, Http::STATUS_NOT_IMPLEMENTED);
@ -2072,6 +2082,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[PublicPage]
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function directDialIn(string $phoneNumber, string $caller): DataResponse {
if (!$this->talkConfig->isSIPConfigured()) {
return new DataResponse(null, Http::STATUS_NOT_IMPLEMENTED);
@ -2133,6 +2145,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequireRoom]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function verifyDialOutNumber(string $number, array $options = []): DataResponse {
if (!$this->talkConfig->isSIPConfigured() || !$this->talkConfig->isSIPDialOutEnabled()) {
return new DataResponse(null, Http::STATUS_NOT_IMPLEMENTED);
@ -2180,6 +2194,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequireRoom]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function createGuestByDialIn(): DataResponse {
try {
if (!$this->validateSIPBridgeRequest($this->room->getToken())) {
@ -2219,6 +2235,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[BruteForceProtection(action: 'talkSipBridgeSecret')]
#[OpenAPI(scope: 'backend-sipbridge')]
#[RequireRoom]
#[RequestHeader(name: 'talk-sipbridge-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-sipbridge-checksum', description: 'Checksum over the request body to verify authenticity from the Sipbridge', indirect: true)]
public function rejectedDialOutRequest(string $callId, array $options = []): DataResponse {
if (!$this->talkConfig->isSIPConfigured() || !$this->talkConfig->isSIPDialOutEnabled()) {
return new DataResponse(null, Http::STATUS_NOT_IMPLEMENTED);
@ -2322,6 +2340,7 @@ class RoomController extends AEnvironmentAwareOCSController {
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
#[PublicPage]
#[BruteForceProtection(action: 'talkRoomToken')]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function leaveFederatedRoom(string $token, string $sessionId): DataResponse {
if (!$this->federationAuthenticator->isFederationRequest()) {
$response = new DataResponse(null, Http::STATUS_NOT_FOUND);
@ -2762,6 +2781,7 @@ class RoomController extends AEnvironmentAwareOCSController {
#[FederationSupported]
#[PublicPage]
#[RequireParticipant]
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
public function getCapabilities(): DataResponse {
$headers = [];
if ($this->room->isFederatedConversation()) {

View file

@ -30,6 +30,7 @@ use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\RequestHeader;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
@ -88,12 +89,12 @@ class SignalingController extends OCSController {
* @return bool
*/
private function validateRecordingBackendRequest(string $data): bool {
$random = $this->request->getHeader('Talk-Recording-Random');
$random = $this->request->getHeader('talk-recording-random');
if (empty($random) || strlen($random) < 32) {
$this->logger->debug('Missing random');
return false;
}
$checksum = $this->request->getHeader('Talk-Recording-Checksum');
$checksum = $this->request->getHeader('talk-recording-checksum');
if (empty($checksum)) {
$this->logger->debug('Missing checksum');
return false;
@ -117,10 +118,12 @@ class SignalingController extends OCSController {
#[BruteForceProtection(action: 'talkRecordingSecret')]
#[BruteForceProtection(action: 'talkFederationAccess')]
#[OpenAPI(tags: ['internal_signaling', 'external_signaling'])]
#[RequestHeader(name: 'talk-recording-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'talk-recording-checksum', description: 'Checksum over the request body to verify authenticity from the recording backend', indirect: true)]
public function getSettings(string $token = ''): DataResponse {
$isRecordingRequest = false;
if (!empty($this->request->getHeader('Talk-Recording-Random')) || !empty($this->request->getHeader('Talk-Recording-Checksum'))) {
if (!empty($this->request->getHeader('talk-recording-random')) || !empty($this->request->getHeader('talk-recording-checksum'))) {
if (!$this->validateRecordingBackendRequest('')) {
$response = new DataResponse(null, Http::STATUS_UNAUTHORIZED);
$response->throttle(['action' => 'talkRecordingSecret']);
@ -651,6 +654,8 @@ class SignalingController extends OCSController {
#[OpenAPI(scope: 'backend-signaling')]
#[PublicPage]
#[BruteForceProtection(action: 'talkSignalingSecret')]
#[RequestHeader(name: 'spreed-signaling-random', description: 'Random seed used to generate the request checksum', indirect: true)]
#[RequestHeader(name: 'spreed-signaling-checksum', description: 'Checksum over the request body to verify authenticity from the signaling backend', indirect: true)]
public function backend(): DataResponse {
$json = $this->getInputStream();
if (!$this->validateBackendRequest($json)) {

View file

@ -30,7 +30,7 @@ class Authenticator {
}
protected function readHeaders(): void {
$this->isFederationRequest = (bool)$this->request->getHeader('X-Nextcloud-Federation');
$this->isFederationRequest = (bool)$this->request->getHeader('x-nextcloud-federation');
if (!$this->isFederationRequest) {
$this->federationCloudId = '';
$this->accessToken = '';

View file

@ -979,7 +979,7 @@
}
},
{
"name": "X-Nextcloud-Federation",
"name": "x-nextcloud-federation",
"in": "header",
"schema": {
"type": "string"

View file

@ -13201,7 +13201,7 @@
}
},
{
"name": "X-Nextcloud-Federation",
"name": "x-nextcloud-federation",
"in": "header",
"schema": {
"type": "string"
@ -19761,14 +19761,14 @@
}
},
{
"name": "Talk-Recording-Random",
"name": "talk-recording-random",
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "Talk-Recording-Checksum",
"name": "talk-recording-checksum",
"in": "header",
"schema": {
"type": "string"

View file

@ -13106,7 +13106,7 @@
}
},
{
"name": "X-Nextcloud-Federation",
"name": "x-nextcloud-federation",
"in": "header",
"schema": {
"type": "string"
@ -19666,14 +19666,14 @@
}
},
{
"name": "Talk-Recording-Random",
"name": "talk-recording-random",
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "Talk-Recording-Checksum",
"name": "talk-recording-checksum",
"in": "header",
"schema": {
"type": "string"

View file

@ -515,7 +515,7 @@ export interface operations {
parameters: {
query?: never;
header: {
"X-Nextcloud-Federation"?: string;
"x-nextcloud-federation"?: string;
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};

View file

@ -7212,7 +7212,7 @@ export interface operations {
parameters: {
query?: never;
header: {
"X-Nextcloud-Federation"?: string;
"x-nextcloud-federation"?: string;
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};
@ -9789,8 +9789,8 @@ export interface operations {
token?: string;
};
header: {
"Talk-Recording-Random"?: string;
"Talk-Recording-Checksum"?: string;
"talk-recording-random"?: string;
"talk-recording-checksum"?: string;
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};

View file

@ -6674,7 +6674,7 @@ export interface operations {
parameters: {
query?: never;
header: {
"X-Nextcloud-Federation"?: string;
"x-nextcloud-federation"?: string;
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};
@ -9251,8 +9251,8 @@ export interface operations {
token?: string;
};
header: {
"Talk-Recording-Random"?: string;
"Talk-Recording-Checksum"?: string;
"talk-recording-random"?: string;
"talk-recording-checksum"?: string;
/** @description Required to be true for the API request to pass */
"OCS-APIRequest": boolean;
};