Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
eb1c7c2
[pipeline-manager] Single-tenant lookup and idempotent tenant creation
gz Aug 5, 2026
e2ca990
[pipeline-manager] Treat empty tenant-claim entries as no claim
gz Aug 5, 2026
1b611d9
[pipeline-manager] Record membership provenance, add login DB operations
gz Aug 5, 2026
aad0ff4
[pipeline-manager] Authorize logins from their memberships
gz Aug 5, 2026
dd0a3ee
[python] Scenario matrix for membership-driven authorization
gz Aug 5, 2026
c5c84e2
[web-console] Drive tenant selection from session memberships
gz Aug 5, 2026
7f224ac
[fda] Manage tenants and members, select the acting tenant
gz Aug 5, 2026
b309820
[docs] Membership authorization guide, migration runbook, changelog
gz Aug 5, 2026
bc4454b
[pipeline-manager] Log RBAC audit lines at debug
gz Aug 6, 2026
0fc4f45
[pipeline-manager] Read member profiles from the OIDC UserInfo endpoint
gz Aug 7, 2026
d5606d8
[web-console] Name members and mark verified emails
gz Aug 7, 2026
524330b
[fda] Name members and mark verified emails in `member list`
gz Aug 7, 2026
0bf4a73
[docs] How members are identified in the member list
gz Aug 7, 2026
943de47
[scripts] Give the dev issuer profiles, `auth_time`, and a readable log
gz Aug 7, 2026
60a1d5a
[pipeline-manager] Build the OIDC HTTP clients once, not per fetch
gz Aug 7, 2026
3027281
[pipeline-manager] Require an encrypted destination for the UserInfo …
gz Aug 7, 2026
86e91ab
[fda] Display text the API returns rather than obey it
gz Aug 7, 2026
94763aa
[web-console] Make newly added tenants immediately available in tenan…
Karakatiza666 Aug 7, 2026
85a2268
[web-console] Simplify the gating on whether any tenant is selected b…
Karakatiza666 Aug 7, 2026
b9d4feb
[python] Retry on a wall-clock deadline instead of an attempt cap
gz Aug 8, 2026
a0bd1e6
[python] Retry dropped connections on idempotent non-GET endpoints
gz Aug 8, 2026
42150ee
[python] Rerun tests that fail with infra-shaped errors
gz Aug 8, 2026
71deec6
[pipeline-manager] Send Retry-After on 503 error responses
gz Aug 8, 2026
99470f9
[python] Gate the retry deadline to CI; reserve 4 GiB for the buffer …
gz Aug 8, 2026
031ece6
[python] Reformat feldera_client.py for current ruff
gz Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 109 additions & 1 deletion crates/fda/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use uuid::Uuid;

use crate::make_client;
use feldera_rest_api::types::{
ClusterMonitorEventFieldSelector, CompilationProfile, PipelineMonitorEventFieldSelector,
ClusterMonitorEventFieldSelector, CompilationProfile, MemberRole,
PipelineMonitorEventFieldSelector,
};

/// Autocompletion for pipeline names by trying to fetch them from the server.
Expand All @@ -28,6 +29,7 @@ fn pipeline_names(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
cli.auth,
None,
cli.timeout,
cli.tenant,
) else {
return completions;
};
Expand Down Expand Up @@ -162,6 +164,19 @@ pub struct Cli {
help_heading = "Global Options"
)]
pub timeout: Option<u64>,
/// The tenant to act in, by name or id, sent as the `Feldera-Tenant`
/// header on every request.
///
/// Needed when the credential may act in several tenants: a platform
/// owner, or a user who belongs to more than one tenant. An API key is
/// tenant-scoped and needs no selection.
#[arg(
long,
env = "FELDERA_TENANT",
global = true,
help_heading = "Global Options"
)]
pub tenant: Option<String>,
}

#[derive(ValueEnum, Clone, Copy, Debug, PartialEq)]
Expand Down Expand Up @@ -254,6 +269,14 @@ pub enum Commands {
#[command(subcommand)]
action: TenantActions,
},
/// Manage the members of the acting tenant and their roles.
///
/// Acts in the tenant named by `--tenant`, or in the one your credential
/// resolves to without it.
Member {
#[command(subcommand)]
action: MemberActions,
},
/// Cluster information and status.
Cluster {
#[command(subcommand)]
Expand Down Expand Up @@ -340,10 +363,95 @@ pub enum TrustRole {
Admin,
}

/// The roles a tenant membership may carry. `owner` is platform-wide rather
/// than a membership, so it is configured at deploy time and never assigned
/// here, which is why this cannot be a total mapping from the API's role type.
#[derive(Clone, Copy, Debug, ValueEnum)]
pub enum TenantMemberRole {
Read,
Write,
Admin,
}

impl From<TenantMemberRole> for MemberRole {
fn from(role: TenantMemberRole) -> Self {
match role {
TenantMemberRole::Read => MemberRole::Read,
TenantMemberRole::Write => MemberRole::Write,
TenantMemberRole::Admin => MemberRole::Admin,
}
}
}

/// Spelled as the API spells it, so printed output matches what the server
/// stores and what `fda member list` reads back.
impl Display for TenantMemberRole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
TenantMemberRole::Read => "read",
TenantMemberRole::Write => "write",
TenantMemberRole::Admin => "admin",
})
}
}

#[derive(Subcommand)]
pub enum MemberActions {
/// List the members of the acting tenant and their roles.
List,
/// Grant a user access to the acting tenant, by identity.
///
/// Works before the user's first login: the membership authorizes as soon
/// as that identity authenticates through the platform's identity
/// provider. The role is capped at your own.
Add {
/// OIDC subject of the user, matching the `sub` claim their identity
/// provider issues.
subject: String,
/// Role to grant: `read`, `write` or `admin`.
#[arg(long)]
role: TenantMemberRole,
/// Email, shown in the member list. Optional.
#[arg(long)]
email: Option<String>,
},
/// Change a member's role in the acting tenant.
SetRole {
/// Identifier of the user, as shown by `fda member list`.
user_id: Uuid,
/// New role: `read`, `write` or `admin`.
role: TenantMemberRole,
},
/// Remove a member from the acting tenant.
///
/// Whether this alone revokes access depends on the deployment: where a
/// login provisions memberships, the user is re-added at the default role
/// on their next login unless their identity provider stops resolving this
/// tenant for them.
#[clap(aliases = &["rm"])]
Remove {
/// Identifier of the user, as shown by `fda member list`.
user_id: Uuid,
},
}

#[derive(Subcommand)]
pub enum TenantActions {
/// List every tenant in the installation.
List,
/// Retrieve a single tenant by name or identifier.
Get {
/// The tenant's name, or its identifier as shown by `fda tenant list`.
tenant: String,
},
/// Create a tenant, or return it if one with this name already exists.
///
/// A login resolves its tenant by name, so a user whose identity provider
/// asserts this name lands in the tenant created here.
Create {
/// The name of the tenant.
name: String,
},
/// Rename a tenant.
///
/// A login resolves its tenant by name, so the new name decides which users
Expand Down
Loading
Loading