auth: extract community auth crate

This commit is contained in:
github-ops
2026-05-14 19:48:58 +00:00
parent 56268d1482
commit 7c2a8f4fac
13 changed files with 457 additions and 139 deletions
+82 -39
View File
@@ -7,12 +7,11 @@ use crank_core::{
Agent, AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AggregationMode,
AsyncJobHandle, AuditSink, AuthConfig, AuthKind, AuthProfile, AuthProfileId, CapabilityProfile,
CommunityCapabilityProfile, ConfigExport, EditionCapabilities, ExecutionMode, ExportMode,
GeneratedDraft, GeneratedDraftStatus, IdentityProvider, InvitationId, InvitationStatus,
InvitationToken, InvocationLevel, InvocationLog, InvocationLogId, InvocationSource,
InvocationStatus, IssueAgentTokenRequest, IssueOneTimeAgentTokenRequest,
IssuedAgentTokenResponse, JobStatus, MachineAccessMode, MachineTokenIssuer, MembershipRole,
NoMachineTokenIssuer, NoopAuditSink, OperationId, OperationSecurityLevel, OperationStatus,
OwnerOnlyPolicyEngine, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyScope,
GeneratedDraft, GeneratedDraftStatus, IdentityError, IdentityProvider, InvitationId,
InvitationStatus, InvitationToken, InvocationLevel, InvocationLog, InvocationLogId,
InvocationSource, InvocationStatus, JobStatus, LoginOutcome, MachineTokenIssuer,
MembershipRole, NoMachineTokenIssuer, NoopAuditSink, OperationId, OperationSecurityLevel,
OperationStatus, OwnerOnlyPolicyEngine, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyScope,
PlatformApiKeyStatus, PolicyEngine, ProductEdition, Protocol, ResponseCachePolicy, SampleId,
Samples, Secret, SecretId, SecretKind, SecretStatus, StreamSession, StreamStatus, Target,
TransportBehavior, UsagePeriod, UserId, UserSessionId, Workspace, WorkspaceId, WorkspaceStatus,
@@ -855,6 +854,67 @@ impl AdminService {
&self,
payload: LoginPayload,
) -> Result<(SessionCookie, SessionResponse), ApiError> {
let authenticated = self.authenticate_login(&payload).await?;
let session_cookie = create_session_cookie(&self.auth_settings)?;
let secret_hash = hash_session_secret(
&session_cookie.session_id,
&session_cookie.value,
&self.auth_settings.session_secret,
);
let memberships = self
.registry
.list_workspaces_for_user(&authenticated.user.id)
.await?;
let current_workspace_id = authenticated
.current_workspace_id
.as_ref()
.map(|workspace_id| workspace_id.as_str().to_owned())
.or_else(|| {
memberships
.first()
.map(|membership| membership.workspace.id.as_str().to_owned())
});
let current_workspace_ref = current_workspace_id
.as_ref()
.map(|workspace_id| WorkspaceId::new(workspace_id.clone()));
self.registry
.create_user_session(
&session_cookie.session_id,
&authenticated.user.id,
current_workspace_ref.as_ref(),
&secret_hash,
&session_cookie.expires_at,
)
.await?;
Ok((
session_cookie,
SessionResponse {
user: authenticated.user,
memberships,
current_workspace_id,
},
))
}
async fn authenticate_login(
&self,
payload: &LoginPayload,
) -> Result<crank_core::AuthenticatedIdentity, ApiError> {
if let Some(identity_provider) = &self.identity_provider {
return match identity_provider
.login_password(crank_core::LoginPayload {
email: payload.email.clone(),
password: payload.password.clone(),
})
.await
{
Ok(LoginOutcome::Authenticated(identity)) => Ok(identity),
Err(error) => Err(map_identity_error(error)),
};
}
let user = self
.registry
.get_auth_user_by_email(&payload.email)
@@ -869,39 +929,11 @@ impl AdminService {
return Err(ApiError::unauthorized("invalid email or password"));
}
let session_cookie = create_session_cookie(&self.auth_settings)?;
let secret_hash = hash_session_secret(
&session_cookie.session_id,
&session_cookie.value,
&self.auth_settings.session_secret,
);
let memberships = self
.registry
.list_workspaces_for_user(&user.user.id)
.await?;
let current_workspace_id = memberships
.first()
.map(|membership| membership.workspace.id.as_str().to_owned());
self.registry
.create_user_session(
&session_cookie.session_id,
&user.user.id,
memberships
.first()
.map(|membership| &membership.workspace.id),
&secret_hash,
&session_cookie.expires_at,
)
.await?;
Ok((
session_cookie,
SessionResponse {
user: user.user,
memberships,
current_workspace_id,
},
))
Ok(crank_core::AuthenticatedIdentity {
user: user.user,
memberships: vec![],
current_workspace_id: None,
})
}
pub async fn logout(
@@ -4033,6 +4065,17 @@ fn format_timestamp(timestamp: OffsetDateTime) -> String {
.unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_owned())
}
fn map_identity_error(error: IdentityError) -> ApiError {
match error {
IdentityError::BadCredentials => ApiError::unauthorized("invalid email or password"),
IdentityError::AccountDisabled => ApiError::forbidden("account is disabled"),
IdentityError::NotSupportedForProvider => ApiError::internal(
"password login is not supported by the configured identity provider",
),
IdentityError::Internal(message) => ApiError::internal(message),
}
}
async fn resolve_runtime_auth_for_task(
registry: &PostgresRegistry,
secret_crypto: &SecretCrypto,