Skip to content

fix(recording): store recordings and transcripts as the owning user - #19078

Open
bakiburakogun wants to merge 1 commit into
nextcloud:mainfrom
bakiburakogun:fix/recording-file-owner-attribution
Open

fix(recording): store recordings and transcripts as the owning user#19078
bakiburakogun wants to merge 1 commit into
nextcloud:mainfrom
bakiburakogun:fix/recording-file-owner-attribution

Conversation

@bakiburakogun

@bakiburakogun bakiburakogun commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #19077

Depends on nextcloud/activity#2889, which is what makes the actor set here reach the activity stream. Until that lands this PR is a no-op.

Summary

Call recordings are stored through POST /ocs/v2.php/apps/spreed/api/v1/recording/{token}/store, which is a #[PublicPage] endpoint authenticated with the recording shared secret only. There is no active user during that request, so when RecordingService writes the file the activity subsystem cannot resolve an actor. The Files activity provider renders that as "remote account", so users see

"remote account" created Recording 2026-08-23 23-23-09.mp4

instead of their own name, even though the file is in their own recording folder.

The service names the actor through the activity manager rather than touching the session:

private function runAsActor(string $userId, callable $callback) {
	$this->activityManager->setCurrentUserId($userId);
	try {
		return $callback();
	} finally {
		$this->activityManager->setCurrentUserId(null);
	}
}

It wraps the operations that create nodes: the recording folder, which covers all four call sites (store(), requestUpload(), finishUpload() and storeTranscript()), the recording file on the direct-upload path, and the transcript file.

Notes

  • The first version of this PR swapped the session user instead. @nickvergessen pushed back on that and pointed at IManager::getCurrentUserId(), which the activity app did not consult; feat: let apps define the actor of an activity via the activity manager activity#2889 is that missing piece and this branch is rebased on top of the chunked upload work.
  • The chunked-upload path is only partly covered, and deliberately so. The file itself is written by the recording backend through the public share created in requestUpload(), in a request Talk takes no part in, so nothing set in this process reaches it. What is covered there is the recording folder, which is created here and was previously unattributed — that is the entry that produced the "remote account" created … and <token> line for the first recording in a conversation. Attributing the uploaded file itself would mean doing something about the public-share upload, either at share level or by re-attributing in finishUpload(); that felt like a separate change rather than something to bolt onto this one, happy to look at it as a follow-up.
  • The owner parameter that the recording backend sends is already validated in store(); getParticipant() throws owner_participant for an unknown owner.
  • The actor is reset in a finally block, so a failing write cannot leak it into the rest of the request.
  • Sharing the recording to the chat is unaffected, that path already posts with the participant actor.

Testing

Reproduced on Nextcloud 34.0.3 with Talk 24.0.4 and nextcloud-talk-recording 0.2.1, four node signaling cluster and seven application servers.

@bakiburakogun

Copy link
Copy Markdown
Contributor Author

Deployed on the instance from the report (Nextcloud 34.0.3, Talk 24.0.4, nextcloud-talk-recording 0.2.1) and recorded a call with it.

The activity entry for the new recording now reads You created Recording 2026-08-24 00-02-34.mp4, while the recordings stored before the patch still show "remote account" created .... The file, the "recording available" notification and sharing it to the chat behave as before.

@bakiburakogun

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up commit.

After running the first version on the instance from the report, the recordings themselves are attributed correctly, but the activity stream still shows an entry like

"remote account" created Recording 2026-08-24 13-55-16.mp4 and <conversation token>

for the first recording of a conversation. The reason is that getRecordingFolder() creates the attachment folder and the per-conversation folder with newFolder() outside of the user context that newFileAsUser() sets up, so the folder creation is still attributed to nobody.

The new commit moves the user switch into a small runAsUser() helper and uses it for the folder lookup as well, so the folder and the file end up with the same actor. No behaviour changes beyond the attribution.

@bakiburakogun

Copy link
Copy Markdown
Contributor Author

Verified on the instance from the report (Nextcloud 34.0.3, Talk 24.0.4, nextcloud-talk-recording 0.2.1, four signaling servers, seven application servers).

