Commit Graph

4635 Commits

Author SHA1 Message Date
Pascal Bleser
bfcf08688c groupware: swagger API documentation improvements
* add more documentation for properties

 * fixes after a bit of trial-and-error with go-swagger

 * fix email filter marshalling when there are no search criteria

 * introduce an apidoc.yml that contains Swagger data and is merged when
   generating the swagger.yml from sources
2026-07-09 14:15:49 +02:00
Pascal Bleser
35fd72eda6 Groupware improvements
* ensure that all the jmap responses contain the SessionState

 * implement missing errors that were marked as TODO

 * moved common functions from pkg/jmap and pkg/services/groupware to
   pkg/log and pkg/structs to commonalize them across both source trees

 * implement error handling for SetError occurences

 * Email: replace anonymous map[string]bool for mailbox rights with a
   MailboxRights struct, as the keys are well-defined, which allows for
   properly documenting them

 * introduce ObjectType as an "enum"

 * fix JSON marshalling and unmarshalling of EmailBodyStructure

 * move the swagger documentation structs from groupware_api.go to
   groupware_docs.go

 * fix: change verb for /groupware/accounts/*/vacation from POST to PUT
2026-07-09 14:15:49 +02:00
Pascal Bleser
66ebf4d5b9 groupware: minor email searching response improvements + started implementing vacation response setting API 2026-07-09 14:15:49 +02:00
Pascal Bleser
2c9378d0b0 groupware: add identities of all accounts to the index resource 2026-07-09 14:15:49 +02:00
Pascal Bleser
aea0d9afd5 groupware: fix email search, add variant that includes the full emails 2026-07-09 14:15:49 +02:00
Pascal Bleser
1d9d972518 groupware: fix email search, add variant that includes the full emails 2026-07-09 14:15:49 +02:00
Pascal Bleser
ab262aa6bb Groupware: refactor jmap package, implement Email/set, EmailSubmission
* refactor the jmap package to split it into several files as the
   jmap.api.go file was becoming too unwieldy

 * refactor the Groupware handler function response to be a Response
   object, to be more future-proof and avoid adding more and more
   return parameters while handling "no content" response as well

 * more godoc for the JMAP model

 * add Email creation, updating, deleting (Email/set,
   EmailSubmission/set)

 * add endpoints
   - POST /accounts/{accountid}/messages
   - PATCH|PUT /accounts/{accountid}/messages/{messageid}
   - DELETE /accounts/{accountid}/messages/{messageid}
2026-07-09 14:15:49 +02:00
Pascal Bleser
9c42f2303a groupware: implement message search with snippets 2026-07-09 14:15:49 +02:00
Pascal Bleser
624ac13182 groupware: blob streaming (upload and download) 2026-07-09 14:15:49 +02:00
Pascal Bleser
96890e315d groupware: more JMAP operations implementation 2026-07-09 14:15:49 +02:00
Pascal Bleser
4286cec023 groupware: further implementation and improvements 2026-07-09 14:15:49 +02:00
Pascal Bleser
7f755300cd refactored the Session object, refactored the services/groupware directory, and started Swagger documentation implementation 2026-07-09 14:15:49 +02:00
Pascal Bleser
5862bebbcc groupware: refactoring the API mechanisms 2026-07-09 14:15:49 +02:00
Pascal Bleser
127cd7406c groupware: implement JSON:API's error response format, with a revamped error handling in jmap and services/groupware 2026-07-09 14:15:49 +02:00
Pascal Bleser
f0242898bf Refactor groupware service after ADR decision on the Groupware API
* after having decided that the Groupware API should be a standalone
   independent custom REST API that is using JMAP data models as much as
   possible,
 * removed Groupware APIs from the Graph service
 * moved Groupware implementation to the Groupware service, and
   refactored a few things accordingly
2026-07-09 14:15:49 +02:00
Pascal Bleser
4a138b8082 Groupware and jmap: cleanup and API documentation 2026-07-09 14:15:49 +02:00
Pascal Bleser
181dd14335 groupware: remove unneeded messages.go that was a remainder from an earlier implementation attempt, which also fixes compilation issues due to changes in main 2026-07-09 14:15:49 +02:00
Pascal Bleser
aaf4be4546 Groupware improvements: refactoring, k6 tests
* refactored the models to be strongly typed with structs and mapstruct
   to decompose the dynamic parts of the JMAP payloads

 * externalized large JSON strings for tests into .json files under
   testdata/

 * added a couple of fantasy Graph groupware APIs to explore further
   options

 * added k6 scripts to test those graph/me/messages APIs, with a setup
   program to set up users in LDAP, fill their IMAP inbox, activate them
   in Stalwart, cleaning things up, etc...
2026-07-09 14:15:49 +02:00
Pascal Bleser
3b43f51592 groupware: removed debugging logs 2026-07-09 14:15:49 +02:00
Pascal Bleser
68eaafece8 auth-api: fix: was missing newly introduced metrics 2026-07-09 14:15:49 +02:00
Pascal Bleser
8cf5aa3e4c groupware and jmap improvements and refactoring 2026-07-09 14:15:48 +02:00
Pascal Bleser
521d2a0820 Introduce a the auth-api service
* primitive implementation to demonstrate how it could work, still to
   be considered WIP at best

 * add new dependency: MicahParks/jwkset and MicahParks/keyfunc to
   retrieve the JWK set from KeyCloak to verify the signature of the
   JWTs sent as part of Bearer authentication in the /auth API

 * (minor) opencloud/.../service.go: clean up a logging statement that
   was introduced earlier to hunt down why the auth-api service was not
   being started
2026-07-09 14:15:48 +02:00
Pascal Bleser
9076c8449f add an auth-api service to make an exemplary implementation of an external authentication API for third party services such as Stalwart 2026-07-09 14:15:48 +02:00
Pascal Bleser
d4cf02b17d move services/groupware/pkg/jmap to pkg/jmap 2026-07-09 14:15:48 +02:00
Pascal Bleser
b8b1b02564 WIP: restructure the Jmap client, and implement the /me/messages Graph API endpoint with it 2026-07-09 14:15:48 +02:00
Pascal Bleser
77af5d6e5e add missing routing for /groupware (currently unprotected for testing) 2026-07-09 14:15:48 +02:00
Pascal Bleser
11f45fc83b WIP: initial implementation of the groupware service 2026-07-09 14:15:48 +02:00
Alex
e4faa179b6 feat: add disableSponsorLink web config option (#3093) 2026-07-09 13:33:07 +02:00
Jörn Friedrich Dreyer
415900ab01 chore(idp): update axios
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
2026-07-08 17:09:05 +02:00
Thomas Schweiger
50be30e5db fix: fix typo in proxy service documentation 2026-07-08 10:25:47 +02:00
opencloudeu
95f29d0440 [tx] updated from transifex 2026-07-07 23:16:19 +00:00
opencloudeu
7de9ade8aa [tx] updated from transifex 2026-07-05 23:16:18 +00:00
opencloudeu
f714b22dc4 [tx] updated from transifex 2026-07-03 23:16:24 +00:00
opencloudeu
0fd05d3f5c [tx] updated from transifex 2026-07-02 23:16:19 +00:00
opencloudeu
c2ceb923e4 [tx] updated from transifex 2026-07-01 23:16:28 +00:00
Dominik Schmidt
11449b5943 docs(graph): frame colon paths as "encode segments" instead of a raw-colon edge case
Per review discussion: don't document accidental behavior. The contract is
simply "percent-encode each path segment, as MS Graph requires; encode ':'
as %3A" - OpenCloud allows ':' in names (OneDrive forbids it), so it's one
more character in the mandatory encode set, not a special case.

The parser is unchanged (split on ":/", decode once). This only rewrites the
docs (code comment, acceptance feature, PR description) to state the encode
contract, and drops the tests that relied on a raw, unencoded ':' in a file
name - keeping the "%3A" test that reflects the actual contract.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
b6a4a66aef refactor(graph): split colon paths on ":/" so colons in names work
Review feedback: split the anchor/path and path/suffix on the structural
delimiter ":/" instead of a bare ":". Since the path and suffix always
start with "/", ":/" is the real delimiter, and a ":" *inside* a file or
directory name (which OpenCloud allows but MS Graph/OneDrive forbid) is
kept as part of the path instead of being mistaken for a separator.

A ":" sitting at a segment boundary (e.g. a name ending in ":") stays
ambiguous and must be percent-encoded as "%3A": the split works on the
literal ":/", so "%3A" is never a delimiter and decodes back to ":". This
is now documented in the code and the acceptance feature.

Tests: colon inside a name (with and without a suffix), the Stat path
carrying the colon, and the "%3A" boundary escape.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
95544b65ef docs(graph): drop stale regex references from colon-path comments
The colon-syntax parsing is plain string operations now, but a few
comments still referred to "the previous regex" / "captures". Update them
to match: parseColonPath extracts the itemID from the path (driveID comes
from the route param), and the shape checks stand on their own.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
407bb2bc70 refactor(graph): clearer colon-path anchor parsing, pin second-colon rule
parseColonPath split the anchor asymmetrically (root trimmed the delimiter
colon as part of a literal "/root:" prefix, item cut on it), and the root
branch's comment talked about the driveID which isn't this function's
concern. Split once at the first colon into anchor + rest, then classify
the anchor (exactly "/root", or "/items/{id}" with a single-segment id).
Same behavior, easier to follow.

Also add explicit coverage for the rule that a suffix requires a second
colon: "/root:/Documents/children" (no second colon) is the path
"/Documents/children", not the path "/Documents" with a "/children"
suffix. Two tests pin it end-to-end - it must route to the bare item (not
/items/{id}/children) and Stat must receive the full path.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
4b98022688 refactor(graph): parse colon paths with string ops instead of regexes
Review feedback: the two colon-syntax regexes were hard to read. Since the
middleware is scoped to /drives/{driveID}, the RoutePath it inspects is just
the sub-path (/root:/... or /items/{id}:/...), so a regex buys nothing.

Replace rootColonRe/itemColonRe (and the matchInto/extract helpers) with a
single parseColonPath that uses plain string operations
(HasPrefix/TrimPrefix/TrimSuffix/Cut), one commented step at a time.
Behavior is unchanged; the existing table tests (trailing colon, no suffix,
deep paths, multi-segment suffixes, item-anchored, encoded paths) still pass
and pin it.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
1f51bad9d1 refactor(graph): scope colon-path middleware to /drives/{driveID}
Addresses review feedback: a sub-router middleware can re-route after all,
as long as it rewrites chi.RouteContext().RoutePath instead of r.URL.Path.
Once chi has descended into a sub-router its routeHTTP matches against
rctx.RoutePath and ignores r.URL.Path, which is why the earlier top-level
registration was thought to be required.

Move ResolveGraphPath off the top-level mux.Use and attach it to the
/drives/{driveID} sub-routers (v1.0 + v1beta1). It now reads driveID from
chi.URLParam and matches against RoutePath (the part below the drive), so
the regexes drop the version + drive prefix entirely.

RoutePath carries the percent-encoded wire form (Graph.ServeHTTP sets
RawPath), so the captured driveID/itemID/path are PathUnescape'd exactly
once - reproducing the decoded r.URL.Path a normal handler would see,
without the previous RawPath/EscapedPath workaround. r.URL.Path is now
left untouched; only chi's internal RoutePath is rewritten.

Tests are reworked to drive requests through a chi router mirroring the
production nesting (including the Graph.ServeHTTP RawPath behavior), so
chi's sub-router middleware ordering, RoutePath encoding and param
round-trip are all covered indirectly: a chi upgrade that changes any of
them fails these tests instead of silently breaking colon-path lookups.
Adds explicit coverage for percent-decoding (%20, %252F) and the `$`/`!`
sub-delimiter id round-trip.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
8f51a4318a test(graph): pin chi URLParam round-trip for IDs with ! sub-delim
Codacy flagged the colon-path middleware comment claiming both `$`
and `!` need percent-encoding for chi's tree match, while the
implementation only calls r.URL.RawPath = r.URL.EscapedPath() which
does not encode either character per the suggestion's reading.

In practice EscapedPath does encode `!` as `%21` (only `?` is the
hardcoded escape, `!` is escaped because Go's net/url treats it as
needing encoding outside specific contexts). It leaves `$` literal,
which chi handles fine. chi.URLParam returns the encoded segment
verbatim, and downstream OpenCloud handlers (parseIDParam,
GetDriveAndItemIDParam) already PathUnescape before parsing IDs, so
the round-trip works end-to-end. The acceptance tests on this
branch already exercise this with real `$`/`!`-containing IDs.

Add a focused unit test that mounts the middleware behind chi,
sends a colon URL, and asserts the actual contract:
  - driveID (only `$`): chi.URLParam returns it literal
  - itemID (with `!`):  PathUnescape(chi.URLParam(...)) == original

Update the misleading comment so future readers (and reviewers) see
what the encoding actually does and which downstream contract it
relies on.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
6343f7e861 refactor(graph): drop MS Graph framing from colon-path middleware logs 2026-07-01 17:44:31 +02:00
Dominik Schmidt
7498e8f848 fix(graph): map UNAUTHENTICATED to 401, validate drive/item id pair
Three more Copilot review nits on the colon-syntax middleware:

- CS3 Stat returning UNAUTHENTICATED now surfaces as HTTP 401 (was 500
  via the default-case fallback). Distinct sentinel + handler branch.
- Item-anchored form (/drives/{driveID}/items/{itemID}:/...) now
  validates that driveID's storage/space prefix matches the itemID's,
  short-circuiting to 400 InvalidRequest on mismatch instead of doing
  a CS3 Stat that would only fail at the handler layer.
- ParseID failures on the anchor id now surface as 400 (was 404). In
  practice this branch is defensive — storagespace.ParseID is lenient
  and only errors on empty input, which the regex already filters out
  — but the right semantic for malformed client input is 400, not 404.

Tests: added two new cases (UNAUTHENTICATED → 401, drive/item id
mismatch → 400). The "malformed drive id" case Copilot suggested
isn't reachable in practice given ParseID's leniency, so it's not
covered.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
e49959b833 fix(graph): preserve RawPath workaround + use NopLogger in tests
Two follow-up Copilot review nits on the colon-syntax middleware:

- Don't blank r.URL.RawPath after the rewrite. Graph.ServeHTTP sets
  RawPath = EscapedPath() as a workaround for chi's parameter-binding
  quirks with `$`/`!` in IDs (see go-chi/chi#641). Clearing RawPath
  for rewritten requests negates that workaround. Re-establish the
  same invariant after the rewrite.

- Tests previously used log.NewLogger(), which mutates global zerolog
  state. Switch to log.NopLogger() — order-independent, no global
  side effects.
2026-07-01 17:44:31 +02:00
Dominik Schmidt
f31e50381f fix(graph): address Copilot review feedback on path lookup middleware
- Drop double-decoding of URL path components. r.URL.Path is already
  decoded by net/http; calling url.PathUnescape again would let crafted
  inputs like "%252F" become "/", changing path semantics. Path and
  anchor-id strings now go straight to utils.MakeRelativePath /
  storagespace.ParseID.
- Distinguish operational errors from "not found". Gateway selection
  failure, RPC transport errors, and unexpected CS3 status codes now
  surface as 500 (don't mask outages); only NOT_FOUND and PERMISSION_DENIED
  collapse to 404 (no existence disclosure). Implemented via a sentinel
  errPathNotFound + a 500 fall-through.
- Refactor the two regex-handling branches in rewriteColonPath to share a
  resolution path via a small colonMatch struct + matchInto/extract helpers.
  Removes the SonarCloud duplication finding without changing behavior.
- Update the docstring on ResolveGraphPath to reflect that the rewrite
  preserves the requested API version (/{version}/...) rather than
  hard-coding /v1beta1/...
- Test cleanup: register t.Cleanup to remove the per-subtest selector
  entry from pool's global selectors map after the subtest, and update
  the "unexpected status" case to expect 500 (matches the new error
  semantics).
2026-07-01 17:44:31 +02:00
Dominik Schmidt
0b373817a8 feat(graph): add MS Graph colon-syntax path lookup middleware
Adds a chi middleware that detects MS Graph colon-syntax URLs and rewrites
them to the canonical /items/{itemID}/... form before chi performs route
matching. Existing handlers, routes, and GetDriveAndItemIDParam stay
unchanged.

Two URL shapes are recognized at both /v1.0 and /v1beta1:

  /drives/{driveID}/root:/<path>[:/<suffix>][:]
  /drives/{driveID}/items/{itemID}:/<relativePath>[:/<suffix>][:]

Path resolution runs as the request user via CS3 Stat. Both NOT_FOUND and
PERMISSION_DENIED collapse to a 404 response so existence isn't disclosed
to unauthorized callers. URLs without colon syntax fast-path through with
a single substring check. The original URL is stashed in request context
under OriginalPathContextKey for downstream tracing/logging.

The middleware is registered as a top-level mux.Use so it runs before any
route matching: chi middleware on a sub-router runs after the prefix is
matched but cannot redirect to a different leaf route. Top-level
middleware lets URL rewriting actually re-route the request.

Tests cover regex matching across versions, all rewrite variants
(root/items anchored, with/without suffix, with/without trailing colon,
deep paths), NOT_FOUND -> 404, PERMISSION_DENIED -> 404 (security: no
existence disclosure), and original-URL preservation in request context.
2026-07-01 17:44:31 +02:00
Alex
93fb9cbf6c feat: adjust theme chrome colors and logos (#2599) 2026-07-01 13:20:39 +02:00
Ralf Haferkamp
22485957fe chore: bump version in license-checker clarifications 2026-06-30 18:10:16 +02:00
dependabot[bot]
06c7d6d7af build(deps-dev): bump i18next-conv in /services/idp
Bumps [i18next-conv](https://github.com/i18next/i18next-gettext-converter) from 15.1.2 to 17.0.0.
- [Release notes](https://github.com/i18next/i18next-gettext-converter/releases)
- [Changelog](https://github.com/i18next/i18next-gettext-converter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next-gettext-converter/compare/v15.1.2...v17.0.0)

---
updated-dependencies:
- dependency-name: i18next-conv
  dependency-version: 17.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 18:10:16 +02:00