Add a new llm service that proxies chat-completion requests from AI web extensions to a configured OpenAI-compatible endpoint, replacing the standalone Node.js sidecar. It is part of the oCIS single binary, opt-in via OCIS_ADD_RUN_SERVICES=llm, and exposed through the proxy at POST /graph/v1beta1/extensions/org.libregraph/llm/chat/completions.
Authentication is delegated to oCIS. Requests are rate-limited per user with a sliding window backed by the shared store, and the request body is sanitized before forwarding. Streaming requests are rejected.
Signed-off-by: Lukas Hirt <info@hirt.cz>
Scrolling inside an app such as OnlyOffice could make the whole page bounce
with it, exposing the blank page background behind the app frame. Set
overscroll-behavior-y: none on the root element while AppWrapper is mounted
and restore the previous value on unmount. The logic lives in a new
useDisableRootOverscroll composable, which is covered by unit tests.
Signed-off-by: Lukas Hirt <info@hirt.cz>
* fix(web): [OCISDEV-1511] refresh files stuck in processing after upload or copy
A postprocessing-finished event that arrives while a folder reload or a
fetch after copy is still in flight is dropped, because the resource is
not in the store yet. The response then adds the resource as processing
and nothing clears it until a page reload.
Remember finished items for a short time and refetch them when they show
up in the store as processing.
* chore: [OCISDEV-1511] add changelog
ci(release): remove the disabled docker jobs from the release pipeline (#13128)
Tagging v9.0.0-rc.1 produced a startup_failure: the workflow was rejected
before any job ran. owncloud pins Actions to an org allowlist
(owncloud/admin:actions-allowlist.yml, allowed_actions: selected with
sha_pinning_required), and three pins in this file are not on it:
docker/setup-buildx-action @f87e5991 v4.4.1 (allowlist has v4.0.0)
docker/login-action @dbcb8138 v4.6.0 (allowlist has v4.1.0)
docker/build-push-action @c3c9e263 v7.4.0 (allowlist has v7.0.0)
Allowlist validation happens at parse time, so it does not care that all
three only appear in jobs already marked `if: false`.
Rather than pin them back, drop the jobs. Building, scanning and publishing
the production image moved to owncloud-docker/ocis, which is why these were
disabled in the first place; the image Trivy scan now runs there on every PR.
That leaves nothing to maintain here and removes all three offending pins.
Removes docker-build, docker-scan (dead too: it needs docker-build),
docker-manifest and docker-readme. Nothing outside that subgraph depended on
them, so the `docker_repos` and `is_production` outputs, the
PRODUCTION_RELEASE_TAGS / DOCKER_REPO_ROLLING / DOCKER_REPO_PRODUCTION env
vars and the REPOS computation go with them. is_prerelease stays, as
create-github-release still reads it.
The filesystem scan in security-scan-trivy is untouched.
(cherry picked from commit aab12d018e)
Signed-off-by: Julian Koberg <julian.koberg@kiteworks.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Save the MFA status after the user is resolved so signed-URL downloads
inherit it, and check its age on read. Build the signing-key URL with
the URL API so it works with the vault base URL.
* fix: [OCISDEV-1476] fix AuthApp requests failing with a custom PROXY_USER_OIDC_CLAIM
* fix: [OCISDEV-1476] map ErrAccountNotFound to 401 in verifyUser
AccountResolver.verifyUser only mapped ErrAccountDisabled to 401 before
falling through to a generic 500. The identity backend excludes disabled
accounts from by-username lookups and returns ErrAccountNotFound instead,
so a disabled user authenticating via AppAuth or basic auth got a 500
instead of a 401, caught by the acceptance test suite.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* fix: [OCISDEV-1523] fix the LDAP master-ID filter for an empty base filter
EnhanceFilterWithMasterID replaced an empty LDAP filter with a master-ID-only
filter. This action restricted an unrestricted search to master-ID users
only. The bug broke the cross-instance user search in multi-instance
deployments. The function now keeps an empty filter unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix: [OCISDEV-1523] require an explicit LDAP user filter with a master ID
EnhanceFilterWithMasterID in ocis-pkg/ldap/filter.go now keeps an empty
LDAP filter unchanged instead of restricting it to master-ID users only.
The auth-basic, users, groups, and graph services reuse this function to
compute their default LDAP user filter at startup.
An unset filter now means these services resolve or authenticate every
user in the shared LDAP directory, not only master-ID users. This action
is a behavior change for a deployment that sets
OCIS_MULTI_INSTANCE_MASTER_ID without an explicit OCIS_LDAP_USER_FILTER.
Each service now rejects startup when OCIS_MULTI_INSTANCE_MASTER_ID is
set and its own user filter is empty. This action keeps a multi-instance
deployment scoped to its own users by default.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* refactor: [OCISDEV-1523] simplify EnhanceFilterWithMasterID guard clauses
The function collapses three early-return guards into one condition.
This action removes a duplicated rationale comment between filter.go
and filter_test.go.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* docs: [OCISDEV-1523] point the changelog entry at the master backport PR
The changelog entry pointed at PR 13096, the stable-8.2 backport. This
commit points the entry at the master-branch PR number instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Requests on unprotected routes can carry a reva token without a resolved
user: reva's internal downloads to the data gateway (/data) are sent with
the caller's token. create_home logged "no user in context" at error level
for each of them, so every download added an error line. Log at debug with
the path instead; the request is passed on unchanged, as before.
Fixes#13101
Claude-Session: https://claude.ai/code/session_01WpoeSoKYEdgQcWjEzTVKpT
Signed-off-by: Paul Faure <paul@faure.ca>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The file and folder converters filled User from the event's Owner, which
reva does not set, so the field was empty on every upload, download, move,
delete, restore and version restore; where Owner was set it named the owner
instead of the actor. Space, user and group converters hard-coded User to
an empty string although every one of those events carries the Executant.
User now comes from the event's Executant everywhere an actor exists. The
Owner field of file events is unchanged; the failed public-link access
event has no actor and keeps an empty User.
Tests give the fixtures different owner and actor ids so they can tell the
two apart (they fail on the previous code), and add cases for the user and
group converters. The group member added/removed messages passed the user
and group ids in swapped order; fixed.
Fixes#4789
Signed-off-by: Paul Faure <paul@faure.ca>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Juan Pablo Villafañez <jpvillafanez@izertis.com>
The next oCIS release is 9.0.0, so all envvars that were annotated with
the previously planned 8.3.0 introduction version need to be updated.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(search): add a --timeout flag to the index command
ocis search index called the search service with a hard-coded 10 minute
request timeout. The service walks the whole space inside that request, so
when the timeout expired the walk was cancelled (error walking the tree:
context canceled) and everything after that point stayed unindexed. A space
whose walk takes longer than ten minutes could never be fully indexed from
the command line.
The timeout is now a flag: --timeout takes a duration, default 10m so the
behaviour is unchanged out of the box; 0 waits indefinitely (go-micro treats
a zero RequestTimeout as an already-expired deadline, so a far-off one is
substituted).
Fixes#13052
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Paul Faure <paul@faure.ca>
* docs(search): say the index command needs the running search service
The index command is a gRPC client of the search service and never opens
the Bleve index, so it works with the default exclusive lock
(SEARCH_ENGINE_BLEVE_SCALE=false) but needs the service running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpoeSoKYEdgQcWjEzTVKpT
Signed-off-by: Paul Faure <paul@faure.ca>
---------
Signed-off-by: Paul Faure <paul@faure.ca>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* feat(audit): record tag changes in the audit log
Graph publishes TagsAdded and TagsRemoved on every tag assign and unassign,
but only the search service listened, so a tag change left no trace in the
audit log.
The audit service now registers both events and logs them as file_tags_add
and file_tags_remove, with the acting user (Executant), the space owner, the
file id and path, and the comma-separated tags. Graph sets the Timestamp on
both events, which it left unset. Two test cases use different owner and
actor ids; the audit README documents the two actions.
Fixes#13054
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Paul Faure <paul@faure.ca>
* fix(audit): record only the tags that actually changed; skip no-op tag events
Review follow-up for #13055:
- graph: TagsAdded/TagsRemoved now carry the tags that were actually added
or removed (requesting an existing tag, or removing an absent one, is not
a change). Search only uses the event's Ref, so it is unaffected.
- audit: skip tag events whose tag list is empty. Graph still publishes
TagsRemoved for search when nothing was present; that is not an audit event.
- audit: document why User is the executant for tag events; describe Tags
as carried by the event; remove a stray copy-pasted doc comment.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpoeSoKYEdgQcWjEzTVKpT
Signed-off-by: Paul Faure <paul@faure.ca>
* fix(audit): carry the requested tags in tag events, as before
Per review: TagsAdded/TagsRemoved go back to carrying the tags as
requested, and the audit log records every request, including tags that
were already present (or absent, for a removal), so repeated requests stay
visible. Computing the actual delta belongs in the tags package, not here.
Removes the graph-side tagsDiff helper and its test, and the audit
service's skip for empty tag lists. The other review fixes stay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpoeSoKYEdgQcWjEzTVKpT
Signed-off-by: Paul Faure <paul@faure.ca>
---------
Signed-off-by: Paul Faure <paul@faure.ca>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* refactor(storage-users): [OCISDEV-1382] list upload sessions through the coordinator
Signed-off-by: Firas Frikha <firas.frikha@kiteworks.com>
* fix(storage-users): [OCISDEV-1382] use a safe type assertion for the driver config
Signed-off-by: Firas Frikha <firas.frikha@kiteworks.com>
---------
Signed-off-by: Firas Frikha <firas.frikha@kiteworks.com>
Remove the "Session expiring" extend/dismiss popup and its expiry
worker. Drop the frontend session_duration capability and config;
OCIS_MFA_SESSION_DURATION stays in use by the proxy.
* chore(OCISDEV-900): bump reva to pr7-switchover, add upload_directory and mount_id to revaconfig
* chore(OCISDEV-900): bump reva to the switchover rebased on the upload write fix
* chore(OCISDEV-900): bump reva to main with the upload coordinator switchover
---------
Co-authored-by: Firas Frikha <firas.frikha@kiteworks.com>
* fix: the public share configuration and fixed graceful shutdown
* fix: [OCISDEV-1433] remove GRPC_MAX_CONNECTION_AGE
The setting closed healthy grpc connections on a timer so that clients would redo
their DNS lookup. It never helped against the failure it was reached for - a
connection whose peer stopped answering - because the age limit is enforced by
the server, the grace period was left at infinity so a request already in flight
was never ended, and any value without a unit suffix silently disabled it again.
Detecting a peer that stops answering is now the job of the grpc clients, which
ping the peer while a request is in flight. The servers here got the matching
enforcement policy so they accept those pings; the client side arrives with the
next reva bump.
* fix: Added the new variable OCDAV_DISABLE_PROPFIND_PUBLIC_LINK_RESOLUTION that disable the resolution of public link shares when answering PROPFIND requests.
* chore: bump reva
* fix: The keepalive added to grpc clinet
* chore: bump reva
* build(deps): bump go 1.26.8, grpc-go dependency
---------
(cherry picked from commit e86eb4d210)
For the 9.0 branch, the reva dependency was pointed at reva's main-branch
merge of PR #748 (commit 373f9509a, pseudo-version
v2.0.0-20261005091647-373f9509a363) instead of the 8.2-targeted
e0beaa380005, since the 8.2 merge predates the VaultMode/UploadSessionFilter.Orphaned
APIs already present on the 9.0 reva lineage.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts:
# changelog/unreleased/security-bump-grpc.md
# go.mod
# go.sum
# vendor/google.golang.org/grpc/server.go
# vendor/google.golang.org/grpc/stream.go
# vendor/google.golang.org/grpc/version.go
# vendor/modules.txt
`requireAcr()`, used by the admin settings route guards, redirected to the
IdP whenever the `acr` did not match, so a user without a second factor
looped forever.
It now uses the same protection as the vault: the step-up carries its
target as OIDC state, and if the IdP answers it without the required
`acr`, `requireAcr()` returns false. The admin settings guards then
redirect to the default view and an error message is shown.
Follow-up to https://github.com/owncloud/ocis/pull/12985, which fixed the same loop for the vault.
* chore: bump reva
* chore: bump pnpm dependencies
* fix: avoid go vet false positive in userlog conversion.go
The go.mod bump to go 1.26.8 made go vet flag loadTemplates' calls to
t.Get(nt.Subject/nt.Message) as non-constant format strings, breaking
the build for services/userlog/pkg/service. These are translation
lookup keys, not printf format strings, so route the call through a
local func value to defeat vet's static trace.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* chore: bump grpc
Fixes GO-2026-6443, a server panic when handling requests missing
authority or Host headers, flagged by govulncheck in CI.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Selecting a language dropped focus to the page body and the save
confirmation went unannounced, because the select's own best-effort
refocus raced against the handler's translation reload and network
save. Restore focus explicitly once that work settles, before
announcing the save.
* fix(web): stop redirect loop when vault MFA step-up fails
Signed-off-by: Samuel Schumacher <samuel.schumacher@werkspiel.de>
Signed-off-by: Samuel Schumacher <schumacher@b1-systems.de>
* fix(web): leave vault with a full page load after failed MFA step-up
Returning to `/` in-app kept the vault clients and loaded vault spaces
with the non-MFA token, which ended on the RUNTIME-BOOTSTRAP-040 crash
page. Vault mode is fixed per page load, so reload outside the vault
instead and show the MFA error message after the reload. The spaces
load error handler no longer routes to the crash page while leaving.
Also addresses review feedback:
- use $pgettext with translator context for the error message
- test that an expired user with a fresh marker is still redirected
* fix(web): only treat a vault step-up as failed after the IdP answered it
The step-up marker relied on a 5 minute TTL, so a user who went to the
IdP, pressed back and opened the vault again got the error without ever
seeing the IdP. The step-up request now carries its target as OIDC
state; the sign-in callback picks it up and only the navigation right
after the callback counts as the answer. The sessionStorage marker and
the TTL are gone.
Also addresses review feedback:
- simplify the auth guard check to `redirect !== undefined`
- drop the storage key re-export, the spec imports it from the helper
Signed-off-by: Samuel Schumacher <schumacher@b1-systems.de>
---------
Signed-off-by: Samuel Schumacher <samuel.schumacher@werkspiel.de>
Signed-off-by: Samuel Schumacher <schumacher@b1-systems.de>