Class QuartzHttpApiOptions
- Namespace
- Quartz
- Assembly
- Quartz.AspNetCore.dll
How the HTTP API is served.
public sealed class QuartzHttpApiOptions
- Inheritance
-
QuartzHttpApiOptions
- Inherited Members
Remarks
There is one set of these per process, not one per scheduler. The API serves every scheduler in the
container through one set of endpoints — a request names the scheduler it is for — so what is
configured here describes the endpoints rather than any scheduler. That is why there is no
IQuartzBuilder registration: calling services.AddQuartzHttpApi(configure) twice configures
the same options twice, and the last callback registered wins for any setting both of them touch.
Properties
ApiPath
The path the API is served under. It is a property of the process, not of a scheduler: every scheduler is reached under this one path.
public string ApiPath { get; set; }
Property Value
Remarks
MapQuartzHttpApi(pattern) says the same thing where the endpoints are mapped, which is
where the rest of an application's routes are written, and a pattern given there wins over this.
IncludeStackTraceInProblemDetails
Whether a failure's stack trace is included in the problem details returned to the caller.
public bool IncludeStackTraceInProblemDetails { get; set; }
Property Value
Remarks
It also puts the real message of a 500 back in the response body. Both are things to read
while developing and neither is something to ship: a fault's message routinely names the server,
the database, the login or the constraint that produced it.
IsJobTypeAllowed
Which job types a request may name. Null — the default — allows every one, which is what every earlier release did.
public Func<string, bool>? IsJobTypeAllowed { get; set; }
Property Value
Remarks
This is the narrowing for the thing SECURITY.md says is not a vulnerability: a caller who
passes authorization can schedule any type that implements IJob, and
Quartz.Jobs' NativeJob implements it and starts the executable its job data names.
An operator who knows which jobs their deployment schedules can say so here, and the surface stops
being "any IJob on the probing path".
The predicate is given the job type name, exactly as the request spelled it. Nothing
resolves it first, and that is the point: a name arriving over HTTP is data until the side that
runs the job loads it, and resolving one here to compare types would be the assembly probe this
API exists not to make. So match on the string — a set of names, a namespace prefix — and allow
for the fact that one type has more than one spelling: Acme.Jobs.Nightly, Acme.Jobs and the
same name with Version, Culture and PublicKeyToken after it are both it. A
predicate matching a bare StartsWith on the namespace covers every spelling at once.
A refused name is 403 with problem details naming the type the request asked for and
nothing else — it is the caller's own input, so it gives nothing away. The refusal is per request
rather than per job, so schedule-jobs stores none of its batch when one job in it names a
type that is not allowed.
It is one predicate for the process, like everything else here, so it cannot say that one
scheduler may run a type another may not; the request's scheduler name is not passed to it. The
dashboard's QuartzDashboardOptions.IsJobTypeAllowed is the same setting for that surface,
and the two are configured separately because a deployment can map one of them and not the other.
MaxPageSize
The most items one paged request may return: 1000 by default, and 0 for no limit.
A take naming a number above it is a 400.
public int MaxPageSize { get; set; }
Property Value
Remarks
A page size is the only thing on this API a caller can use to make the server do arbitrary work, and until 4.0.0-beta.1 nothing bounded it: one request could materialize every trigger in the store while the bulk key fetch next door refused 1001 keys. The default is that same 1000.
?take=all is bounded by this rather than refused by it. It does not name a number — it says
"as many as you will give me" — so it is answered with this many, and hasMore says whether
that was all of them. A listing whose matches fit under the cap therefore answers exactly as it
would with no cap at all, which is what keeps the 3.x-compatible listings
(GetJobKeys and its neighbours) working through HttpScheduler: they ask for
everything whether the answer is three rows or three million.
Set it to 0 where an export or a migration really has to take everything in one call, and
put the API behind something that says who may.
SchedulerAuthorizationPolicy
The authorization policy every route that names a scheduler is held to, evaluated against a
SchedulerResource carrying that name. Null — the default — leaves the API as it was:
whatever RequireAuthorization(…) the application put on the mapped group, applied uniformly
to every scheduler.
public string? SchedulerAuthorizationPolicy { get; set; }
Property Value
Remarks
Set it and each request is checked with
IAuthorizationService.AuthorizeAsync(user, new SchedulerResource(name), policy) before the
scheduler is looked up, so a caller who fails cannot tell "no such scheduler" from "not yours": a
refusal is 403 with problem details, and a 404 only ever answers a scheduler the
caller was allowed to ask about. The scheduler listing is filtered the same way.
The check is authorization, never authentication: an anonymous caller gets whatever the policy
says, which is a 403 when the policy refuses. Put RequireAuthorization() on the
mapped group as well to have an anonymous caller challenged with a 401 first.