Commit Graph

181 Commits

Author SHA1 Message Date
Edouard Vanbelle 00af0e8a89 feat(registraton): add anti enumeration (cannot know if an account already exists)
Important: anti-enumeration is active only if SMTP is defined, welcome email can be used
    otherwise it is a classic registration with ok or conflic if account alrady exists
2026-06-03 00:35:25 +02:00
Edouard Vanbelle 9a49ab44d8 feat(passwordless): pass3: passwordless account (via emailed magic-link)
Backend
  - RegisterDto — username and password both become Option<String> with #[serde(default)] so JSON can omit them entirely.
  - AuthApplicationService::register — username uniqueness check skipped when None (multiple NULLs OK under the UNIQUE index); password hashing skipped when None; User::new called with the actual Options instead of forcing Some(...).
  - auth_handler::register — branches on dto.password.is_none(). With password → existing 201 + UserDto. Without → triggers MagicLinkInviteService::send_login_link(&email) best-effort, then returns 200 + {"message": "Check your email…"}. The
  OIDC-mode-disables-password-registration gate now only fires for the password path (email-only signup is still allowed even in OIDC-only mode, because it doesn't store a password).
  - magic_link_handler::redirect_target — new 3-way decision tree:
    - Resource target (folder invitation) → /#/files/folder/{id} (existing)
    - NULL resource + is_external = false → /#/files (the welcome path for new internal users — they have a home folder)
    - NULL resource + is_external = true → /#/sharedwithme (the existing external-user landing)

  Tests
  - New tests/api/registration.hurl with 9 requests covering: classic (with-password) register → 201 + UserDto, email-only register → 200 + uniform message + welcome magic-link captured, redemption → 302 to /#/files + cookies set, profile read → username
  absent + is_external: false, resend magic-link works (eligible while passwordless), cleanup deletes both new users.
  - Wired into tests/api/run.sh right after auth_login.hurl.

  Plan additions
  - auth-simplification.md gained PR 22 at the bottom of the PR sequence — device-bound magic-link redemption via challenge cookie + asymmetric TTLs (login: 10 min, invitation: 24 h). Full design recap, schema migration, config knobs
  (OXICLOUD_MAGIC_LINK_LOGIN_TTL_MINUTES / _INVITE_TTL_HOURS), and Hurl coverage outline are in the plan. Slots in before PR 21's docs so the architecture page describes the final state from the start.

  Checks — cargo fmt, cargo clippy --all-features --all-targets -- -D warnings, cargo test --lib (297 passed), biome, stylelint, tsc, full Hurl suite (16 files) all green.
2026-06-03 00:35:25 +02:00
Edouard Vanbelle ac24a0eda1 feat(username|email): pass2: accept login via email orusername
- login via (username or email) + password
    - hurl test to cover the feature
2026-06-03 00:35:25 +02:00
Edouard Vanbelle d57a50d056 feat(username|email): pass1: normalize auth.user data
username: now optional, if defined 2..64 chars
    password: now optional (no mode __NO_PASSWORD...__)
    oidc: now optional

    important: if need Nextcloud, username must be defined
2026-06-03 00:35:25 +02:00
Edouard Vanbelle 6d70a7000e security(external_user): protect unnecessary route access to external users 2026-06-03 00:32:00 +02:00
Edouard Vanbelle 21c06da700 feat(magic-links): add rate limiting + archirecture documentation 2026-06-03 00:32:00 +02:00
Edouard Vanbelle 64d081ad0b feat(external): permit login via email (magic link) 2026-06-03 00:31:59 +02:00
Edouard Vanbelle 6763f2ca9e fix(/api/users): external users can only query themself and their granters 2026-06-03 00:31:59 +02:00
Edouard Vanbelle ec72374651 feat(api): can grant external user (via email)
- add possibility to grant an external user.
    - route /api/users/{id} added (rate limited for security)
    - security: start route limitation for external users
        ex: they must not browse /api/users/{id} nor addressbook
2026-06-03 00:31:59 +02:00
Edouard Vanbelle 03f63ad103 feat(external users): email sanity + mock SMTP
- SMTP has a mock to enable end to end test and validate the whole path
     (via OXICLOUD_SMTP_MOCK)
    - add email normalisation ( including punicode)
    - api to share to external user
2026-06-03 00:31:59 +02:00
Edouard Vanbelle 96097431c3 test(api): upgrade tests to new routes (don't use deprecated routes anymore) 2026-05-31 20:58:01 +02:00
Edouard Vanbelle 09985f8a95 feat(group): 1st implementation of Groups
this implements first version (manageable only by admin right now)

    routes:

        GET /api/groups
        List subject groups (paginated). Admin-only.

        POST /api/groups
        Create a new ReBAC subject group. Admin-only. The name must match the RFC 5321 local-part shape and be globally unique (case-insensitive).

        GET /api/groups/search
        Search non-virtual groups by name substring. Authenticated only (no admin role required) — backs the share-dialog recipient autocomplete.

        GET /api/groups/{id}
        Fetch a single group's details. Admin-only.

        DELETE /api/groups/{id}
        Delete a group. Cascades to `subject_group_members` (FK) and to `access_grants` rows referencing this group as a subject. Admin-only.

        PATCH /api/groups/{id}
        Update a group's metadata. Admin-only. v1 only persists name renames.

        GET /api/groups/{id}/effective-members
        List every user transitively reached through this group (members of members of members, etc.). Used by admin / audit tooling. Admin-only.

        GET /api/groups/{id}/members
        List the *direct* members of a group (one level only). Admin-only.

        POST /api/groups/{id}/members
        Add a member to a group. Exactly one of `user_id` / `group_id` must be provided. Adding a group-member runs a write-time cycle check and a nesting-depth check (max 8). Admin-only.

        DELETE /api/groups/{id}/members/group/{gid}
        Remove a nested group-member from a group. Admin-only.

        DELETE /api/groups/{id}/members/user/{uid}
        Remove a user-member from a group. Admin-only.

fix hurl

groups

round

groups
2026-05-31 20:57:45 +02:00
Edouard Vanbelle ea83891a61 feat(trash): move trash API to normalized version (with cursor, orderBy) + normalize Trash section to existing components
normalize also component to format badges (expiry, role, etc)
2026-05-30 00:49:32 +02:00
Edouard Vanbelle 5a43b14e29 test(api): move deprecated call to new one 2026-05-29 13:13:23 +02:00
Edouard Vanbelle 12466d4b83 feat(roles): simplify roles to only Viewer, Editor, Admin
┌────────┬──────┬─────────┬────────┬────────┬───────┬────────┐
  │  Role  │ read │ comment │ create │ update │ share │ delete │
  ├────────┼──────┼─────────┼────────┼────────┼───────┼────────┤
  │ viewer │  ✓   │         │        │        │       │        │
  ├────────┼──────┼─────────┼────────┼────────┼───────┼────────┤
  │ editor │  ✓   │    ✓    │   ✓    │   ✓    │       │        │
  ├────────┼──────┼─────────┼────────┼────────┼───────┼────────┤
  │ admin  │  ✓   │    ✓    │   ✓    │   ✓    │   ✓   │   ✓    │
  └────────┴──────┴─────────┴────────┴────────┴───────┴────────┘
2026-05-25 22:47:36 +02:00
Edouard Vanbelle dd68d783e0 fix(authz): permit policiy: a user with Delete permission can delete a file/folder. Only the owner can permanently delete or restore a trashed item 2026-05-21 22:45:49 +02:00
Edouard Vanbelle a1c21ce446 refactor(authz): permet require_permission() as has_permission(), more explicit 2026-05-21 21:50:42 +02:00
Edouard Vanbelle cb35775f77 fix(dedub): correct ref count on hashes, many thanks to you api tests... 2026-05-21 21:12:38 +02:00
Edouard Vanbelle eb95567a7d feat(authz): test & cover batch cases
┌────────────────────────────────┬─────────────────────────────────┬───────────────────────┬─────────────────┬──────────────────────────────┐
  │            Endpoint            │        Phase 3A no-grant        │    Phase 3B Viewer    │ Phase 3C Editor │        Phase 3D Admin        │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/files/get      │ 400 (all failed)                │ 200 (2 successful)    │ —               │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/files/move     │ 400                             │ 400 (no Update)       │ 200             │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/files/copy     │ 400                             │ —                     │ 200             │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/files/delete   │ 400                             │ 400                   │ 400 (no Delete) │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/folders/get    │ 400                             │ 200                   │ —               │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/folders/create │ 400                             │ —                     │ 201             │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/folders/move   │ 400                             │ —                     │ 200             │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/folders/copy   │ 400                             │ —                     │ 200             │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/folders/delete │ 400                             │ —                     │ 400 (no Delete) │ 200                          │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/trash          │ 400                             │ —                     │ —               │ 400 (owner-only, documented) │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ POST /api/batch/download       │ 404 (NotFound)                  │ 200 + application/zip │ —               │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ GET /api/batch/download?...    │ 404                             │ 200 + zip             │ —               │ —                            │
  ├────────────────────────────────┼─────────────────────────────────┼───────────────────────┼─────────────────┼──────────────────────────────┤
  │ Phase 3E lifecycle cleanup     │ grants table empty after delete │                       │                 │                              │
  └────────────────────────────────┴─────────────────────────────────┴───────────────────────┴─────────────────┴──────────────────────────────┘
2026-05-21 20:30:58 +02:00
Edouard Vanbelle a53c09f361 feat(authz): covert and test chunked upload with permissions 2026-05-21 20:30:58 +02:00
Edouard Vanbelle bd1b17b589 test(grants): full coverate of /api/files and /api/folders 2026-05-21 20:30:58 +02:00
Edouard Vanbelle cba9be8c21 feat(rebac): first pass 2026-05-20 22:56:00 +02:00
Edouard Vanbelle ac42a6d3cc test(api): check right management for folder creation and folder move + check also webdsav MKCOL protection
│ Steps 1-6 │ Setup: admin's resources + create bob + bob's home folder                                                           │
  │ Step 7    │ REST: bob can't create a folder inside admin's home → 404                                                           │
  │ Step 8    │ REST: bob can't create inside admin's private folder → 404                                                          │
  │ Step 9    │ REST: parent_id: null auto-resolves to bob's home (documents the convenience)                                       │
  │ Step 10   │ REST: positive control — bob creates in his own home → 201                                                          │
  │ Step 12   │ REST: bob can't move his file into admin's folder → 404                                                             │
  │ Step 13   │ REST: bob moves file to root (null) → 200 (legitimate root state)                                                   │
  │ Step 14   │ REST: bob can't read admin's file → 404                                                                             │
  │ Step 15   │ REST: admin's tree integrity preserved                                                                              │
  │ Step 16   │ WebDAV: path-prefix isolation rewrites cross-user paths into caller's tree                                          │
  │ Step 17   │ WebDAV: positive control MKCOL in bob's own tree → 201                                                              │
  │ Step 18   │ WebDAV: bob's home contains the rewritten "My Folder - admin" sub-folder, proving the isolation rerouted the attack │
  │ Step 19   │ WebDAV: admin's tree never sees bob's WebDAV traffic                                                                │
2026-05-20 13:04:15 +02:00
Edouard Vanbelle 78cb37b311 feat: check thumbnail cleanup on files deletion + correct ref counter 2026-05-13 15:55:38 +02:00
Edouard Vanbelle 07d1f18bf4 test API thumbnail fix on updates 2026-05-13 10:17:47 +02:00
Edouard Vanbelle e953236589 test(api): add functional test on copy_folder 2026-05-12 00:28:42 +02:00
Edouard Vanbelle a0aa9c5cd1 ci: trigger test on /tests change 2026-05-11 20:00:22 +02:00
Edouard Vanbelle 5df4075f7f test(api): add API test coverage on files, folders, favorites, recent, trash 2026-05-11 19:31:12 +02:00
Edouard Vanbelle d8625b9f39 ci: merge end-to-end workflow into ci, permits to reduce amount of built 2026-05-11 12:10:15 +02:00
Edouard Vanbelle cc14ec53ee ci(api-tests): add API test to CI 2026-05-11 10:53:59 +02:00
Edouard Vanbelle 8ab537797a test(api): add API test on contacts
next will be to add CI on it
2026-05-11 00:53:29 +02:00