Class MethodSafety
Mutating is not bound to
one of them.
This is the single definition of the safe-method set for this framework. Anything that needs to know whether a
request may change state — notably
LoopbackBoundary.isStateChanging(String), which delegates here
— reads it from this class, so a security filter and a startup check cannot come to disagree about which
methods are writes. A divergence between those two would be silent and would defeat both.
The check
check(List) runs when a resource's operation table is assembled, from
RestOperations(RestOperations.Builder) — the one constructor every operation table
passes through, so there is no registration to remember and no path around it. It fails the resource when an
operation carrying @Mutating resolves to a safe method.
The resolved method is read from RestOpContext.getHttpMethod(), which is what dispatch itself uses. That
matters more than it sounds: the method may have been inferred from the Java method name rather than
written down (see RestOp.method()), and a check that re-derived it independently could disagree with
dispatch about which operation is bound to what. A security-relevant check that disagrees with the router is
worse than none.
A wildcard operation (@RestOp(method="*")) matches GET among everything else, so it is a
contradiction too and is reported as one.
The same check fails an operation declared read=true that is bound to a method defined as
unsafe, to "*" or to "RRPC", or that is also @Mutating. A read can be reached by
overriding a safe request, so a misdeclared read would be a write behind a plain link.
Why the rule is a declared contradiction rather than a detected one
See Mutating, which records why inferring "this handler mutates" is not soundly possible and why a check
that can wrongly refuse to start an application is worse than no check at all. The short form: this class only
ever reports a contradiction the developer wrote down, so it cannot produce a false positive, and it cannot catch
a handler that mutates and says nothing.
See Also:
- Since:
- 10.0.0
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic voidcheck(List<RestOpContext> ops) static voidcheckOperation(String httpMethod, Method javaMethod) static voidcheckOperation(String httpMethod, Method javaMethod, boolean read) static StringeffectiveMethod(String method, String rawQuery, UnaryOperator<String> headers) The method a request is judged on by a write-protection check.static booleanWhethermethodis defined as safe, and therefore promises not to change state.The names of the request headers that can claim a request's method, in precedence order.queryClaims(String rawQuery) Every value of themethodquery parameter, in order.
-
Constructor Details
-
MethodSafety
protected MethodSafety()Constructor.
-
-
Method Details
-
isSafe
Whethermethodis defined as safe, and therefore promises not to change state.- Parameters:
method- The request method. Can benull , which is not safe.- Returns:
true if the method is one ofGET,HEAD,OPTIONSorTRACE.
-
effectiveMethod
The method a request is judged on by a write-protection check.A request is judged on the method it claims, not only on its request line. A
methodquery parameter, or anX-Method,X-HTTP-Method-Override,X-HTTP-MethodorX-Method-Overrideheader, naming anything other than a safe method makes the request a write, whether or not the resource it reaches is configured to honour that override. A check that followed dispatch's override configuration instead would open a hole the day a resource's allowed overrides changed.The rules fail closed:
- The request-line method is the floor. An unsafe request line is returned (upper-cased); an override can raise a
request's severity but never lower it, so
POST ?method=GETstaysPOST. - Any unsafe claim wins, so
?method=GET&method=DELETEisDELETE. - An unknown method is unsafe, so
?method=FROBis judged as a write. - A claim with a malformed percent-escape is judged on its raw text, which is never a safe method name.
- A blank claim is ignored.
- Parameters:
method- The request line's method. Can benull , which is returned asnull (unsafe).rawQuery- The raw, undecoded query string. Can benull .headers- Looks up a request header by name, returningnull when absent. Must not benull .- Returns:
- The upper-cased request-line method when it is not safe, else the first unsafe claim (upper-cased), else the upper-cased request-line method.
- The request-line method is the floor. An unsafe request line is returned (upper-cased); an override can raise a
request's severity but never lower it, so
-
overrideHeaders
The names of the request headers that can claim a request's method, in precedence order.The order is part of the contract: dispatch honours the first admitted header in this order.
- Returns:
- An immutable list of header names. Never
null .
-
queryClaims
Every value of themethodquery parameter, in order.The one parser shared by the write-protection filters and by dispatch, so they cannot disagree about what a query string claims. The raw query is split on
&, each name and value is percent-decoded, and the name is matched case-insensitively. Values are returned untrimmed. A malformed percent-escape leaves the raw text.- Parameters:
rawQuery- The raw, undecoded query string. Can benull .- Returns:
- The decoded values, in order. Never
null ; empty when there is no claim.
-
check
Fails when any operation declared@Mutatingis bound to a safe method, or any operation declaredread=trueis bound to a method that cannot be a read.- Parameters:
ops- The operations of one resource. Must not benull .- Throws:
InternalServerError- If a mutating operation is bound to a safe method, or a read declaration is misplaced or contradicts the operation's method or a@Mutatingdeclaration.
-
checkOperation
Fails whenjavaMethodis declared@MutatingandhttpMethodis safe.The per-operation half of
check(List), taking the two facts it compares rather than a builtRestOpContext, so the rule can be exercised directly.- Parameters:
httpMethod- The resolved HTTP method, as dispatch sees it. Can benull , which is not safe."*"matches every method and is therefore treated as including a safe one.javaMethod- The Java method implementing the operation. Must not benull .- Throws:
InternalServerError- IfjavaMethodis declared mutating andhttpMethodis safe.
-
checkOperation
Fails whenjavaMethodis declared@MutatingandhttpMethodis safe, or when the operation is declaredread=trueandhttpMethodcannot be a read.A read declaration is refused on the methods defined as unsafe (
POST,PUT,PATCH,DELETE,CONNECT), on"*"(which answersDELETEtoo), on"RRPC"(which dispatches arbitrary interface methods), and together with@Mutating. It is accepted on a custom method, and is redundant on a safe one.- Parameters:
httpMethod- The resolved HTTP method, as dispatch sees it. Can benull , which is not safe."*"matches every method and is therefore treated as including a safe one.javaMethod- The Java method implementing the operation. Must not benull .read- Whether the operation is declaredread=true.- Throws:
InternalServerError- If the declarations contradict each other or the method.- Since:
- 10.0.0
-