Recorded a call in a conversation that had never been recorded before, so the per-conversation folder was created as part of storing the recording. The activity stream now attributes both the folder and the .mp4 to the user who started the recording. Before this commit the same case produced a single entry reading "remote account" created Recording ....mp4 and <conversation token>, with the file and the folder both unattributed.

Recording, the "recording available" notification and sharing it into the chat behave as before.

Comment thread lib/Service/RecordingService.php Outdated
}

$previousUser = $this->userSession->getUser();
$this->userSession->setUser($user);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's the best idea to overwrite the session user like this.
Also it's not going to work on the new chunked-uploading of recordings we implemented recently.

I think instead we need work in the activity app to allow defining different actors:
https://github.com/nextcloud/activity/blob/f95186c14cb89cb8e9f7f367e9ac394b2c398c50/lib/CurrentUser.php#L35

The activity manager has code for this already: https://github.com/nextcloud/server/blob/1ab09ec753f106661beadee1794b390ebec455dd/lib/private/Activity/Manager.php#L346-L354
but it's not used in the activity app itself. I think CurrentUser should first check IManager::getCurrentUserId() that would allow Talk to overwrite the user for the action

@nickvergessen nickvergessen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per comment

Recordings and transcripts are written by a background job, outside of any
session, so the folder and the file end up attributed to nobody and the
activity stream renders them as "remote account" created … .

Set the actor through the activity manager for the operations that create
nodes, rather than swapping the session user around them: the session swap
does not survive into the chunked upload, which happens in a separate request
against a public share, and overwriting the session for unrelated code running
in the same process is not something this service should do.

This depends on nextcloud/activity#2889, which makes the activity app consult
IManager::getCurrentUserId(). Without it the call here is a no-op and the
behaviour is unchanged.

The file created by the chunked upload itself is not covered: it is uploaded by
the recording backend through the public share created in requestUpload(), in a
request Talk does not take part in. The recording folder created for that upload
is attributed correctly, as is everything on the direct-upload and transcript
paths.

Signed-off-by: Baki Burak Öğün <63836730+bakiburakogun@users.noreply.github.com>
@bakiburakogun
bakiburakogun force-pushed the fix/recording-file-owner-attribution branch from e033e8e to 6b093d8 Compare August 29, 2026 02:45
@bakiburakogun

Copy link
Copy Markdown
Contributor Author

Reworked along the lines you suggested — thank you, the session swap was the wrong tool and the chunked upload made that obvious.

What changed

The service no longer touches IUserSession. It sets the actor through the activity manager instead:

private function runAsActor(string $userId, callable $callback) {
	$this->activityManager->setCurrentUserId($userId);
	try {
		return $callback();
	} finally {
		$this->activityManager->setCurrentUserId(null);
	}
}

and wraps the operations that create nodes with it: the recording folder (which covers all four call sites — store(), requestUpload(), finishUpload() and storeTranscript()), the recording file on the direct-upload path, and the transcript file.

The branch is rebased onto current main, so it now sits on top of the chunked upload work.

Dependency

This needs nextcloud/activity#2889, which makes CurrentUser consult IManager::getCurrentUserId() as you described. OC\Activity\Manager already honours the override; the activity app was the piece that never asked. Until that lands the call here is a no-op, so this PR is safe to sit behind it but pointless on its own.

The chunked upload

You were right that the previous approach could not work there, and I want to be explicit that this one only partly does. The file created by a chunked upload is written by the recording backend through the public share created in requestUpload(), in a request Talk takes no part in, so nothing set in this process reaches it. What this PR does cover for that path is the recording folder, which is created here and was previously unattributed — that is the entry that produced the "remote account" created … and <token> line for the first recording in a conversation.

Making the uploaded file itself land on the owner would mean doing something about the public-share upload — either attributing it at share level or re-attributing in finishUpload(). Happy to look into that as a follow-up if you think it is worth it, but it felt like a separate change rather than something to bolt onto this one.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug feature: api 🛠️ OCS API for conversations, chats and participants feature: recordings ⏺️ Including the recording server feedback-requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Call recordings are attributed to "remote account" in the Files activity stream

2 participants