feat: let apps define the actor of an activity via the activity manager - #2889
feat: let apps define the actor of an activity via the activity manager#2889bakiburakogun wants to merge 1 commit into
Conversation
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>
|
From my perspective this is what Talk needs to be able to overwrite the |
would this also allow filtering activity by Team? It's a challenge we have with the Teams app, where we'd like to show a stream of activity related to team owned resources... |
Not related from what I understand |
| // Neither a session nor a valid feed token, fall back to the session below | ||
| } | ||
|
|
||
| $user = $this->userSession->getUser(); |
There was a problem hiding this comment.
Looks like getCurrentUserId() already looks in the session, so we can drop that part.
For consistency, we can also replace all $this->userSession->getUser(); calls in this file with $userId = $this->activityManager->getCurrentUserId();
CurrentUser::getUID() read the user straight from the session, so an action performed outside of a user's session could not be attributed to anybody. File activities created from a background job end up rendered as "remote account" did something, because FilesHooks asks CurrentUser for the actor. IManager::setCurrentUserId() already exists for exactly this, and OC\Activity\Manager::getCurrentUserId() honours it, but the activity app never consulted it. Read the actor from the activity manager instead of the session. With no override the result is unchanged for a request that has a session, because the manager returns the session user itself; when there is neither a session nor a valid feed token it throws, which is caught here so getUID() keeps returning null. Signed-off-by: Baki Burak Öğün <63836730+bakiburakogun@users.noreply.github.com>
14ab080 to
7022abe
Compare
|
Dropped the session part, thank you — the fallback was indeed unreachable for everything that matters. public function getUID(): ?string {
try {
$userId = $this->activityManager->getCurrentUserId();
} catch (\UnexpectedValueException) {
// No override, no session and no valid feed token
return null;
}
return $userId === '' ? null : $userId;
}Two edge cases do come out differently, so rather than leaving them buried in the diff:
The bottom two rows are new with this push. The disabled-account one follows from On the second half of your comment, the remaining Tests updated along with it: |
|
@nickvergessen on naming the recording backend rather than the owner: I do not think it can be expressed with the current interface, and here is why.
There is a second, softer place where it could live. return $nickname . ' (' . $this->l10nFactory->get('comments')->t('remote user') . ')';so a similar labelled actor for the subject parameters would not be a new concept, but it would be a separate change to the manager interface and the schema rather than something this PR can carry. Worth adding for the Talk side: even with a way to name it, Talk could only do so for part of the flow. On the chunked-upload path the recording file is written by the recording backend through the public share created in Separately, and only because it is easy to miss: the workflow runs on this PR sit in "action required" because it comes from a fork, so nothing beyond DCO has run on it. If one of you approves the runs, the test and lint results will show up. |
Problem
CurrentUser::getUID()reads the user straight from the session:FilesHooksasksCurrentUserfor the actor of a file activity, so anything an app does on a user's behalf outside of their session — from a background job, from a webhook — cannot be attributed to them. The activity stream renders it as"remote account" created ….IManager::setCurrentUserId()exists for exactly this case, andOC\Activity\Manager::getCurrentUserId()honours it, but the activity app never consults it. There is currently no way for an app to name the actor.Change
Read the actor from the activity manager instead of the session:
Manager::getCurrentUserId()returns the override when an app set one, otherwise the session user, and only when there is neither a session nor a valid feed token does it throw, which is caught here sonullis still returned.Against master:
alicealicealicebobnullnullnullcaroldavenull''alicenullThe last three rows are the differences. The feed-token one attributes such a request to the token owner, where the method previously returned
nullandgetUserIdentifier()fell through to the cloud id or the nickname header. The disabled-account one follows fromSession::isLoggedIn()being false for a disabled user, so the manager goes to the token lookup and throws rather than reading the session; a request from a disabled account should not be producing activities in the first place. The last one only happens when an app passes''tosetCurrentUserId(), andnullfits the?stringreturn better there. Say the word if you would rather keep any of them on the old path.Motivation
Raised in nextcloud/spreed#19078, where call recordings are stored by a background job and end up attributed to nobody. The first attempt there swapped the session user around the file write, which @nickvergessen rightly pushed back on:
With this in place Talk calls
setCurrentUserId()around storing the recording and the session juggling is gone. That PR is a no-op until this one lands.Testing
The
CurrentUsertests are updated along with the change:getUID()no longer touches the session, so the data provider drives the manager and every case asserts the session is never read, with separate cases for an override being set and for the manager throwing.