How Macro Implements Channel-Based Permissions: Architecture and Code
Macro's channel-based permissions system uses a share-permission model built on the ChannelSharePermission struct, storing access levels in a database table and enforcing them via JWT tokens validated at runtime.
The macro-inc/macro repository implements a robust authorization layer for collaborative documents through its channel-based permissions system. This architecture allows fine-grained control over who can view, comment on, or edit specific channels within a workspace. The implementation spans multiple crates and services, utilizing Rust's type system and PostgreSQL's atomic operations to ensure secure, consistent access control.
Core Data Structures in the Permissions Model
ChannelSharePermission and AccessLevel
The foundation of the system lives in crates/models_permissions/src/share_permission/channel_share_permission.rs, which defines the ChannelSharePermission struct. This struct stores a channel_id paired with an AccessLevel, representing a literal permission grant.
The AccessLevel enum, defined in crates/models_permissions/src/share_permission/access_level.rs, establishes a hierarchy of View, Comment, and Edit privileges. These levels implement comparison operators, allowing runtime checks using the > operator to verify if a user meets the required access threshold.
UpdateChannelSharePermission Payload
Modifications to permissions flow through the UpdateChannelSharePermission struct, which carries an UpdateOperation enum with variants Add, Remove, and Replace. This payload structure enables atomic batch updates while maintaining type safety across API boundaries.
Database Representation
The ChannelSharePermissionRow struct maps to the channel_share_permissions table, linking a share_permission_id to specific channels via channel_id and access_level columns. This relational design supports efficient querying and maintains referential integrity through foreign key constraints.
Database Operations and Atomic Updates
Permission persistence relies on SQL INSERT … ON CONFLICT … DO UPDATE statements wrapped in the create_channel_share_permissions and update_channel_share_permissions functions. These routines accept slices of ChannelSharePermission objects, enabling batch operations that either establish new permissions or modify existing ones atomically.
When the Document Storage Service processes mutations, it utilizes the code in services/document_storage_service/src/outbound/entity_mutation.rs to attach channel permissions to document requests, ensuring the database state remains synchronized with user intentions.
Runtime Enforcement and JWT Validation
Token Generation
The permission verification flow begins in the Document Storage Service, which generates JWT tokens embedding a DocumentPermissionsToken. This token contains a serialized list of ChannelSharePermission objects tied to the authenticated user, effectively packaging the user's channel access rights into a cryptographically secure format.
Request Validation
The sync-service handles enforcement through services/sync-service/src/auth.rs, where incoming requests undergo JWT validation. The service extracts the channel permissions from the token and compares the stored AccessLevel against operation requirements using the enum's ordering logic. Requests failing this check receive an immediate 403 Forbidden response, while authorized requests proceed to the handler.
Implementation Examples
The following examples demonstrate practical usage patterns drawn from the macro-inc/macro source code.
Creating a Channel Share Permission
use models_permissions::share_permission::channel_share_permission::{
ChannelSharePermission, UpdateChannelSharePermission, UpdateOperation,
};
use models_permissions::share_permission::access_level::AccessLevel;
// Build a new permission granting Edit access to channel "c123"
let permission = ChannelSharePermission {
channel_id: "c123".into(),
access_level: AccessLevel::Edit,
};
// Persist it (inside an async context)
macro_db_client::share_permission::channel_permission::create::create_channel_share_permissions(
&vec![permission],
).await?;
Updating a Channel Permission via the API
POST /api/v1/channel-permissions
{
"operation": "replace",
"channel_id": "c123",
"access_level": "comment"
}
The handler converts this payload into UpdateChannelSharePermission, then calls the database routine with the Replace operation.
Checking Permissions in Request Handlers
use models_permissions::share_permission::access_level::AccessLevel;
// `user_perms` is a Vec<ChannelSharePermission> extracted from the JWT
fn has_edit(user_perms: &[ChannelSharePermission], channel_id: &str) -> bool {
user_perms.iter()
.find(|p| p.channel_id == channel_id)
.map_or(false, |p| p.access_level >= AccessLevel::Edit)
}
If has_edit returns true, the request proceeds; otherwise the service returns a 403 Forbidden error.
Summary
- Core Abstraction: The
ChannelSharePermissionstruct incrates/models_permissions/src/share_permission/channel_share_permission.rsrepresents user access to specific channels through a combination ofchannel_idandAccessLevel. - Persistence Layer: Permissions store in the
channel_share_permissionstable viacreate_channel_share_permissionsandupdate_channel_share_permissions, using atomic upserts to maintain consistency. - Access Hierarchy: The
AccessLevelenum defines View < Comment < Edit ordering, enabling runtime comparison operations for authorization checks. - Security Model: The Document Storage Service packages permissions into JWT tokens as
DocumentPermissionsToken, while the sync-service validates these tokens inservices/sync-service/src/auth.rsto enforce access control on every request. - Type Safety: All permission structs derive
ToSchemavia utoipa, ensuring automatic OpenAPI documentation and API contract validation.
Frequently Asked Questions
What database table stores channel permissions in Macro?
The channel_share_permissions table persists channel access rights, with rows represented by the ChannelSharePermissionRow struct linking share_permission_id to specific channel_id values and their associated access_level.
How does Macro handle atomic updates to multiple channel permissions?
The system uses PostgreSQL's INSERT … ON CONFLICT … DO UPDATE syntax wrapped in the create_channel_share_permissions and update_channel_share_permissions functions, accepting vectors of ChannelSharePermission objects to batch modifications atomically.
What are the different access levels available in Macro's channel permissions?
The AccessLevel enum defines three hierarchical levels: View, Comment, and Edit. These levels support comparison operators, allowing code to check if a user's access meets or exceeds required thresholds using standard comparison syntax.
How does the sync-service validate channel permissions?
The sync-service extracts ChannelSharePermission vectors from JWT tokens containing DocumentPermissionsToken claims, then validates requests against these permissions in services/sync-service/src/auth.rs, returning 403 Forbidden for unauthorized operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →