backend/api/mcp/skills/grant-permission.md
Grant roles to users or groups using Bytebase's RBAC system. Permissions can be set at workspace level (global) or project level (scoped).
bb.workspaces.setIamPolicy for workspace-level grantsbb.projects.setIamPolicy for project-level grants| Level | Scope | Use Case |
|---|---|---|
| Workspace | All projects | Admins, DBAs, global viewers |
| Project | Single project | Developers, project owners |
search_api(operationId="RoleService/ListRoles")
call_api(operationId="RoleService/ListRoles", body={})
Built-in roles:
| Role | Description |
|---|---|
roles/workspaceAdmin | Full workspace access |
roles/workspaceDBA | Database administration |
roles/workspaceMember | Basic workspace access |
roles/projectOwner | Full project access |
roles/projectDeveloper | Development access (create issues, plans) |
roles/projectReleaser | Execute rollouts |
roles/sqlEditorUser | SQL Editor access only |
roles/projectViewer | Read-only access |
roles/gitopsServiceAgent | GitOps automation |
Always fetch current policy first to avoid overwriting existing bindings.
Workspace level:
search_api(operationId="WorkspaceService/GetIamPolicy")
call_api(operationId="WorkspaceService/GetIamPolicy", body={
"resource": "workspaces/{id}"
})
Project level:
search_api(operationId="ProjectService/GetIamPolicy")
call_api(operationId="ProjectService/GetIamPolicy", body={
"resource": "projects/{project-id}"
})
Save the returned etag for the update.
Add new bindings while preserving existing ones.
Workspace level:
search_api(operationId="WorkspaceService/SetIamPolicy")
call_api(operationId="WorkspaceService/SetIamPolicy", body={
"resource": "workspaces/{id}",
"etag": "{etag-from-get}",
"policy": {
"bindings": [
{
"role": "roles/workspaceDBA",
"members": ["user:[email protected]"]
},
{
"role": "roles/workspaceMember",
"members": [
"user:[email protected]",
"user:[email protected]",
"group:[email protected]"
]
}
]
}
})
Project level:
call_api(operationId="ProjectService/SetIamPolicy", body={
"resource": "projects/{project-id}",
"etag": "{etag-from-get}",
"policy": {
"bindings": [
{
"role": "roles/projectOwner",
"members": ["user:[email protected]"]
},
{
"role": "roles/projectDeveloper",
"members": ["group:[email protected]"]
}
]
}
})
| Type | Format | Example |
|---|---|---|
| User | user:{email} | user:[email protected] |
| Group | group:{email} | group:[email protected] |
| All users | allUsers | allUsers |
Grant temporary access using CEL conditions.
call_api(operationId="ProjectService/SetIamPolicy", body={
"resource": "projects/{project-id}",
"etag": "{etag}",
"policy": {
"bindings": [{
"role": "roles/projectDeveloper",
"members": ["user:[email protected]"],
"condition": {
"expression": "request.time < timestamp('2024-12-31T23:59:59Z')",
"title": "Temporary access",
"description": "Access expires end of 2024"
}
}]
}
})
CEL variables for sqlEditorUser:
resource.database: Database name (instances/{id}/databases/{name})resource.schema_name: Schema nameresource.table_name: Table namerequest.time: For expirationCEL examples:
request.time < timestamp('2024-12-31T23:59:59Z')resource.database == "instances/prod/databases/main"| Error | Cause | Fix |
|---|---|---|
| etag mismatch | Policy changed | Re-fetch policy and retry |
| role not found | Invalid role name | List roles first, use roles/{name} format |
| invalid member | Wrong format | Use user:email or group:email format |
| permission denied | Missing setIamPolicy | Check workspace/project admin access |
| invalid CEL | Bad expression syntax | Validate CEL expression format |