auth: extract community auth crate
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user