fix(fxt,api): wire real gRPC auth login and add api.access permission

- Replace simulated login in fxt CLI with real AuthServiceClient gRPC call
- Add auth proto to fxt build.rs compile list and lib.rs proto module
- Add api.access permission to JWT claims issued by AuthGrpcService
- Remove orphaned ci-sensor.yaml (replaced by cluster-applied version)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-05 01:41:45 +01:00
parent 8ab6a28119
commit 8b6588936d
5 changed files with 22 additions and 78 deletions

View File

@@ -4,6 +4,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// legacy fat-client proto kept for wire compatibility (package name retained).
let proto_root = "../../proto";
let protos: Vec<String> = [
"auth",
"trading",
"health",
"ml",
@@ -29,6 +30,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.compile_protos(&protos, &[proto_root.to_owned()])?;
for proto in &[
"auth",
"trading",
"health",
"ml",

View File

@@ -148,35 +148,25 @@ impl LoginClient {
password: &str,
auth_manager: &AuthTokenManager<S>,
) -> Result<()> {
// Build login request
let _login_request = LoginRequest {
use crate::proto::auth::auth_service_client::AuthServiceClient;
let mut client = AuthServiceClient::new(self.gateway_channel.clone());
let request = crate::proto::auth::LoginRequest {
username: username.to_owned(),
password: password.to_owned(),
};
// TODO: Call API login endpoint via gRPC
// For now, simulate a successful login response
if Self::is_api_available() {
tracing::warn!(
"API configured but gRPC client not yet implemented -- using simulation"
);
}
tracing::warn!(
"SECURITY: Using simulated login response -- not suitable for production"
);
let response = self.simulate_login_response();
if response.mfa_required {
anyhow::bail!(
"MFA required but non-interactive login does not support MFA. Use interactive login instead."
);
}
let resp = client
.login(request)
.await
.map_err(|e| anyhow::anyhow!("Login failed: {e}"))?
.into_inner();
let token_info = TokenInfo {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_at: response.expires_at,
access_token: resp.access_token,
refresh_token: resp.refresh_token,
#[allow(clippy::cast_sign_loss)]
expires_at: resp.expires_at as u64,
};
auth_manager.set_tokens(token_info).await?;

View File

@@ -62,6 +62,12 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
///
/// All protos are compiled from the workspace-root `proto/` directory.
pub mod proto {
/// Auth service protobuf definitions (package `auth`)
#[allow(clippy::shadow_reuse, clippy::impl_trait_in_params, clippy::empty_structs_with_brackets)]
pub mod auth {
tonic::include_proto!("auth");
}
/// Trading service protobuf definitions (package `trading`)
#[allow(clippy::shadow_reuse, clippy::impl_trait_in_params, clippy::empty_structs_with_brackets)]
pub mod trading {

View File

@@ -1,54 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: ci-pipeline-trigger
namespace: foxhunt
labels:
app.kubernetes.io/name: ci-pipeline-trigger
app.kubernetes.io/part-of: foxhunt
spec:
template:
serviceAccountName: argo-workflow
eventBusName: default
dependencies:
- name: gitlab-push-dep
eventSourceName: gitlab-push
eventName: gitlab-push
filters:
data:
- path: body.ref
type: string
value:
- refs/heads/main
triggers:
- template:
name: ci-pipeline
argoWorkflow:
operation: submit
source:
resource:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: ci-pipeline-
namespace: foxhunt
spec:
serviceAccountName: argo-workflow
arguments:
parameters:
- name: commit-sha
- name: commits-json
- name: cuda-compute-cap
value: "89"
workflowTemplateRef:
name: ci-pipeline
parameters:
- src:
dependencyName: gitlab-push-dep
dataKey: body.checkout_sha
dest: spec.arguments.parameters.0.value
- src:
dependencyName: gitlab-push-dep
dataKey: body.commits
dataTemplate: "{{ toJson .Input }}"
dest: spec.arguments.parameters.1.value

View File

@@ -88,7 +88,7 @@ impl AuthGrpcService {
iss: self.jwt_config.jwt_issuer.clone(),
aud: self.jwt_config.jwt_audience.clone(),
roles: vec!["user".to_string()],
permissions: vec![],
permissions: vec!["api.access".to_string()],
token_type: token_type.to_string(),
session_id: Some(session_id.to_string()),
};