mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-23 16:41:22 +01:00
Refactored the request body PR stuff
- Made sure to not break compatibility with discord bots that use the old authentication via query parameters - Query parameters now accept no-value keys - Added access control checks for the errors endpoints
This commit is contained in:
parent
a3f5298617
commit
c523e40b7f
@ -34,29 +34,29 @@ public final class Request {
|
||||
private final URIQuery query;
|
||||
private final WebUser user;
|
||||
private final Map<String, String> headers;
|
||||
private final byte[] body;
|
||||
private final byte[] requestBody;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param method HTTP method, GET, PUT, POST, etc
|
||||
* @param path Requested path /example/target
|
||||
* @param query Request parameters ?param=value etc
|
||||
* @param user Web user doing the request (if authenticated)
|
||||
* @param headers Request headers https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
* @param body Raw body as bytes, if present
|
||||
* @param method HTTP method, GET, PUT, POST, etc
|
||||
* @param path Requested path /example/target
|
||||
* @param query Request parameters ?param=value etc
|
||||
* @param user Web user doing the request (if authenticated)
|
||||
* @param headers Request headers https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
|
||||
* @param requestBody Raw body as bytes, if present
|
||||
*/
|
||||
public Request(String method, URIPath path, URIQuery query, WebUser user, Map<String, String> headers, byte[] body) {
|
||||
public Request(String method, URIPath path, URIQuery query, WebUser user, Map<String, String> headers, byte[] requestBody) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.query = query;
|
||||
this.user = user;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
this.requestBody = requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special constructor that figures out URIPath and URIQuery from "/path/and?query=params" and has no form body.
|
||||
* Special constructor that figures out URIPath and URIQuery from "/path/and?query=params" and has no request body.
|
||||
*/
|
||||
public Request(String method, String target, WebUser user, Map<String, String> headers) {
|
||||
this.method = method;
|
||||
@ -70,7 +70,7 @@ public final class Request {
|
||||
}
|
||||
this.user = user;
|
||||
this.headers = headers;
|
||||
this.body = new byte[0];
|
||||
this.requestBody = new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +106,7 @@ public final class Request {
|
||||
* @return byte[].
|
||||
*/
|
||||
public byte[] getRequestBody() {
|
||||
return body;
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +129,7 @@ public final class Request {
|
||||
}
|
||||
|
||||
public Request omitFirstInPath() {
|
||||
return new Request(method, path.omitFirst(), query, user, headers, body);
|
||||
return new Request(method, path.omitFirst(), query, user, headers, requestBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,7 +140,7 @@ public final class Request {
|
||||
", query=" + query +
|
||||
", user=" + user +
|
||||
", headers=" + headers +
|
||||
", body=" + body.length +
|
||||
", body=" + requestBody.length +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -56,19 +56,35 @@ public final class URIQuery {
|
||||
}
|
||||
String[] keyAndValue = StringUtils.split(kv, "=", 2);
|
||||
if (keyAndValue.length >= 2) {
|
||||
try {
|
||||
parameters.put(
|
||||
URLDecoder.decode(keyAndValue[0], StandardCharsets.UTF_8.name()),
|
||||
URLDecoder.decode(keyAndValue[1], StandardCharsets.UTF_8.name())
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If UTF-8 is unsupported, we have bigger problems
|
||||
}
|
||||
parseAndPutKeyValuePair(parameters, keyAndValue);
|
||||
} else if (keyAndValue.length == 1) {
|
||||
parseAndPutKeyEmptyValue(parameters, keyAndValue[0]);
|
||||
}
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private void parseAndPutKeyValuePair(Map<String, String> parameters, String[] keyAndValue) {
|
||||
try {
|
||||
parameters.put(
|
||||
URLDecoder.decode(keyAndValue[0], StandardCharsets.UTF_8.name()),
|
||||
URLDecoder.decode(keyAndValue[1], StandardCharsets.UTF_8.name())
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If UTF-8 is unsupported, we have bigger problems
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAndPutKeyEmptyValue(Map<String, String> parameters, String s) {
|
||||
try {
|
||||
parameters.put(
|
||||
URLDecoder.decode(s, StandardCharsets.UTF_8.name()), ""
|
||||
);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// If UTF-8 is unsupported, we have bigger problems
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an URI parameter by key.
|
||||
*
|
||||
@ -97,6 +113,8 @@ public final class URIQuery {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Map<String, String> byKey = new HashMap<>(this.byKey);
|
||||
byKey.remove("password");
|
||||
return "URIQuery{" +
|
||||
"byKey=" + byKey +
|
||||
'}';
|
||||
|
@ -20,6 +20,11 @@ import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.URIQuery;
|
||||
|
||||
public class RequestBodyConverter {
|
||||
|
||||
private RequestBodyConverter() {
|
||||
/* Static utility class */
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of a request as an url-encoded form.
|
||||
*
|
||||
@ -27,8 +32,8 @@ public class RequestBodyConverter {
|
||||
*/
|
||||
public static URIQuery formBody(Request request) {
|
||||
if (
|
||||
"POST".equalsIgnoreCase(request.getMethod()) &&
|
||||
"application/x-www-form-urlencoded".equalsIgnoreCase(request.getHeader("Content-type").orElse(""))
|
||||
"POST".equalsIgnoreCase(request.getMethod()) &&
|
||||
"application/x-www-form-urlencoded".equalsIgnoreCase(request.getHeader("Content-type").orElse(""))
|
||||
) {
|
||||
return new URIQuery(new String(request.getRequestBody()));
|
||||
} else {
|
||||
|
@ -189,20 +189,23 @@ public class RequestHandler implements HttpHandler {
|
||||
String requestMethod = exchange.getRequestMethod();
|
||||
URIPath path = new URIPath(exchange.getRequestURI().getPath());
|
||||
URIQuery query = new URIQuery(exchange.getRequestURI().getRawQuery());
|
||||
byte[] requestBody = new byte[0];
|
||||
byte[] requestBody = readRequestBody(exchange);
|
||||
WebUser user = getWebUser(exchange);
|
||||
Map<String, String> headers = getRequestHeaders(exchange);
|
||||
return new Request(requestMethod, path, query, user, headers, requestBody);
|
||||
}
|
||||
|
||||
private byte[] readRequestBody(HttpExchange exchange) {
|
||||
try (ByteArrayOutputStream buf = new ByteArrayOutputStream(512)) {
|
||||
int b;
|
||||
while ((b = exchange.getRequestBody().read()) != -1) {
|
||||
buf.write((byte) b);
|
||||
}
|
||||
|
||||
requestBody = buf.toByteArray();
|
||||
return buf.toByteArray();
|
||||
} catch (IOException ignored) {
|
||||
// requestBody stays empty
|
||||
return new byte[0];
|
||||
}
|
||||
WebUser user = getWebUser(exchange);
|
||||
Map<String, String> headers = getRequestHeaders(exchange);
|
||||
return new Request(requestMethod, path, query, user, headers, requestBody);
|
||||
}
|
||||
|
||||
private WebUser getWebUser(HttpExchange exchange) {
|
||||
|
@ -71,8 +71,9 @@ public class LoginResolver implements NoAuthResolver {
|
||||
|
||||
public User getUser(Request request) {
|
||||
URIQuery form = RequestBodyConverter.formBody(request);
|
||||
String username = form.get("user").orElseThrow(() -> new BadRequestException("'user' parameter not defined"));
|
||||
String password = form.get("password").orElseThrow(() -> new BadRequestException("'password' parameter not defined"));
|
||||
URIQuery query = request.getQuery();
|
||||
String username = getUser(form, query);
|
||||
String password = getPassword(form, query);
|
||||
User user = dbSystem.getDatabase().query(WebUserQueries.fetchUser(username))
|
||||
.orElseThrow(() -> new WebUserAuthException(FailReason.USER_PASS_MISMATCH));
|
||||
|
||||
@ -82,4 +83,16 @@ public class LoginResolver implements NoAuthResolver {
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private String getPassword(URIQuery form, URIQuery query) {
|
||||
return form.get("password")
|
||||
.orElseGet(() -> query.get("password")
|
||||
.orElseThrow(() -> new BadRequestException("'password' parameter not defined")));
|
||||
}
|
||||
|
||||
private String getUser(URIQuery form, URIQuery query) {
|
||||
return form.get("user")
|
||||
.orElseGet(() -> query.get("user")
|
||||
.orElseThrow(() -> new BadRequestException("'user' parameter not defined")));
|
||||
}
|
||||
}
|
||||
|
@ -57,12 +57,12 @@ public class RegisterResolver implements NoAuthResolver {
|
||||
}
|
||||
|
||||
URIQuery form = RequestBodyConverter.formBody(request);
|
||||
String username = form.get("user").orElseThrow(() -> new BadRequestException("'user' parameter not defined"));
|
||||
String username = getUser(form, query);
|
||||
|
||||
boolean alreadyExists = dbSystem.getDatabase().query(WebUserQueries.fetchUser(username)).isPresent();
|
||||
if (alreadyExists) throw new BadRequestException("User already exists!");
|
||||
|
||||
String password = form.get("password").orElseThrow(() -> new BadRequestException("'password' parameter not defined"));
|
||||
String password = getPassword(form, query);
|
||||
try {
|
||||
String code = RegistrationBin.addInfoForRegistration(username, password);
|
||||
return Response.builder()
|
||||
@ -77,4 +77,15 @@ public class RegisterResolver implements NoAuthResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private String getPassword(URIQuery form, URIQuery query) {
|
||||
return form.get("password")
|
||||
.orElseGet(() -> query.get("password")
|
||||
.orElseThrow(() -> new BadRequestException("'password' parameter not defined")));
|
||||
}
|
||||
|
||||
private String getUser(URIQuery form, URIQuery query) {
|
||||
return form.get("user")
|
||||
.orElseGet(() -> query.get("user")
|
||||
.orElseThrow(() -> new BadRequestException("'user' parameter not defined")));
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +192,9 @@ public class AccessControlTest {
|
||||
"/v1/players,200",
|
||||
"/query,200",
|
||||
"/v1/filters,200",
|
||||
"/v1/query,400"
|
||||
"/v1/query,400",
|
||||
"/v1/errors,200",
|
||||
"/errors,200",
|
||||
})
|
||||
void levelZeroCanAccess(String resource, String expectedResponseCode) throws NoSuchAlgorithmException, IOException, KeyManagementException {
|
||||
int responseCode = access(resource, cookieLevel0);
|
||||
@ -250,7 +252,9 @@ public class AccessControlTest {
|
||||
"/v1/players,200",
|
||||
"/query,200",
|
||||
"/v1/filters,200",
|
||||
"/v1/query,400"
|
||||
"/v1/query,400",
|
||||
"/v1/errors,403",
|
||||
"/errors,403",
|
||||
})
|
||||
void levelOneCanAccess(String resource, String expectedResponseCode) throws NoSuchAlgorithmException, IOException, KeyManagementException {
|
||||
int responseCode = access(resource, cookieLevel1);
|
||||
@ -308,7 +312,9 @@ public class AccessControlTest {
|
||||
"/v1/players,403",
|
||||
"/query,403",
|
||||
"/v1/filters,403",
|
||||
"/v1/query,403"
|
||||
"/v1/query,403",
|
||||
"/v1/errors,403",
|
||||
"/errors,403",
|
||||
})
|
||||
void levelTwoCanAccess(String resource, String expectedResponseCode) throws NoSuchAlgorithmException, IOException, KeyManagementException {
|
||||
int responseCode = access(resource, cookieLevel2);
|
||||
|
Loading…
Reference in New Issue
Block a user