pub struct Challenge {
pub id: Uuid,
pub authz_id: Uuid,
pub typ: String,
pub token: String,
pub status: ChallengeStatus,
pub validated: Option<i64>,
pub error: Option<Value>,
pub created_at: i64,
}Expand description
An ACME challenge (RFC 8555 §8): one proof the client may offer for its authorization’s identifier.
Which types an authorization carries is decided by
ChallengeRegistry::types_for
— UNIQUE(authz_id, type) allows several, one per type. Whether triggering
one performs a real network check or is accepted outright is the registry’s
bypass setting, not this model’s business.
A failed challenge stores the problem document that explains why in error,
which the client reads back from the challenge object.
Fields§
§id: Uuid§authz_id: Uuid§typ: String§token: String§status: ChallengeStatus§validated: Option<i64>§error: Option<Value>The RFC 8555 problem document of a failed validation. None until one
fails.
created_at: i64Implementations§
Source§impl Challenge
impl Challenge
Sourcepub fn new(authz_id: Uuid, typ: &str) -> Challenge
pub fn new(authz_id: Uuid, typ: &str) -> Challenge
Builds a new pending challenge of type typ with a fresh random token.
Pure — nothing is persisted until Challenge::insert runs.
Each challenge gets its own token, even when several are offered for one authorization: RFC 8555 §8 describes the token as a per-challenge value, and every key authorization derives from it.
Sourcepub async fn insert<'e>(
&self,
executor: impl Into<Exec<'e>>,
) -> Result<(), Error>
pub async fn insert<'e>( &self, executor: impl Into<Exec<'e>>, ) -> Result<(), Error>
Inserts the challenge using any executor — a pool, or a transaction
(see crate::order::Order::insert for why that matters).
Sourcepub async fn create(
authz_id: Uuid,
typ: &str,
database: &Database,
) -> Result<Challenge, Error>
pub async fn create( authz_id: Uuid, typ: &str, database: &Database, ) -> Result<Challenge, Error>
Creates a new challenge of type typ, with a fresh random token, in the
pending state.
Sourcepub async fn find_by_id(
id: &str,
database: &Database,
) -> Result<Option<Challenge>, Error>
pub async fn find_by_id( id: &str, database: &Database, ) -> Result<Option<Challenge>, Error>
Looks a challenge up by id. An id that does not parse as one is None,
like an unknown one: it came from a URL.
Sourcepub async fn find_by_authz(
authz_id: Uuid,
database: &Database,
) -> Result<Vec<Challenge>, Error>
pub async fn find_by_authz( authz_id: Uuid, database: &Database, ) -> Result<Vec<Challenge>, Error>
Lists an authorization’s challenges (creation order), for the
authorization object’s challenges array.
Sourcepub async fn claim_for_validation(
&mut self,
max_in_flight: u32,
database: &Database,
) -> Result<ValidationClaim, Error>
pub async fn claim_for_validation( &mut self, max_in_flight: u32, database: &Database, ) -> Result<ValidationClaim, Error>
Takes this challenge for validation, moving pending to processing.
Returns whether the claim was won. Order::claim_for_finalize’s
primitive, applied one table down and for a sharper reason: the
validator reaches out to an address the client chose, so two triggers
that both pass a status check in memory become two probes of somebody
else’s host. Deciding it in the UPDATE is what makes “exactly one
validation per challenge” a property of the row rather than of how the
handler happens to be scheduled.
A losing caller is not an error: its challenge is already being decided,
and the answer it owes the client is the object as it stands. That
object now says processing, which is exactly what §7.1.6 asks for —
“they transition to the processing state when the client responds to
the challenge” — and §8.2 pairs it with a Retry-After, which
handlers::authz::add_pending_retry_after supplies. A retry request is
explicitly not a state change there, so the loser’s answer is a
conformant one rather than a consolation.
A claim that is never settled (the process dies mid-validation) leaves
the row processing, which is the same trade claim_for_finalize
makes: the authorization’s own expires retires it, and
post_challenge refuses an expired authorization before looking at the
challenge at all.
The claim also requires the authorization and its order to still be
pending, read in the same statement. A challenge under an authorization
a sibling already decided, or under an order another authorization has
already failed, has nothing left to prove, and validating it would be an
outbound probe of a client-chosen host for no purpose.
max_in_flight is challenge.max_in_flight_per_account, counted in the
same statement — 0 is no limit. One account can create as many orders
as it likes, and every challenge under them is a probe of an address it
named; without the cap one account fills the runner while signings,
revocations and CRLs wait behind it.
Sourcepub async fn release_validation_claim(
&mut self,
database: &Database,
) -> Result<bool, Error>
pub async fn release_validation_claim( &mut self, database: &Database, ) -> Result<bool, Error>
Gives a claim back, moving processing to pending.
The guarded inverse of claim_for_validation,
named after Order::release_finalize_claim and owed for the same reason:
post_challenge claims the row and then queues the work, so a failed
enqueue would otherwise leave a challenge processing with nothing
coming — polled by its client until the authorization expired.
Guarded on the status rather than fired blindly, so a claim another
runner has already settled is never walked back to pending.
Sourcepub async fn find_processing(
now: i64,
database: &Database,
) -> Result<Vec<(Uuid, i64)>, Error>
pub async fn find_processing( now: i64, database: &Database, ) -> Result<Vec<(Uuid, i64)>, Error>
Every challenge left processing whose authorization has not expired,
with that authorization’s expires.
The recovery read behind ChallengeValidateJob::recover: a process that
died between the claim and the enqueue leaves exactly these rows, and
nothing else re-derives them. Expired authorizations are excluded here
rather than filtered afterwards — a job queued past its own deadline is
retired without ever running, so queueing one would be work that exists
only to be thrown away.
Sourcepub async fn set_valid<'e>(
id: Uuid,
validated: i64,
executor: impl Into<Exec<'e>>,
) -> Result<bool, Error>
pub async fn set_valid<'e>( id: Uuid, validated: i64, executor: impl Into<Exec<'e>>, ) -> Result<bool, Error>
The valid transition as a bare statement, over any executor.
validated is taken as an argument rather than read from the clock here,
so a caller composing this into a transaction stamps the challenge and
its in-memory copy with the same instant. See
Authorization::set_valid for why the sync is separate.
Guarded on the challenge being undecided (pending or processing), and
returns whether it wrote: a verdict is terminal (§7.1.6), so a second
one — a redelivered job racing the first — leaves the first standing.
Sourcepub async fn set_invalid<'e>(
id: Uuid,
error: &Value,
executor: impl Into<Exec<'e>>,
) -> Result<bool, Error>
pub async fn set_invalid<'e>( id: Uuid, error: &Value, executor: impl Into<Exec<'e>>, ) -> Result<bool, Error>
The invalid transition as a bare statement, guarded the same way; see
Challenge::set_valid.
Sourcepub async fn mark_valid(&mut self, database: &Database) -> Result<bool, Error>
pub async fn mark_valid(&mut self, database: &Database) -> Result<bool, Error>
Records a successful validation: moves the challenge to valid, stamps
validated, and keeps self in sync; false when it was already decided.
Sourcepub async fn mark_invalid(
&mut self,
error: Value,
database: &Database,
) -> Result<bool, Error>
pub async fn mark_invalid( &mut self, error: Value, database: &Database, ) -> Result<bool, Error>
Records a failed validation: moves the challenge to the terminal
invalid state, stores the problem document explaining why, and keeps
self in sync.
validated is deliberately not stamped — RFC 8555 §8 defines it as
the time the challenge was successfully validated.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Challenge
impl RefUnwindSafe for Challenge
impl Send for Challenge
impl Sync for Challenge
impl Unpin for Challenge
impl UnsafeUnpin for Challenge
impl UnwindSafe for Challenge
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more