Skip to content

Instantly share code, notes, and snippets.

@yanisurbis
Created November 12, 2024 22:15
Show Gist options
  • Select an option

  • Save yanisurbis/07636e91231f3b498832f86e55b5476b to your computer and use it in GitHub Desktop.

Select an option

Save yanisurbis/07636e91231f3b498832f86e55b5476b to your computer and use it in GitHub Desktop.

Understanding Instance and Collection-Level Authorization Queries

When designing authorization systems, one of the fundamental distinctions we need to make is between instance-level and collection-level authorization queries. Understanding this distinction is crucial for building secure and maintainable applications.

Collection-Level Authorization

Collection-level authorization answers questions about entire types or collections of resources. These queries determine whether an actor has broad permissions across a category of resources.

Common Collection-Level Queries

  • Can a user create new instances of a resource type?
  • Can a user list/view all resources of a type?
  • Can a user export the entire collection?
  • Can a user perform batch operations?

Example Implementation

// Definition
can('list', 'Document', { organizationId: '123' })
can('create', 'Document', { organizationId: '123' })

// Usage
assertCan('list', 'Document')  // Fails - too broad
assertCan('list', 'Document', { organizationId: '123' }) // Succeeds

Collection-level authorization is particularly important for:

  • API endpoints that return lists of resources
  • UI components that show resource indexes
  • Bulk operations and exports
  • Resource creation permissions

Instance-Level Authorization

Instance-level authorization deals with permissions for specific resource instances. These queries determine whether an actor can perform operations on particular objects.

Common Instance-Level Queries

  • Can a user view this specific document?
  • Can a user edit this particular record?
  • Can a user delete this instance?
  • Can a user share this resource?

Example Implementation

// Definition
can('read', 'Document', { 
  id: '456',
  organizationId: '123',
  ownerId: ctx.user.id
})

// Usage
assertCan('read', 'Document', { 
  id: '456',
  organizationId: '123',
  ownerId: 'user1'
})

Instance-level authorization is crucial for:

  • Individual resource operations
  • Detail views in applications
  • Update/delete operations
  • Sharing and collaboration features

The Relationship Between Levels

It's important to note that having collection-level permission doesn't automatically grant instance-level permissions, and vice versa. For example:

  • A user might be able to list all documents but not read specific confidential ones
  • A user might be able to read specific shared documents but not list all documents
  • A user might be able to create new resources but not modify existing ones

Best Practices

  1. Be Explicit: Always explicitly define both collection and instance-level permissions
  2. Default to Deny: Require specific conditions for collection-level access
  3. Consider Performance: Cache instance-level permissions when checking collections
  4. Maintain Consistency: Ensure collection and instance rules don't contradict
  5. Document Intentions: Make it clear which level each permission rule targets

Common Pitfalls

  1. Over-permissive Collection Access: Granting broad collection access without proper filtering
  2. Inconsistent Rules: Having collection rules that conflict with instance rules
  3. Missing Level: Implementing one level without considering the other
  4. Ambiguous Conditions: Not clearly specifying whether conditions apply at collection or instance level

Conclusion

Understanding and properly implementing both instance and collection-level authorization is fundamental to building secure applications. Always consider both levels when designing authorization rules, and be explicit about which level each rule targets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment