Skip to content

Add organization and collection support - #379

Open
leoc wants to merge 6 commits into
doy:mainfrom
leoc:org-collection-support
Open

leoc wants to merge 6 commits into
doy:mainfrom
leoc:org-collection-support

Conversation

@leoc

@leoc leoc commented Sep 16, 2026

Copy link
Copy Markdown

Fixes #102

Summary

This implements organization/collection support as requested in #102:

  • rbw sync now stores the account's organizations and collections in the local database, and each entry's collection memberships.
  • rbw list --fields name,org,collection (and search --fields ...) show which organization and collections an entry belongs to; rbw get --raw includes them in the JSON output.
  • --org <name> and --collection <name> filter/disambiguate lookups on get, code, edit, remove, history, and search — useful when the same entry name exists both personally and in an organization.
  • rbw add and rbw generate accept --org <name> --collection <name> to create entries directly in an organization (POST /ciphers/create), encrypted with the organization key. --collection is repeatable; the two flags require each other since Bitwarden requires org items to be in at least one collection.
  • rbw edit --set-collection <name> replaces the set of collections an existing org entry belongs to (PUT /ciphers/{id}/collections), allowing moves between collections of the same organization. This doesn't touch the entry's contents and doesn't open an editor — no re-encryption is needed since all collections of an organization share the org key.

Design notes

  • Follows the folder pattern where it fits (sync parsing with rename/alias for server casing variance, --fields values, filter flags), with two deliberate differences: collections are many-to-many, so they're stored as lookup tables (Db.organizations, Db.collections) plus Entry.collection_ids instead of denormalized per entry — each collection name is decrypted at most once per command; and collection names are encrypted with the organization key, unlike folder names (org names arrive in plaintext in the sync profile).
  • No agent protocol change: names are resolved client-side from the db; the existing Decrypt/Encrypt actions already carry org_id.
  • Backward compatible: new db fields are #[serde(default)], so existing db files load fine, and older rbw versions simply ignore the new fields.
  • Defensive parsing: the sync collections array, per-cipher collectionIds, and org name are all optional, so servers that omit them keep working; a collection name that fails to decrypt degrades to showing its id with a warning instead of failing the command.
  • --org/--collection act as a hard pre-filter before the existing exact/substring disambiguation in find_entry_raw, which stays untouched. Filter names match exactly (case-insensitively with -i); a miss lists the known names in the error.

Out of scope (possible follow-ups)

  • Moving entries into/out of an organization (PUT /ciphers/{id}/share; requires re-encrypting every field with the org key).
  • Creating or managing collections themselves (org-admin operations), so --collection intentionally has no create-if-missing behavior, unlike --folder.

Testing

  • Unit tests: sync parsing (camelCase and PascalCase, with and without the optional fields), db backward compatibility with pre-change files, filter resolution/matching semantics, uuid lookups honoring the filters, and clap arg relationships (including a new Opt::command().debug_assert()).
  • Manually tested end-to-end against a local vaultwarden instance: sync populating the db; list/search/get display and filtering; disambiguating same-named personal/org entries; creating entries via add/generate with --org/--collection and verifying them in the web vault; edit on org entries preserving collection membership; --set-collection moving entries between collections; error paths (unknown names, org item without collection, --set-collection on a personal entry).
  • Daily-driving this build against my own up-to-date vaultwarden server with no issues so far, including a custom rofi launcher built on rbw list --fields name,org,collection that groups entries by organization and collection.

@leoc
leoc marked this pull request as draft September 16, 2026 13:35
@leoc

leoc commented Sep 16, 2026

Copy link
Copy Markdown
Author

I will check the linter issues. ✌️

these are pre-existing lints that newer clippy versions have started
flagging (they fail the lint CI job on main as well), and are unrelated
to the organization and collection support in this branch:

- collapse two match expressions into ? in actions::unlock
- use a match guard instead of a nested if in classify_login_error
- drop a redundant reference in a format! argument in dirs

cargo clippy --all-targets --all-features -- -Dwarnings is clean with
this applied.
@leoc

leoc commented Sep 16, 2026

Copy link
Copy Markdown
Author

heads up on the red CI, since it looks worse than it is:

the lint job gets through clippy and cargo fmt now and stops at cargo deny check. those are RUSTSEC advisories in transitive deps (rsa → num-bigint-dig → spin, plus quick-xml, bytes, anyhow). nothing to do with this branch, i didn't touch Cargo.toml or Cargo.lock. main has the same ones, it just never gets that far because clippy fails first.

that clippy failure is also why there's a fix clippy lints in existing code commit in here. newer clippy flags a few things in unlock(), classify_login_error() and dirs.rs, and -Dwarnings turns them into errors. it's unrelated to the feature, so drop it if you'd rather keep this PR focused and i'll send it separately.

@leoc
leoc marked this pull request as ready for review September 16, 2026 19:56
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.

Support for organizations and collections when adding / getting a password

1 participant