API Guides
Permissions Guide
Flow implements a flexible permission system that controls access to samples, projects, executions, and data. This guide explains how permissions work and how to manage access control effectively.
Permission Levels
Flow uses three permission levels for resource access:
Level | Value | Capabilities |
---|---|---|
Read | 1 | View resource, download data, use in pipelines |
Edit | 2 | All Read permissions + modify metadata, add/remove data |
Share | 3 | All Edit permissions + share with others, transfer ownership |
Permission Hierarchy
Share (3)
└── Edit (2)
└── Read (1)
Higher permission levels include all capabilities of lower levels.
Resource Ownership
Every resource in Flow has an owner who has full control:
- Automatic Share permission - Owners always have share-level access
- Transfer capability - Can transfer ownership to another user
- Delete permission - Can permanently delete the resource
- Billing responsibility - Storage and compute costs
Ownership Rules
- Creator becomes owner - User who creates a resource owns it
- Ownership is exclusive - Only one owner per resource
- Owner cannot lose access - Cannot revoke own permissions
- Transfer is permanent - Ownership transfers are irreversible
Sharing Resources
Share with Users
# Python client
sample.share(
users=["colleague@example.com", "collaborator@example.com"],
permission="read" # "read", "edit", or "share"
)
# GraphQL
mutation ShareSample {
shareSample(
id: "123",
users: ["user1", "user2"],
permission: READ
) {
id
sharedWith {
... on User {
username
permission
}
}
}
}
Share with Groups
# Share with entire research group
project.share(
groups=["research-lab"],
permission="edit"
)
# GraphQL
mutation ShareProject {
shareProject(
id: "456",
groups: ["lab-members", "collaborators"],
permission: EDIT
) {
id
sharedWith {
... on Group {
name
memberCount
}
}
}
}
Bulk Sharing
# Share multiple resources at once
client.bulk_share(
resource_type="sample",
resource_ids=[123, 124, 125],
users=["user1"],
groups=["group1"],
permission="read"
)
Group-Based Permissions
Groups provide efficient permission management for teams.
Group Hierarchy
Organization
└── Research Lab (parent group)
├── Bioinformatics Team (child group)
├── Wet Lab Team (child group)
└── Data Analysis Team (child group)
Permission Inheritance
Child groups inherit parent permissions
- Members of child groups get access to parent group resources
- Parent group members do NOT get child group access
Effective permissions
- User gets the highest permission level from all their groups
- Direct user permissions override group permissions
Group Management
# Create group
group = client.create_group(
name="RNA-seq Analysis Team",
description="Team analyzing RNA-seq data",
parent_group="research-lab"
)
# Add members
group.add_members(["user1", "user2", "user3"])
# Share resources with group
sample.share(groups=[group.slug], permission="edit")
Permission Checking
Check Current Permissions
# Python client
sample = client.get_sample(123)
print(f"Can edit: {sample.permissions.can_edit}")
print(f"Can share: {sample.permissions.can_share}")
print(f"Can delete: {sample.permissions.can_delete}")
# GraphQL
query CheckPermissions {
sample(id: "123") {
id
permissions {
canView
canEdit
canShare
canDelete
}
}
}
REST API Permission Headers
# REST endpoints return permission info
curl -H "Authorization: Bearer <token>" \
https://api.flow.bio/samples/123
# Response includes permissions
{
"id": 123,
"name": "Sample-001",
"permissions": {
"can_edit": true,
"can_share": false,
"can_delete": false
}
}
Access Control Lists (ACLs)
View who has access to a resource:
Get Resource ACL
# Python client
sample = client.get_sample(123)
for user in sample.shared_with_users:
print(f"{user.username}: {user.permission_level}")
for group in sample.shared_with_groups:
print(f"{group.name}: {group.permission_level}")
GraphQL ACL Query
query GetSampleACL($id: ID!) {
sample(id: $id) {
id
owner {
username
}
sharedWith {
... on User {
id
username
email
permissionLevel
}
... on Group {
id
name
slug
permissionLevel
memberCount
}
}
}
}
Modify ACL
# Update user permission
sample.update_permission(user="colleague@example.com", permission="edit")
# Revoke access
sample.revoke_access(users=["former-collaborator@example.com"])
# Revoke group access
sample.revoke_access(groups=["old-team"])
Pipeline Execution Permissions
Special rules apply to pipeline executions:
Execution Creation
- Requires
can_run_pipelines
user flag - Must have read access to all input samples/data
- Execution owner gets full access to outputs
Execution Visibility
# Executions inherit input permissions
execution = client.run_pipeline(
pipeline="RNA-seq",
samples=[sample1.id, sample2.id], # Must have read access
share_with_input_owners=True # Auto-share with sample owners
)
Output Permissions
Pipeline outputs inherit permissions:
- Execution owner - Full access to all outputs
- Input owners - Configurable access to outputs
- Shared users - Must explicitly share execution
Public Resources
Make resources publicly accessible:
Public Projects
# Make project public
project = client.create_project(
name="Public RNA-seq Dataset",
is_public=True
)
# Or update existing project
project.update(is_public=True)
Public Access Rules
- Projects only - Only projects can be made public
- Read-only access - Public users get read permission
- Samples included - Project samples become readable
- No execution - Public users cannot run pipelines
Permission Scenarios
Collaboration Workflow
# 1. Create project for collaboration
project = client.create_project(
name="Collaborative Study",
organism="human"
)
# 2. Share with collaborators
project.share(
users=["collaborator@university.edu"],
permission="edit" # Can add samples
)
# 3. Share with broader team (read-only)
project.share(
groups=["department"],
permission="read"
)
# 4. Collaborator adds samples
# They automatically get edit permission on their samples
Lab Hierarchy
# PI has share access to everything
lab_group = client.get_group("smith-lab")
# Postdocs get edit access
postdoc_group = client.create_group(
name="smith-lab-postdocs",
parent=lab_group
)
# Students get read access
student_group = client.create_group(
name="smith-lab-students",
parent=lab_group
)
# Share project with different permission levels
project.share(groups=[postdoc_group.slug], permission="edit")
project.share(groups=[student_group.slug], permission="read")
Temporary Access
# Share for review
sample.share(
users=["reviewer@journal.com"],
permission="read"
)
# After review, revoke access
sample.revoke_access(users=["reviewer@journal.com"])
Best Practices
1. Use Groups for Teams
# Bad - sharing with individuals
for user in ["user1", "user2", "user3", "user4"]:
sample.share(users=[user], permission="read")
# Good - sharing with group
sample.share(groups=["team"], permission="read")
2. Principle of Least Privilege
# Only grant necessary permissions
if user_role == "analyst":
permission = "read" # Can run pipelines
elif user_role == "scientist":
permission = "edit" # Can modify metadata
elif user_role == "pi":
permission = "share" # Can manage access
3. Regular Access Reviews
# Audit resource access
def audit_project_access(project_id):
project = client.get_project(project_id)
print(f"Project: {project.name}")
print(f"Owner: {project.owner.username}")
print("\nShared with:")
for user in project.shared_with_users:
last_active = user.last_login
if (datetime.now() - last_active).days > 90:
print(f"⚠️ {user.username} - Inactive for 90+ days")
4. Document Permissions
# Add metadata about sharing
project.update(
metadata={
"sharing_policy": "Lab members: edit, Collaborators: read",
"data_retention": "5 years",
"contact": "pi@university.edu"
}
)
Permission Errors
Common Permission Errors
Insufficient Permissions:
{
"error": "You do not have permission to edit this sample",
"code": "FORBIDDEN",
"required_permission": "EDIT",
"current_permission": "READ"
}
Cannot Share:
{
"error": "You need share permission to share this resource",
"code": "FORBIDDEN",
"required_permission": "SHARE"
}
Pipeline Execution Denied:
{
"error": "You do not have permission to run pipelines",
"code": "FORBIDDEN",
"required_capability": "can_run_pipelines"
}
Handling Permission Errors
from flowbio.exceptions import PermissionError
try:
sample.share(users=["colleague"], permission="read")
except PermissionError as e:
if e.required_permission == "SHARE":
print("You need share permission to share this sample")
print("Please contact the owner:", sample.owner.email)
Security Considerations
1. Token Security
- Tokens inherit user permissions
- Revoked permissions take effect immediately
- Service accounts should have minimal permissions
2. Audit Logging
All permission changes are logged:
# View permission history
history = sample.get_permission_history()
for event in history:
print(f"{event.timestamp}: {event.user} {event.action}")
# 2024-01-10: alice@example.com granted READ to bob@example.com
# 2024-01-12: alice@example.com upgraded bob@example.com to EDIT
3. Data Isolation
- Users cannot see resources they don't have access to
- Search results are filtered by permissions
- No permission elevation vulnerabilities
API-Specific Permissions
REST API
All REST endpoints respect permissions:
# Returns 403 if no permission
curl -H "Authorization: Bearer <token>" \
https://api.flow.bio/samples/123
# Response
{
"error": "You do not have permission to view this sample",
"code": "FORBIDDEN",
"status": 403
}
GraphQL
GraphQL queries return null for inaccessible resources:
query GetSample($id: ID!) {
sample(id: $id) { # Returns null if no access
id
name
}
}
Python Client
The client provides permission-aware methods:
# Only returns samples you can access
samples = client.get_samples()
# Raises PermissionError if no access
try:
sample = client.get_sample(123)
except PermissionError:
print("No access to sample 123")
Advanced Permission Management
Batch Permission Updates
# Update permissions for multiple resources
client.batch_update_permissions(
resource_type="sample",
updates=[
{"id": 123, "user": "user1", "permission": "edit"},
{"id": 124, "user": "user1", "permission": "read"},
{"id": 125, "group": "team", "permission": "edit"}
]
)
Permission Templates
# Define permission templates
templates = {
"collaborator": {
"samples": "read",
"projects": "read",
"executions": "read"
},
"team_member": {
"samples": "edit",
"projects": "edit",
"executions": "read"
},
"admin": {
"samples": "share",
"projects": "share",
"executions": "share"
}
}
# Apply template
def apply_permission_template(user, template_name, resources):
template = templates[template_name]
for resource_type, permission in template.items():
resource_list = resources.get(resource_type, [])
for resource in resource_list:
resource.share(users=[user], permission=permission)
Cross-Project Permissions
# Share all samples in a project
def share_project_samples(project, users, groups, permission):
samples = project.get_samples()
for sample in samples:
sample.share(
users=users,
groups=groups,
permission=permission
)
print(f"Shared {len(samples)} samples")
Next Steps
- API Authentication - Authentication and tokens
- Group Management - Managing groups and members
- Search API - Permission-aware searching
- Error Handling - Handling permission errors