Skip to content

[full-ci] feat: ocisdev-533 graph#12108

Open
2403905 wants to merge 8 commits intomasterfrom
feat/OCISDEV-533-graph
Open

[full-ci] feat: ocisdev-533 graph#12108
2403905 wants to merge 8 commits intomasterfrom
feat/OCISDEV-533-graph

Conversation

@2403905
Copy link
Copy Markdown
Contributor

@2403905 2403905 commented Mar 12, 2026

Description

Provide the separated vault storage that could be MFA-protected

Implementation approach:

Provide the dedicated storage-users and graph service to care only about vault storage.

  • Provide the new vault user storage with a dedicated VaultStorageProviderID mounted to "/vault/users" and "/vault/projects".
  • Teach the storage registry to associate the vault storage with the dedicated storage-users service.
  • Modify the graph service to force using StorageProviderID in a vault-mode. Run in addition the graph API serves the /vault prefix.
  • Run the dedicated storage-users service pointed to the vault.

Related reva PR owncloud/reva#559

How to run in a Docker

UPD: 13.04.2026

  • build the dev docker image
  • goto ./deployments/examples/ocis_full
  • in a .env uncomment the line KEYCLOAK=:keycloak.yml and VAULT_STORAGE=:vault-storage.yml
  • run the docker compose IDM_ADMIN_PASSWORD=admin DEMO_USERS=true OCIS_DOCKER_TAG=dev OCIS_MFA_ENABLED=true docker compose up -d

How to run locally

UPD: 18.03.2026 - No extra graph service needed
ocis main

WEB_ASSET_CORE_PATH=/Users/roman/projects/owncloud-extra/web/dist \
OCIS_MFA_ENABLED=true \
PROXY_CREATE_VAULT_HOME=true \
GRAPH_ENABLE_VAULT_MODE=true \
IDM_ADMIN_PASSWORD=admin IDM_CREATE_DEMO_USERS=true PROXY_ENABLE_BASIC_AUTH=true OCIS_LOG_LEVEL=info ./ocis/bin/ocis server

In a Keycloak setup set to trueOCIS_MFA_ENABLED
WEB_ASSET_CORE_PATH={path to web}/web/dist \

Vault storage-users

OCIS_LOG_LEVEL=debug \
STORAGE_USERS_ENABLE_VAULT_MODE=true \
STORAGE_USERS_SERVICE_NAME=storage-users-vault \
STORAGE_USERS_GRPC_ADDR=0.0.0.0:9170 \
STORAGE_USERS_HTTP_ADDR=0.0.0.0:9168 \
STORAGE_USERS_DATA_SERVER_URL=http://localhost:9168/data \
STORAGE_USERS_DEBUG_ADDR=0.0.0.0:9169 \
STORAGE_USERS_OCIS_ROOT=${HOME}/.ocis/storage/users-vault \
STORAGE_USERS_EVENTS_CONSUMER_GROUP=vault-dcfs \
./ocis/bin/ocis storage-users server
curl 'https://localhost:9200/vault/graph/v1beta1/drives' \
-H 'Accept: application/json' \
--data-raw '{"name":"vault Space"}' \
--insecure  -uadmin:admin

curl 'https://localhost:9200/vault/graph/v1beta1/me/drives?%24orderby=name+asc&%24filter=driveType+eq+project' \
-H 'Accept: application/json' \
--insecure  -uadmin:admin

curl 'https://localhost:9200/vault/graph/v1beta1/me/drives?%24orderby=name+asc&%24filter=driveType+eq+personal' \
-H 'Accept: application/json' \
--insecure  -uadmin:admin

FS:

~/.ocis/storage
$ tree  users*
users
├── indexes
│   ├── by-group-id
│   ├── by-type
│   │   └── personal.mpk
│   └── by-user-id
│       └── a032f2bd-fa5c-430b-a163-2c19f54190d0.mpk
├── spaces
│   └── a0
│       └── 32f2bd-fa5c-430b-a163-2c19f54190d0
│           └── nodes
│               └── a0
│                   └── 32
│                       └── f2
│                           └── bd
│                               ├── -fa5c-430b-a163-2c19f54190d0
│                               ├── -fa5c-430b-a163-2c19f54190d0.mlock
│                               └── -fa5c-430b-a163-2c19f54190d0.mpk
└── uploads
users-vault
├── indexes
│   ├── by-group-id
│   ├── by-type
│   │   └── personal.mpk
│   └── by-user-id
│       └── a032f2bd-fa5c-430b-a163-2c19f54190d0.mpk
├── spaces
│   └── a0
│       └── 32f2bd-fa5c-430b-a163-2c19f54190d0
│           └── nodes
│               └── a0
│                   └── 32
│                       └── f2
│                           └── bd
│                               ├── -fa5c-430b-a163-2c19f54190d0
│                               ├── -fa5c-430b-a163-2c19f54190d0.mlock
│                               └── -fa5c-430b-a163-2c19f54190d0.mpk
└── uploads

@update-docs
Copy link
Copy Markdown

update-docs Bot commented Mar 12, 2026

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a changelog item based on your changes.

@2403905 2403905 requested review from jvillafanez and kobergj March 12, 2026 11:15
@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch from 96ce268 to c79f474 Compare March 12, 2026 11:31
Comment thread services/graph/pkg/config/config.go Outdated
@jvillafanez
Copy link
Copy Markdown
Member

There are a couple of things that seems weird to me:

  • I understand that we can setup a storage-user service in "vault mode" so it's only accessible / usable with MFA, but why graph?
    • The graph service can just check if the request is under MFA to do anything with the vault.
  • Using mount ids in the configuration isn't user-friendly. If possible, I'd drop them, otherwise they MUST be documented, including where such id can be found and / or provide a command to get the id.
    • Note that it's easy to make typos and setup a mount id that doesn't exist. And people makes mistakes and might use any random id found anywhere, so it might point to who-knows-what.

@2403905
Copy link
Copy Markdown
Contributor Author

2403905 commented Mar 12, 2026

There are a couple of things that seems weird to me:

  • I understand that we can setup a storage-user service in "vault mode" so it's only accessible / usable with MFA, but why graph?

Maybe we can improve it and use only one graph. Now I use the second one for enforcing the vault storage and MFA for all graph endpoints

  • Note that it's easy to make typos and setup a mount id that doesn't exist. And people makes mistakes and might use any random id found anywhere, so it might point to who-knows-what.

Ideally, we could try to add one more storageprovider in a config and get rid of the dedicated storage-users service.

"services": map[string]interface{}{
"storageprovider": map[string]interface{}{
"driver": cfg.Driver,
"drivers": StorageProviderDrivers(cfg),
"mount_id": cfg.MountID,
"expose_data_server": cfg.ExposeDataServer,
"data_server_url": cfg.DataServerURL,
"upload_expiration": cfg.UploadExpiration,
"events": map[string]interface{}{
"nats_address": cfg.Events.Addr,
"nats_clusterid": cfg.Events.ClusterID,
"tls_insecure": cfg.Events.TLSInsecure,
"tls_root_ca_cert": cfg.Events.TLSRootCaCertPath,
"nats_enable_tls": cfg.Events.EnableTLS,
"nats_username": cfg.Events.AuthUsername,
"nats_password": cfg.Events.AuthPassword,
},
},
},
"interceptors": map[string]interface{}{

Thank you.

@jvillafanez
Copy link
Copy Markdown
Member

Maybe we can improve it and use only one graph. Now I use the second one for enforcing the vault storage and MFA for all graph endpoints

MFA needs to be enforced in the vault storage. Technically, graph shouldn't need to enforce MFA; it can make the request and the request will fail. The fact that we want graph to check for MFA is mostly for convenience, to avoid making a request that we know it will fail.

In addition, whether the request is under MFA or not is information that should be part of the request, and should be propagated as part of the request. This is very similar to what is done with telemetry. Graph shouldn't need a configuration flag to know if it can access to the vault or if MFA is active.

@2403905 2403905 changed the title Feat/ocisdev 533 graph [full-ci] feat: ocisdev-533 graph Mar 14, 2026
@2403905 2403905 requested review from Copilot and mklos-kw March 16, 2026 14:33
@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch from df07882 to a9fa2c1 Compare March 16, 2026 14:35
@2403905 2403905 marked this pull request as ready for review March 16, 2026 14:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the vendored reva dependency and introduces “vault mode” support across gateway, storage-users, proxy, and graph, including storage scoping for events and storage space selection.

Changes:

  • Bump github.com/owncloud/reva/v2 vendor version and adapt code to upstream changes (events, storage registry filtering, gateway client acquisition).
  • Add vault-mode plumbing: vault storage provider IDs/constants, vault-specific space provider config, and passing storage_id via CS3 opaque to target the vault storage.
  • Add configurable event consumer group and storage-aware filtering for decomposedfs postprocessing events; add MFA middleware integration for graph routes.

Reviewed changes

Copilot reviewed 18 out of 34 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
vendor/modules.txt Updates vendored module version for reva/v2.
vendor/github.com/owncloud/reva/v2/pkg/utils/utils.go Adds vault storage provider/space IDs.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/options/options.go Adds consumer_group event option + default.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go Uses configurable consumer group; filters events by storage.
vendor/github.com/owncloud/reva/v2/pkg/storage/registry/spaces/spaces.go Filters vault spaces unless explicitly requested; supports storage_id filter.
vendor/github.com/owncloud/reva/v2/pkg/storage/cache/createpersonalspace.go Removes create-personal-space cache implementation.
vendor/github.com/owncloud/reva/v2/pkg/storage/cache/createhome.go Removes create-home cache implementation.
vendor/github.com/owncloud/reva/v2/pkg/storage/cache/cache.go Removes create-home/create-personal-space cache APIs.
vendor/github.com/owncloud/reva/v2/pkg/events/postprocessing.go Adds ResourceID to postprocessing events.
vendor/github.com/owncloud/reva/v2/internal/http/.../shares/spaces.go Simplifies provider client creation via pool directly.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/storageprovider/storageprovider.go Ensures root-info IDs get storage provider IDs filled.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovidercache.go Adjusts cache keying to include storage_id (opaque).
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovider.go Forwards storage_id via opaque and provider selection; removes some caching wrappers.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/gateway.go Removes create-personal-space cache wiring.
services/storage-users/pkg/revaconfig/drivers.go Passes consumer_group to reva storage config.
services/storage-users/pkg/config/defaults/defaultconfig.go Sets MountID to vault provider ID when vault mode enabled.
services/storage-users/pkg/config/config.go Adds enable_vault_mode and consumer_group config fields.
services/proxy/pkg/middleware/create_home.go Adds short-lived in-process TTL cache; creates regular + vault homes via storage_id.
services/proxy/pkg/config/defaults/defaultconfig.go Adds proxy policy route for /vault/graph/.
services/postprocessing/pkg/postprocessing/postprocessing.go Propagates ResourceID into emitted events.
services/policies/pkg/service/event/service.go Propagates ResourceID into emitted events.
services/graph/pkg/service/v0/service.go Adds RequireMFA middleware and vault-mode MFA routing behavior.
services/graph/pkg/service/v0/graph_test.go Updates tests to exercise router (ServeHTTP) instead of direct handler calls.
services/graph/pkg/service/v0/drives.go Forces vault storage selection via storage_id in opaque; removes inline MFA checks.
services/graph/pkg/service/v0/driveitems.go Adds vault-mode filtering for personal root children.
services/graph/pkg/middleware/mfa.go New middleware to enforce MFA.
services/graph/pkg/config/service.go Adds env/yaml tags for service name.
services/graph/pkg/config/config.go Adds enable_vault_mode config field.
services/gateway/pkg/revaconfig/config.go Adds a dedicated vault spaces provider definition.
services/gateway/pkg/config/defaults/defaultconfig.go Removes create-home cache defaults.
services/gateway/pkg/config/config.go Removes create-home cache configuration fields.
go.mod / go.sum Updates reva/v2 dependency version checksums.
.gitignore Ignores .agents/ directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/proxy/pkg/middleware/create_home.go Outdated
Comment thread services/graph/pkg/service/v0/driveitems.go Outdated
Comment thread services/graph/pkg/config/config.go Outdated
Comment thread services/graph/pkg/config/service.go Outdated
Comment thread services/storage-users/pkg/config/config.go Outdated
Comment thread services/storage-users/pkg/config/config.go Outdated
@2403905
Copy link
Copy Markdown
Contributor Author

2403905 commented Mar 17, 2026

Maybe we can improve it and use only one graph. Now I use the second one for enforcing the vault storage and MFA for all graph endpoints

MFA needs to be enforced in the vault storage. Technically, graph shouldn't need to enforce MFA; it can make the request and the request will fail. The fact that we want graph to check for MFA is mostly for convenience, to avoid making a request that we know it will fail.

In addition, whether the request is under MFA or not is information that should be part of the request, and should be propagated as part of the request. This is very similar to what is done with telemetry. Graph shouldn't need a configuration flag to know if it can access to the vault or if MFA is active.

That is a good point. @jvillafanez
The idea is to implement the Vault that doesn't require MFA dependencies.
The Vault-graph should ensure that requests go to Vault storage by requiring that some requests, like CreateStorageSpace and GetAllDrives, use the Vault storage ID explicitly.
So, the Vault graph can work without MFA, ensuring the drives' separation.
Then I assumed that the Graph is a good place to force MFA for all of the routes under the /vault with minimal effort.
The cons of this we still need to take care of the ocdav api MFA protection.

We can get rid of the extra vault-graph instance to provide an additional router that could be above the MFA

@2403905
Copy link
Copy Markdown
Contributor Author

2403905 commented Mar 17, 2026

@jvillafanez @mklos-kw I made a commit that makes the graph able to handle the vault prefix. no extra service needed
d456f68

@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch 2 times, most recently from b53fb87 to 5fc45db Compare March 18, 2026 08:53
@mmattel
Copy link
Copy Markdown
Contributor

mmattel commented Mar 18, 2026

The envvar description text says the following:
"GRAPH_ENABLE_VAULT_MODE" desc:"Enable vault mode for the graph service running in addition to the regular graph service. Requires running the storage-users-vault additional service."

This part confuses me: Requires running the storage-users-vault additional service.
We have an additional service (which I cant identify) or just an instance of the storage-users service with a different config? (a config example (or reference) should be added e.g. in the storage-users readme)

The envvar text should be precised (or the missing service added).

@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch 3 times, most recently from 8fcac29 to f9c326c Compare March 19, 2026 18:16
@2403905
Copy link
Copy Markdown
Contributor Author

2403905 commented Mar 20, 2026

The envvar description text says the following: "GRAPH_ENABLE_VAULT_MODE" desc:"Enable vault mode for the graph service running in addition to the regular graph service. Requires running the storage-users-vault additional service."

This part confuses me: Requires running the storage-users-vault additional service. We have an additional service (which I cant identify) or just an instance of the storage-users service with a different config? (a config example (or reference) should be added e.g. in the storage-users readme)

The envvar text should be precised (or the missing service added).

For now, the Vault mode requires running the storage-users as an additional service with specific configuration

@mmattel
Copy link
Copy Markdown
Contributor

mmattel commented Mar 20, 2026

For now, the Vault mode requires running the storage-users as an additional service with specific configuration

OK, makes sense. Please rewrite the envvar text accordingly because the current one does not match.

@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch from 85d7fdb to 702c7c7 Compare March 23, 2026 09:54
@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch from b7e39bc to f4d83bc Compare April 8, 2026 16:35
@2403905
Copy link
Copy Markdown
Contributor Author

2403905 commented Apr 9, 2026

MFA needs to be enforced in the vault. Anyone can access to the vault / storage_users using GRPC, and that will bypass the enforcement. If it's enforced in the vault, at service level, it doesn't matter how the user access (HTTP or GRPC) because he must be under MFA.

I think the problem is that the MFA data isn't propagated among the services. It "works" with graph because there are several direct requests from web to access to the graph service, so the information is there, but since the storage_users service is usually behind and accessed via GRPC, web won't access to it directly. Due to the missing propagation, the storage_users service can't check if the user is under MFA or not because it doesn't have such info. Fixing the data propagation would allow us to check if the request is under MFA anywhere, in particular the storage_users service.

For the office apps, note that the requests come from an external service, so they won't have MFA as such. I assume this is another reason for you to enforce MFA in the graph, but technically you're accessing to the vault without MFA. I'd need to check, but it might be possible to open "vaulted" files with an office app without being under MFA. In order to fix this issue, if the MFA data is propagated, the collaboration service can include MFA information in the access token for the office app (I think this will need to be done manually), so when the office app makes a request with such access token, the collaboration service can get the MFA information from the access token and decide to fetch the "vaulted" file or not (or just send the request with the MFA info).

@jvillafanez This is a very good point. We need to figure out how to propagate the MFA. Can we add it to the "tokenScope"?

@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch from f4d83bc to 76293bb Compare April 13, 2026 09:55
@jvillafanez
Copy link
Copy Markdown
Member

For GRPC, we can use the context. There is work already done as part of the brute force protection for sharing, and the mechanism can be reused (https://github.com/owncloud/reva/blob/d1e2a4cfd79b7b3a0ed7b26b8de6f1f24443eb17/pkg/auth/manager/publicshares/bruteforceprotection.go#L295)
Basically, you use the GRPC context metadata to include a custom key with a specific prefix.

metakey := ocisgrpcmeta.AutoPropPrefix + "mfa-enabled"
metadata.AppendToOutgoingContext(ctx, metakey, "enabled")

We probably should include some useful information instead of just "enabled", mostly to try to prevent tampering.

All the "standard" ocis and reva GRPC services should be configured already to propagate that metadata automatically, so the information should be available everywhere once the information is set in the metadata.

For HTTP, I think we'll have to check what the tracing is doing because I think we should implement this in a similar way. We'll have to check how effective it is for this particular case though.
The alternative is to include our MFA header on each outgoing request and check it on each incoming request. However, it's easy to miss a HTTP request and that would break the propagation chain.

For the office apps, I think it's easy to include the information in our WOPI context (we can add a new field there), and getting the information back from the access token should be trivial. We just need to propagate the information from there using the techniques from the previous points.

In general, I think tracing is working fine, so we should basically copy the propagation model.

@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch 2 times, most recently from 60173c7 to 21da7b7 Compare April 13, 2026 13:43
@jvillafanez
Copy link
Copy Markdown
Member

Anything wrong with #12108 (comment) ?
Just setting the mfa value with the prefixed key in the context should be enough to make the mfa value accessible through all the GRPC services. There is already code prepared for that, no need to duplicate the effort. You have a working example in https://github.com/owncloud/reva/blob/260a74454e4a1929312385c398ae55b74417c2e2/pkg/auth/manager/publicshares/bruteforceprotection.go#L295

What is missing is the propagation through HTTP. The steps should be roughly like:

  1. Server middleware checks if the MFA header is present.
  2. If present, it stores the data in the context using the metadata.AppendToOutgoingContext method. The key must use our custom prefix so it's auto-propagated through the GRPC calls.
  3. Once a request happens from the service, it needs to check the metadata metadata.FromIncomingContext, and check for the prefixed keys. Any HTTP call should get those prefixed keys and send the corresponding values using the HTTP headers. Prefixing headers like X-OCIS-AutoProp-* will help to identify which information needs to be propagated.

}

if m.createVaultHome {
// TODO there is no MFA context
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is there no MFA context here? The mfa middleware should add it. Maybe createHome middleware runs BEFORE mfa middleware?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@kobergj The MFA is not in a context here. It is in a header. I can check if m.createVaultHome && mfa.MfaHeaderIsTrue(req) and later add the condition if the user has an appropriate permission.
We need to review whether we can simplify.

Comment thread services/storage-users/pkg/config/config.go Outdated
Comment thread .gitignore
@2403905 2403905 force-pushed the feat/OCISDEV-533-graph branch 2 times, most recently from 9211b6f to 33c192f Compare April 23, 2026 13:11
2403905 added 5 commits April 23, 2026 23:16
feat: Separate the storage-users and graph to handle

feat: move the space create cache

configurate the vault srorage Postprocessing

hide the assignment of the MountID to VaultStorageProviderID behind a flag to avoid the ID mismatch

configurate the proxy to create the vault home space

update the filter

Make the graph able to handle vault prefix. no extra service needed

apply the vault filter to sharedbyme and sharedwithme

added vault-storage docker

use squashed reva

fix the finishUpload event

restict webdav copy/move from the vault
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a dedicated “vault” storage provider (with optional MFA enforcement) by adding a vault-mode storage-users instance and extending Graph/WebDAV + supporting services to route and filter vault requests appropriately.

Changes:

  • Add a vault storage provider ID and wiring to route /vault/* spaces to a dedicated storage-users-vault service.
  • Enforce/propagate MFA state across HTTP → go-micro → gRPC hops and add gRPC-side MFA blocking for vault storage.
  • Update Graph (vault-mode routing + filtering) and WebDAV copy/move restrictions for vault resources.

Reviewed changes

Copilot reviewed 36 out of 59 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
vendor/modules.txt Bump vendored reva/v2 version.
vendor/github.com/owncloud/reva/v2/pkg/utils/utils.go Add VaultStorageProviderID constant.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/upload/upload.go Include StorageId in postprocessing event ResourceID.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/options/options.go Add events consumer_group option + default.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go Use configurable consumer group + ignore events for other storages.
vendor/github.com/owncloud/reva/v2/pkg/storage/registry/spaces/spaces.go Filter vault spaces unless storage_id is explicitly requested.
vendor/github.com/owncloud/reva/v2/pkg/events/postprocessing.go Add ResourceID to postprocessing events.
vendor/github.com/owncloud/reva/v2/pkg/ctx/mfactx.go Define MFA HTTP header + gRPC autoprop metadata key.
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go Import order adjustment (vendored).
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/oidc/oidc.go Import order adjustment (vendored).
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/spaces.go Simplify storage provider client acquisition.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go Consider StorageId when checking parent/child refs; add vault destination restriction helper.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/move.go Block MOVE from vault to non-vault destinations.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/copy.go Block COPY from vault to non-vault destinations.
vendor/github.com/owncloud/reva/v2/internal/http/services/archiver/handler.go Emit X-Ocis-Mfa-Required header on MFA-related permission denials.
vendor/github.com/owncloud/reva/v2/internal/http/interceptors/auth/auth.go Forward MFA HTTP header into outgoing gRPC metadata.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/storageprovider/storageprovider.go Ensure storage provider ID is set on additional resource IDs in Stat responses.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovidercache.go Include storage_id in cache keying for provider listing.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovider.go Propagate storage_id through CreateHome/CreateStorageSpace/ListStorageSpaces.
vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/auth/mfa.go Add gRPC-side MFA blocking response helper.
vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/auth/auth.go Add mfa_enabled option and enforce MFA via incoming metadata.
services/webdav/pkg/service/v0/service.go Forward MFA status via go-micro metadata for thumbnails calls.
services/thumbnails/pkg/thumbnail/imgsource/cs3.go Bridge go-micro MFA metadata into gRPC metadata for gateway calls.
services/thumbnails/pkg/service/grpc/v0/service.go Bridge MFA metadata during Stat calls used for thumbnail generation.
services/storage-users/pkg/revaconfig/drivers.go Pass mount ID + event consumer group into reva storage driver configs.
services/storage-users/pkg/revaconfig/config.go Enable mfa_enabled in reva auth interceptor when vault mode is active.
services/storage-users/pkg/config/defaults/defaultconfig.go Force mount ID to VaultStorageProviderID in vault mode.
services/storage-users/pkg/config/config.go Add vault-mode toggle + consumer group config fields.
services/proxy/pkg/middleware/options.go Add MFA store + CreateVaultHome option plumbing.
services/proxy/pkg/middleware/mfa.go Persist MFA verification status for non-OIDC flows.
services/proxy/pkg/middleware/create_home.go Optionally provision vault home (forcing MFA metadata for provisioning call).
services/proxy/pkg/config/defaults/defaultconfig.go Route /vault/graph/ to Graph via proxy policies.
services/proxy/pkg/config/config.go Add create_vault_home config flag.
services/proxy/pkg/command/server.go Wire MFA store and CreateVaultHome into middleware chain.
services/postprocessing/pkg/postprocessing/postprocessing.go Include ResourceID in emitted PostprocessingFinished events.
services/policies/pkg/service/event/service.go Propagate ResourceID into PostprocessingStepFinished events.
services/graph/pkg/service/v0/spacetemplates.go Use vault-mode to target the vault storage-users instance for template operations.
services/graph/pkg/service/v0/sharedwithme.go Filter received shares by vault vs non-vault mode.
services/graph/pkg/service/v0/sharedbyme.go Filter “shared by me” results by vault vs non-vault mode.
services/graph/pkg/service/v0/service.go Add /vault/graph routing + require MFA for drives listing endpoints.
services/graph/pkg/service/v0/graph_test.go Update tests to execute via router (middleware-aware).
services/graph/pkg/service/v0/drives.go Force storage_id in vault mode for space creation/listing.
services/graph/pkg/service/v0/driveitems_test.go Add tests ensuring vault mode injects storage_id filter.
services/graph/pkg/service/v0/driveitems.go Inject storage_id filter for root drive children listing in vault mode.
services/graph/pkg/middleware/vault.go Add vault-mode context/middleware.
services/graph/pkg/middleware/mfa.go Add Graph middleware to require MFA.
services/graph/pkg/middleware/auth.go Propagate MFA status into outgoing gRPC metadata.
services/graph/pkg/config/service.go Make Graph service name configurable via env var.
services/graph/pkg/config/config.go Add Graph vault-mode toggle.
services/gateway/pkg/revaconfig/config.go Register dedicated vault storage provider and mountpoints.
services/collaboration/pkg/service/grpc/v0/service.go Carry MFA state into WOPI context.
services/collaboration/pkg/middleware/wopicontext.go Propagate WOPI MFA claim into outgoing gRPC metadata.
services/collaboration/pkg/connector/httpadapter.go Propagate MFA HTTP header into outgoing gRPC metadata for connector ops.
go.mod / go.sum Update dependencies for reva bump.
deployments/examples/ocis_full/vault-storage.yml Add example compose overlay for vault storage-users service.
deployments/examples/ocis_full/.env Add VAULT_STORAGE compose overlay toggle.
.make/go.mk Adjust debug docker build flags.
.gitignore Add ignores for local agent/tool files.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/storage-users/pkg/config/config.go Outdated
Comment thread services/graph/pkg/config/config.go Outdated
Comment thread .make/go.mk
Comment thread services/graph/pkg/service/v0/spacetemplates.go
Comment on lines +48 to +49
EnableVaultMode bool `yaml:"enable_vault_mode" env:"STORAGE_USERS_ENABLE_VAULT_MODE" desc:"Enable vault mode for the storage-users service was run in addition to the regular storage-users service by owerrwiting the MountID to VaultStorageProviderID. Required the running the storage-users-vault additional service." introductionVersion:"Deledda"`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed to: "Not applicable for use with the primary storage-users service. Used only when an additional storage-users service needs to be run in vault mode. Enabling the flag forces the storage-users service to run with a MountID set to VaultStorageProviderID."

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a dedicated “vault” storage area intended to be MFA-protected by splitting responsibilities across a dedicated storage-users-vault instance and adding vault-aware routing/filtering in Graph, WebDAV, and supporting services.

Changes:

  • Add a vault storage provider ID and wire a dedicated storage-users-vault provider/mount (/vault/users, /vault/projects) into the gateway registry and space/provider lookups.
  • Enforce/propagate MFA across HTTP → gRPC hops (proxy, graph, webdav, thumbnails, collaboration/WOPI) and add a gRPC MFA gate for the vault storage-users instance.
  • Add vault-mode Graph routing (/vault/graph/...) and filter spaces/shares/drives based on the vault storage provider.

Reviewed changes

Copilot reviewed 36 out of 59 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vendor/modules.txt Bumps vendored module listing for updated reva version.
vendor/github.com/owncloud/reva/v2/pkg/utils/utils.go Adds VaultStorageProviderID constant.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/upload/upload.go Includes StorageId in emitted postprocessing ResourceID.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/options/options.go Adds events consumer_group option with default.
vendor/github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go Uses configurable consumer group + ignores postprocessing events for other storages.
vendor/github.com/owncloud/reva/v2/pkg/storage/registry/spaces/spaces.go Adds vault-aware filtering and supports storage_id filter propagation.
vendor/github.com/owncloud/reva/v2/pkg/events/postprocessing.go Extends postprocessing events with ResourceID.
vendor/github.com/owncloud/reva/v2/pkg/ctx/mfactx.go Introduces MFA metadata/header constants for propagation.
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go Import ordering adjustment due to vendoring changes.
vendor/github.com/owncloud/reva/v2/pkg/auth/manager/oidc/oidc.go Import ordering adjustment due to vendoring changes.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/spaces.go Simplifies storage provider client acquisition.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/ocdav.go Adds storage ID check in recursion detection + helper to restrict vault copies/moves.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/move.go Blocks moving from vault to non-vault destinations.
vendor/github.com/owncloud/reva/v2/internal/http/services/owncloud/ocdav/copy.go Blocks copying from vault to non-vault destinations.
vendor/github.com/owncloud/reva/v2/internal/http/services/archiver/handler.go Emits MFA-required header + status behavior for MFA-protected accesses.
vendor/github.com/owncloud/reva/v2/internal/http/interceptors/auth/auth.go Forwards MFA status from HTTP header into outgoing gRPC metadata.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/storageprovider/storageprovider.go Ensures storage provider IDs are set (incl. root_info.id).
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovidercache.go Improves cache key derivation to include storage_id and avoid collisions.
vendor/github.com/owncloud/reva/v2/internal/grpc/services/gateway/storageprovider.go Propagates storage_id through create/list requests for vault handling.
vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/auth/mfa.go Adds MFA-blocking responder for many storageprovider RPCs.
vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/auth/auth.go Adds mfa_enabled config and enforces MFA based on incoming metadata.
services/webdav/pkg/service/v0/service.go Bridges MFA into go-micro metadata for thumbnail requests.
services/thumbnails/pkg/thumbnail/imgsource/cs3.go Bridges MFA from go-micro metadata into gRPC outgoing metadata.
services/thumbnails/pkg/service/grpc/v0/service.go Preserves request context and bridges MFA when stat’ing via gateway.
services/storage-users/pkg/revaconfig/drivers.go Passes mount ID and event consumer group into reva driver config.
services/storage-users/pkg/revaconfig/config.go Enables MFA enforcement in auth interceptor when vault mode is on.
services/storage-users/pkg/config/defaults/defaultconfig.go Sets mount ID to VaultStorageProviderID when vault mode is enabled.
services/storage-users/pkg/config/config.go Adds vault mode and event consumer group configuration options.
services/proxy/pkg/middleware/options.go Adds MFAStore and CreateVaultHome middleware options.
services/proxy/pkg/middleware/mfa.go Persists MFA status for non-OIDC requests via store with TTL.
services/proxy/pkg/middleware/create_home.go Optionally provisions a vault home (forcing MFA metadata for provisioning).
services/proxy/pkg/config/defaults/defaultconfig.go Adds proxy policy entry for /vault/graph/.
services/proxy/pkg/config/config.go Adds PROXY_CREATE_VAULT_HOME config option.
services/proxy/pkg/command/server.go Wires MFAStore and CreateVaultHome into middleware chain.
services/postprocessing/pkg/postprocessing/postprocessing.go Includes ResourceID in finished postprocessing event.
services/policies/pkg/service/event/service.go Propagates ResourceID through policies-driven postprocessing events.
services/graph/pkg/service/v0/spacetemplates.go Selects storage-users(-vault) address based on vault mode.
services/graph/pkg/service/v0/sharedwithme.go Filters “shared with me” results based on vault vs regular mode.
services/graph/pkg/service/v0/sharedbyme.go Filters “shared by me” results based on vault vs regular mode.
services/graph/pkg/service/v0/service.go Adds /vault/graph routes and applies vault/MFA middleware.
services/graph/pkg/service/v0/graph_test.go Updates tests to route through the service router for middleware coverage.
services/graph/pkg/service/v0/drives.go Forces vault storage_id in Create/List flows when vault mode is enabled.
services/graph/pkg/service/v0/driveitems_test.go Adds tests for vault-mode storage_id filtering behavior.
services/graph/pkg/service/v0/driveitems.go Forces vault storage_id when listing personal root drive children in vault mode.
services/graph/pkg/middleware/vault.go Adds vault-mode context marker + middleware.
services/graph/pkg/middleware/mfa.go Adds Graph-level RequireMFA middleware.
services/graph/pkg/middleware/auth.go Propagates MFA state to outgoing gRPC metadata from Graph.
services/graph/pkg/config/service.go Makes graph service name configurable via env/yaml.
services/graph/pkg/config/config.go Adds GRAPH_ENABLE_VAULT_MODE config option.
services/gateway/pkg/revaconfig/config.go Registers dedicated storage-users-vault provider with vault mount points.
services/collaboration/pkg/service/grpc/v0/service.go Carries MFA status into WOPI token context.
services/collaboration/pkg/middleware/wopicontext.go Propagates MFA status from WOPI token into outgoing gRPC metadata.
services/collaboration/pkg/connector/httpadapter.go Propagates MFA header into outgoing gRPC metadata for connector calls.
go.sum Updates checksums for new reva version.
go.mod Bumps github.com/owncloud/reva/v2 dependency.
deployments/examples/ocis_full/vault-storage.yml Adds example compose overlay to run storage-users-vault and enable vault mode.
deployments/examples/ocis_full/.env Adds VAULT_STORAGE compose overlay toggle.
.make/go.mk Adjusts debug docker build flags (removes -trimpath).
.gitignore Adds additional ignore entries (but introduces duplication).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread services/graph/pkg/service/v0/spacetemplates.go
Comment thread services/graph/pkg/service/v0/service.go Outdated
Comment on lines +404 to +406
m.Route("/vault/graph", func(r chi.Router) {
r.Use(requireMFA)
r.Use(graphm.VaultModeMiddleware())
Copy link
Copy Markdown
Member

@jvillafanez jvillafanez left a comment

Choose a reason for hiding this comment

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

The collaboration service is using "custom" HTTP clients in some places such as

so we'll need to check those. Those clients won't send the MFA header, which might cause problems (we need to fix those). GRPC services should be covered with the AppendToOutgoingContext calls.
Note that the discovery part will connect to the office app, which is an external system, so we don't need to propagate data there.

Comment thread services/graph/pkg/middleware/mfa.go
Comment thread services/collaboration/pkg/connector/httpadapter.go Outdated
@jvillafanez
Copy link
Copy Markdown
Member

Before I forget, a couple of things to investigate:

ocis-1                 | {"level":"info","service":"auth-service","pkg":"rgrpc","traceid":"2c619983da969c56a58b7b0f2f1ff57c","time":"2026-04-28T16:49:51Z","line":"/home/juan/src/ocis/ocis/vendor/github.com/owncloud/reva/v2/internal/grpc/services/authprovider/authprovider.go:146","message":"user idp:\"none\" opaque_id:\"13dbb844-e104-4ddf-ad53-aeb8af6b44ea\" type:USER_TYPE_SERVICE authenticated"}
storage-users-vault-1  | {"level":"warn","service":"storage-users-vault","pkg":"rgrpc","traceid":"485cc04473abe3df886c51f92b169968","user_id":"13dbb844-e104-4ddf-ad53-aeb8af6b44ea","mfa_values":[],"time":"2026-04-28T16:49:51Z","line":"/home/juan/src/ocis/ocis/vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/auth/auth.go:162","message":"MFA is required"}
storage-users-vault-1  | {"level":"debug","service":"storage-users-vault","pkg":"rgrpc","traceid":"485cc04473abe3df886c51f92b169968","user-agent":"grpc-go/1.80.0","from":"tcp://172.26.0.5:42394","uri":"/cs3.storage.provider.v1beta1.ProviderAPI/Stat","start":"28/Apr/2026:16:49:51 +0000","end":"28/Apr/2026:16:49:51 +0000","time_ns":147804,"code":"OK","time":"2026-04-28T16:49:51Z","line":"/home/juan/src/ocis/ocis/vendor/github.com/owncloud/reva/v2/internal/grpc/interceptors/log/log.go:69","message":"unary"}
ocis-1                 | {"level":"error","service":"search","error":"error: permission denied: MFA required to access vault storage","time":"2026-04-28T16:49:51Z","line":"/home/juan/src/ocis/ocis/services/search/pkg/search/service.go:454","message":"error walking the tree"}
ocis-1                 | {"level":"error","service":"search","error":"error: permission denied: MFA required to access vault storage","spaceID":{"opaque_id":"1a01c2c4-4309-4483-a845-842fd56d8622$bca1b64f-c5c5-43d8-93ac-02d000241c3a"},"time":"2026-04-28T16:49:51Z","line":"/home/juan/src/ocis/ocis/services/search/pkg/search/events.go:56","message":"error while indexing a space"}

The "thumbnails", "webdav", "search" and "collaboration" services use custom HTTP clients to make some requests to other oCIS services. These clients won't send the MFA header automatically, so we need to add the header manually.
I assume the MFA error above is caused by the search client, although we'll need to confirm.
The collaboration service should be fixed already (I haven't found anything wrong accessing to the vault from collabora). The other services need to be checked.

@jvillafanez
Copy link
Copy Markdown
Member

Regarding the errors shown in #12108 (comment) I'm not sure if that will be a blocking issue, but I think it's out of scope for the time being.
I'm pretty sure the problem is caused by the data propagation in the events. There are several problems to tackle first:

  • Context handling in the events: Right now, there is no context associated to the event. When the event starts being processed, a new context should be created. Additional contexts might be derived from it if needed.
  • Data propagation between event publisher and consumer: This is also missing. Outside if the event itself and the data it carries, no other data is propagated. In particular, contextual data such as the MFA isn't propagated (this seems the cause of the error above). Note that without a context to set the propagated data into, it's going to be problematic for the data to reach the relevant components.
  • Tracing information: the tracing information seems to be sent with the event, but the consumer might not use it or link it with the publisher. This leads to inconsistent data shown in tools like jaeger. Since the consumer trace isn't linked, it will appear as a new trace with a weird starting point (like the reva gateway).

In addition to the points above, the search service will need additional refactoring to accommodate context usage in some parts of its code, so the information can be propagated from the event processor through the context.

For the short term, the plan is to sort this out for the search service (in a different PR). Once it's done and merged, we should be able to fix the issue above. Other services might still need similar changes.

httpReq.Header.Add("X-Access-Token", wopiContext.AccessToken)
}
if wopiContext.HasMFA {
httpReq.Header.Add(mfa.MFAHeader, "true")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Please use mfa.SetHeader(httpReq, true) instead.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants