Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.

Multi-Tenant + RBAC support#56

Open
awantoch wants to merge 21 commits intomainfrom
feat/multi-tenant
Open

Multi-Tenant + RBAC support#56
awantoch wants to merge 21 commits intomainfrom
feat/multi-tenant

Conversation

@awantoch
Copy link
Collaborator

No description provided.

@awantoch awantoch self-assigned this Nov 10, 2025
… validation

- Implemented comprehensive security tests to ensure data isolation between organizations, covering run access, flow deployments, flow lists, batch content retrieval, version history, and deletion operations.
- Updated schema validation tests to reflect changes from tenant-based to organization-based identifiers, ensuring proper foreign key relationships and constraints.
- Adjusted SQL queries in tests to use organization_id instead of tenant_id, aligning with the new multi-organization architecture.
- Added role and permission types to the TypeScript definitions.
- Introduced permission checking utilities in a new permissions module.
- Updated AuthContext to manage user roles and permissions.
- Enhanced API client to use versioned endpoints for authentication and OAuth.
- Refactored organization and team management pages to utilize new permission checks.
- Updated login and registration pages to handle errors more gracefully.
- Improved user experience by displaying user roles and permission-related messages.
- Updated flow storage functions to require organization_id for isolation.
- Modified Engine and webhook handlers to include organization_id in flow operations.
- Enhanced McpServer and McpManager to support organization-specific server instances.
- Added organization_id field to StepRun for better query isolation.
- Updated database schemas and queries in Postgres and SQLite to handle organization_id.
- Implemented tests to verify organization isolation across flows and steps.
///
/// This is used for both organization_id and flow names to ensure
/// they don't contain path separators or parent directory references.
fn validate_path_component(value: &str, field_name: &str) -> Result<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of banning certain chars to be interpolated into the path, you should validate organization_id to only contain valid chars (ones that are safe to interpolate). Suggest creating a custom struct OrganizationId(String) type (note no pub) and then create a try_from() constructor which validates the characters inside. Construct the OrganizationId ASAP in the flow.

Comment on lines 581 to 586
let row = sqlx::query(
"SELECT version FROM flow_versions
WHERE flow_name = $1
WHERE organization_id = $1 AND flow_name = $2
ORDER BY deployed_at DESC, version DESC
LIMIT 1",
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let row = sqlx::query(
"SELECT version FROM flow_versions
WHERE flow_name = $1
WHERE organization_id = $1 AND flow_name = $2
ORDER BY deployed_at DESC, version DESC
LIMIT 1",
)
let query = "
SELECT version
FROM flow_versions
WHERE organization_id = $1
AND flow_name = $2
ORDER BY deployed_at DESC,
version DESC
LIMIT 1
";
let row = sqlx::query(query)

// User methods
async fn create_user(&self, user: &crate::auth::User) -> Result<()> {
sqlx::query(
r#"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need to be a raw string

@@ -478,15 +538,20 @@ impl FlowStorage for PostgresStorage {
Ok(row.and_then(|r| r.try_get("content").ok()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should map to Result not None

encrypted_refresh,
)?;

creds.push(OAuthCredential {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use #[derive(sqlx::FromRow)] and sqlx::query_as()

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants