# How Organizations, Collections, and Groups Work in Vaultwarden

> Understand Vaultwarden's organizations collections and groups. Learn how memberships and shared permissions create a hierarchical access model for secure password management.

- Repository: [Daniel García/vaultwarden](https://github.com/dani-garcia/vaultwarden)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Vaultwarden implements a hierarchical permission model where Organizations contain Collections of passwords, Groups aggregate users with shared permissions, and Memberships define individual access levels through a combination of direct assignments and group-based rights.**

Vaultwarden, the open-source Bitwarden server implementation written in Rust, provides enterprise-grade password sharing through a sophisticated access control system. Understanding how **organizations, collections, and groups work in Vaultwarden** is essential for administrators managing team credentials. This article examines the data models in `src/db/models/` and the API implementation to explain the permission flow from organization creation to individual cipher access.

## The Core Data Model

Vaultwarden’s permission system rests on four interconnected entities defined in the database layer: **Organization**, **Membership**, **Collection**, and **Group**.

### Organization Structure

An **Organization** represents the top-level container that groups members, collections, and groups. According to the source code in [`src/db/models/organization.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/organization.rs) (lines 26-30), the struct is defined as:

```rust
pub struct Organization {
    pub uuid: OrganizationId,
    pub name: String,
    pub billing_email: String,
    pub private_key: Option<String>,
    pub public_key: Option<String>,
}

```

When an organization is created via the `create_organization` endpoint in [`src/api/core/organizations.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/organizations.rs), the system automatically provisions a default **owner** `Membership` and a default **collection** (lines 90-93 and 96-98). This ensures the creating user immediately has full administrative access to manage the organization’s vault.

### Membership Types and Status

**Memberships** link individual users to organizations through the `users_organizations` table. Each membership carries a **type** defined by the `MembershipType` enum:

- **Owner**: Full administrative control, including billing and deletion rights
- **Admin**: Management access without ownership-specific functions
- **User**: Standard member with limited collection access
- **Manager**: Intermediate role with user management capabilities for assigned collections

A membership must also have a status of **Confirmed** before it grants any access rights. The type determines whether the member receives `full access` (owner/admin) or operates under limited collection/group constraints.

### Collections as Permission Containers

**Collections** function as the primary unit of data sharing, containing ciphers (password entries) within an organization. Defined in [`src/db/models/collection.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/collection.rs) (lines 19-23):

```rust
pub struct Collection {
    pub uuid: CollectionId,
    pub org_uuid: OrganizationId,
    pub name: String,
    pub external_id: Option<String>,
}

```

Permissions are stored in two join tables:
- **`users_collections`** → Links a user directly to a collection with `read_only`, `hide_passwords`, and `manage` boolean flags
- **`collections_groups`** → Links a **Group** to a collection with identical permission flags

### Groups for Scalable Access Control

**Groups** provide a mechanism to grant identical collection permissions to multiple members simultaneously. The `Group` struct in [`src/db/models/group.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/group.rs) (lines 16-22) includes a critical `access_all` field:

```rust
pub struct Group {
    pub uuid: GroupId,
    pub organizations_uuid: OrganizationId,
    pub name: String,
    pub access_all: bool,
    pub external_id: Option<String>,
    pub creation_date: NaiveDateTime,
    pub revision_date: NaiveDateTime,
}

```

When `access_all` is set to `true`, every member assigned to that group receives full access to any collection linked to the organization. Group membership is tracked in the `groups_users` table, which associates `GroupId` values with `MembershipId` values.

## Permission Evaluation Logic

Vaultwarden centralizes access control in the `can_access_collection` method within [`src/db/models/collection.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/collection.rs) (lines 40-46). This function implements a cascading permission check:

```rust
pub async fn can_access_collection(member: &Membership, col_id: &CollectionId, conn: &DbConn) -> bool {
    member.has_status(MembershipStatus::Confirmed)
        && (member.has_full_access()
            || CollectionUser::has_access_to_collection_by_user(col_id, &member.user_uuid, conn).await
            || (CONFIG.org_groups_enabled()
                && (GroupUser::has_full_access_by_member(&member.org_uuid, &member.uuid, conn).await
                    || GroupUser::has_access_to_collection_by_member(col_id, &member.uuid, conn).await)))
}

```

The evaluation follows this hierarchy:
1. Verify the member’s status is **Confirmed**
2. Check if the member has **full access** (owner or admin)
3. Check for direct **CollectionUser** rights in `users_collections`
4. If groups are enabled, verify **group-based** full access via `GroupUser::has_full_access_by_member` or specific collection access via `GroupUser::has_access_to_collection_by_member`

The `has_full_access_by_member` function in [`src/db/models/group.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/group.rs) (lines 33-49) queries the `groups_users` table joined with `groups` to count rows where `access_all` equals `true` for the given member:

```rust
pub async fn has_full_access_by_member(
    org_uuid: &OrganizationId,
    member_uuid: &MembershipId,
    conn: &DbConn,
) -> bool {
    db_run! { conn: {
        groups_users::table
            .inner_join(groups::table.on(groups::uuid.eq(groups_users::groups_uuid)))
            .filter(groups::organizations_uuid.eq(org_uuid))
            .filter(groups::access_all.eq(true))
            .filter(groups_users::users_organizations_uuid.eq(member_uuid))
            .count()
            .first::<i64>(conn)
            .unwrap_or(0) != 0
    }}
}

```

## Configuration and Feature Flags

Vaultwarden uses compile-time and runtime configuration to toggle organization features. The critical flag `CONFIG.org_groups_enabled()` determines whether group-related permission checks execute in the `can_access_collection` logic. Additional flags like `CONFIG.org_events_enabled()`, `useCollections`, and `useGroups` are reflected in the JSON payload returned by `Organization::to_json` (lines 96-98 in [`organization.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/organization.rs)), allowing clients to adapt their UI based on server capabilities.

## API Endpoints for Managing Access

All CRUD operations for the permission hierarchy are implemented in [`src/api/core/organizations.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/organizations.rs). Key endpoints include:

- **`POST /organizations`** → Creates an organization, its owner membership, and a default collection
- **`GET /organizations/:id/collections`** → Lists collections belonging to an organization
- **`POST /organizations/:id/collections`** → Creates a new collection
- **`GET /organizations/:id/groups`** → Lists available groups
- **`POST /organizations/:id/groups`** → Creates a new group
- **`POST /organizations/:id/collections/:cid/groups`** → Assigns a collection to a group (creates a `CollectionGroup` row)
- **`POST /organizations/:id/collections/:cid/users`** → Assigns a collection directly to a member (creates a `CollectionUser` row)

These routes wire the data-model logic to the HTTP layer, ensuring that `can_access_collection` and related checks enforce permissions consistently across the API surface.

## Practical Implementation Examples

### Creating an Organization with Collections and Groups

The following Rust example demonstrates programmatic creation of the full hierarchy using Vaultwarden’s internal models:

```rust
use vaultwarden::db::models::{Organization, Collection, Group, Membership, MembershipType};
use vaultwarden::CONFIG;

// Create the organization
let org = Organization::new(
    "Acme Corp".into(),
    "admin@acme.com",
    None,                // private_key
    None,                // public_key
);

// Create the owner membership
let mut owner = Membership::new(user_uuid, org.uuid.clone(), None);
owner.atype = MembershipType::Owner as i32;
owner.status = vaultwarden::db::models::MembershipStatus::Confirmed as i32;
owner.access_all = true;
owner.akey = "random-key".into();

// Create a default collection
let collection = Collection::new(org.uuid.clone(), "Passwords".into(), None);

// Create a group with full access
let group = Group::new(org.uuid.clone(), "Admins".into(), true, None);

// Persist to database
org.save(&conn).await?;
owner.save(&conn).await?;
collection.save(&conn).await?;
group.save(&conn).await?;

```

### Assigning User-Specific Collection Permissions

To grant a specific member manage rights to a collection via the REST API:

```http
POST /organizations/{org_id}/collections/{collection_id}/users
Content-Type: application/json

{
  "id": "<membership-id>",
  "readOnly": false,
  "hidePasswords": false,
  "manage": true
}

```

The handler parses this payload into `CollectionMembershipData` (defined in [`src/api/core/organizations.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/organizations.rs) lines 45-50) and creates a `CollectionUser` record, which subsequent calls to `can_access_collection` evaluate.

### Granting Group-Based Collection Access

To link a collection to a group with specific permissions:

```http
POST /organizations/{org_id}/collections/{collection_id}/groups
Content-Type: application/json

{
  "id": "<group-id>",
  "readOnly": true,
  "hidePasswords": true,
  "manage": false
}

```

This creates a `CollectionGroup` entry. When any member of the target group attempts to access the collection, `GroupUser::has_access_to_collection_by_member` validates their rights through the membership linkage in `groups_users`.

### Checking Access Programmatically

Within protected endpoints, enforce permissions using the centralized check:

```rust
let member = Membership::find_by_uuid(&member_uuid, &conn).await.unwrap();
let can_view = Collection::can_access_collection(&member, &collection_uuid, &conn).await;

if !can_view {
    return Err(ApiError::Forbidden);
}

```

## Summary

- **Organizations** serve as the root container, automatically creating an owner membership and default collection upon instantiation in [`src/db/models/organization.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/organization.rs).
- **Collections** contain encrypted items and act as the primary permission boundary, with access controlled through `users_collections` and `collections_groups` join tables.
- **Groups** enable scalable permission management via the `access_all` flag and membership tracking in `groups_users`, evaluated through `GroupUser::has_full_access_by_member`.
- **Access evaluation** follows a strict hierarchy in `Collection::can_access_collection`, confirming membership status before checking full access, direct collection rights, or group-derived permissions.
- **Feature toggles** like `CONFIG.org_groups_enabled()` conditionally enable group logic, allowing administrators to disable complex group features if unnecessary.

## Frequently Asked Questions

### What is the difference between a Collection and a Group in Vaultwarden?

A **Collection** is a container for passwords (ciphers) that defines the actual data boundary for sharing, while a **Group** is a logical aggregation of users designed to simplify permission management. Collections store the actual encrypted vault items, whereas groups only store references to members and collection permissions. A single group can be granted access to multiple collections, and multiple groups can access the same collection, creating flexible role-based access control.

### How does Vaultwarden determine if a user can access a specific password?

Vaultwarden executes the `can_access_collection` function in [`src/db/models/collection.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/collection.rs), which requires the user’s membership status to be **Confirmed**. The function then checks four conditions in order: owner/admin full access, direct user-to-collection rights via `CollectionUser`, group-based full access via `access_all`, or specific group-to-collection rights via `CollectionGroup`. If any condition passes, access is granted.

### What are the different membership types in a Vaultwarden organization?

Vaultwarden defines four membership types in the `MembershipType` enum: **Owner** (full control including billing and deletion), **Admin** (management without ownership-specific functions), **Manager** (user management for assigned collections), and **User** (limited access based on explicit collection or group assignments). Only Owners and Admins receive automatic full access to all organization collections unless explicitly restricted.

### Can groups be disabled in Vaultwarden?

Yes. Groups are controlled by the `CONFIG.org_groups_enabled()` configuration flag. When disabled, the permission evaluation logic in `can_access_collection` skips all group-related checks, meaning only direct user-to-collection assignments and owner/admin full access determine permissions. This simplifies the permission model for smaller organizations that do not require complex group-based access control.