Skip to main content

Challenge

Struct Challenge 

Source
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: i64

Implementations§

Source§

impl Challenge

Source

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.

Source

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).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn to_json(&self, base_url: &str) -> Value

The RFC 8555 challenge object: type, the derived challenge url, status, token, plus validated (RFC3339) and error once set.

Trait Implementations§

Source§

impl Debug for Challenge

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more