From 0e17f3b3a26106cadf3711f16aba3cb67b7d7302 Mon Sep 17 00:00:00 2001 From: finnWellers Date: Sun, 9 Feb 2020 20:34:52 +0100 Subject: [PATCH 01/13] Add initial layout --- .../authentication/IAuthorisationService.java | 13 +++++++++++ .../AuthorisationEntityManager.java | 12 ++++++++++ .../authentication/AuthorisationService.java | 22 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java create mode 100644 src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java create mode 100644 src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java diff --git a/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java b/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java new file mode 100644 index 00000000..f91e9881 --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java @@ -0,0 +1,13 @@ +package de.fearnixx.jeak.service.authentication; + +import de.fearnixx.jeak.teamspeak.data.IUser; + +/** + * A Service for handling authentication of clients for resources. + * + */ +public interface IAuthorisationService { + String authorisationGrant(IUser user); + String requestAccessToken(String authorizationGrant); + boolean validateToken(String token); +} diff --git a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java new file mode 100644 index 00000000..98b2cb09 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java @@ -0,0 +1,12 @@ +package de.fearnixx.jeak.service.authentication; + +import javax.persistence.EntityManager; + +public class AuthorisationEntityManager { + private EntityManager entityManager; + + public EntityManager getEntityManager() { + return entityManager; + } + +} diff --git a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java new file mode 100644 index 00000000..bd783149 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java @@ -0,0 +1,22 @@ +package de.fearnixx.jeak.service.authentication; + +import de.fearnixx.jeak.teamspeak.data.IUser; + +public class AuthorisationService implements IAuthorisationService { + private AuthorisationEntityManager authorisationEntityManager; + + @Override + public String authorisationGrant(IUser user) { + return null; + } + + @Override + public String requestAccessToken(String authorizationGrant) { + return null; + } + + @Override + public boolean validateToken(String token) { + return false; + } +} From bd31ffbf89363395f49165bce70011cc32692fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Wed, 24 Jun 2020 21:36:26 +0200 Subject: [PATCH 02/13] Create drafts for authentication tokens --- .../controller/request/IRequestContext.java | 10 ++ .../request/token/IAuthenticationToken.java | 16 +++ .../request/token/ITokenAuthService.java | 20 +++ .../auth/token/AuthenticationToken.java | 37 +++++ .../request/auth/token/TokenAuthService.java | 134 ++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java create mode 100644 src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java create mode 100644 src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java create mode 100644 src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java create mode 100644 src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java new file mode 100644 index 00000000..64ed67d3 --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java @@ -0,0 +1,10 @@ +package de.fearnixx.jeak.service.controller.request; + +import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; + +import java.util.Optional; + +public interface IRequestContext { + + Optional getAuthenticationToken(); +} diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java b/src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java new file mode 100644 index 00000000..faf5a615 --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java @@ -0,0 +1,16 @@ +package de.fearnixx.jeak.service.controller.request.token; + +import de.fearnixx.jeak.service.permission.base.ISubject; +import de.fearnixx.jeak.teamspeak.data.IUser; + +import java.time.ZonedDateTime; +import java.util.Optional; + +public interface IAuthenticationToken { + + String getTokenString(); + + IUser getTokenOwner(); + + Optional getExpiry(); +} diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java b/src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java new file mode 100644 index 00000000..639f685b --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java @@ -0,0 +1,20 @@ +package de.fearnixx.jeak.service.controller.request.token; + +import de.fearnixx.jeak.service.permission.base.ISubject; +import de.fearnixx.jeak.teamspeak.data.IUser; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.ZonedDateTime; + +public interface ITokenAuthService { + + @Nonnull + IAuthenticationToken generateToken(IUser subject); + + void setTokenExpiry(@Nonnull IAuthenticationToken token, @Nullable ZonedDateTime expiryValue); + + void revokeToken(IAuthenticationToken token); + + void revokeTokens(ISubject subject); +} diff --git a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java b/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java new file mode 100644 index 00000000..ff8a6000 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java @@ -0,0 +1,37 @@ +package de.fearnixx.jeak.service.controller.request.auth.token; + +import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; +import de.fearnixx.jeak.teamspeak.data.IUser; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.ZonedDateTime; +import java.util.Optional; + +public class AuthenticationToken implements IAuthenticationToken { + + private final String tokenStr; + private final IUser tokenOwner; + private final ZonedDateTime tokenExpiry; + + public AuthenticationToken(@Nonnull String tokenStr, @Nonnull IUser tokenOwner, @Nullable ZonedDateTime tokenExpiry) { + this.tokenStr = tokenStr; + this.tokenOwner = tokenOwner; + this.tokenExpiry = tokenExpiry; + } + + @Override + public String getTokenString() { + return tokenStr; + } + + @Override + public IUser getTokenOwner() { + return tokenOwner; + } + + @Override + public Optional getExpiry() { + return Optional.ofNullable(tokenExpiry); + } +} diff --git a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java new file mode 100644 index 00000000..98349fb1 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java @@ -0,0 +1,134 @@ +package de.fearnixx.jeak.service.controller.request.auth.token; + +import de.fearnixx.jeak.event.bot.IBotStateEvent; +import de.fearnixx.jeak.profile.IUserIdentity; +import de.fearnixx.jeak.profile.event.IProfileEvent; +import de.fearnixx.jeak.reflect.Config; +import de.fearnixx.jeak.reflect.Inject; +import de.fearnixx.jeak.reflect.Listener; +import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; +import de.fearnixx.jeak.service.controller.request.token.ITokenAuthService; +import de.fearnixx.jeak.service.permission.base.ISubject; +import de.fearnixx.jeak.service.teamspeak.IUserService; +import de.fearnixx.jeak.teamspeak.data.IUser; +import de.fearnixx.jeak.util.Configurable; +import de.mlessmann.confort.api.IConfig; +import de.mlessmann.confort.api.IConfigNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.UUID; + +public class TokenAuthService extends Configurable implements ITokenAuthService { + + private static final Logger logger = LoggerFactory.getLogger(TokenAuthService.class); + private static final String NO_EXPIRY_VALUE = "never"; + public static final String EXPIRY_NODE_NAME = "expiry"; + public static final DateTimeFormatter EXPIRY_FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + + @Inject + @Config(category = "rest", id = "token-auth") + private IConfig tokenConfig; + + @Inject + private IUserService userService; + + public TokenAuthService() { + super(TokenAuthService.class); + } + + @Override + protected IConfig getConfigRef() { + return tokenConfig; + } + + protected IConfigNode getTokensNode() { + return getConfig().getNode("tokens"); + } + + @Nonnull + @Override + public synchronized IAuthenticationToken generateToken(IUser tokenOwner) { + Objects.requireNonNull(tokenOwner, "Token owner may not be null!"); + var tokenStr = UUID.randomUUID().toString().replace("-", ""); + var tokenInstance = new AuthenticationToken(tokenStr, tokenOwner, null); + + var ownerNode = getTokensNode().getNode(tokenOwner.getUniqueID().toString()); + var tokenNode = ownerNode.getNode(tokenStr); + tokenNode.getNode(EXPIRY_NODE_NAME).setString(NO_EXPIRY_VALUE); + + logger.info("Generated new authentication token for subject '{}': {}", tokenOwner, tokenStr); + return tokenInstance; + } + + @Override + public synchronized void setTokenExpiry(@Nonnull IAuthenticationToken token, @Nullable ZonedDateTime expiryValue) { + Objects.requireNonNull(token, "Provided token cannot be null."); + + var tokenNode = getTokensNode().getNode(token.getTokenString()); + if (tokenNode.isVirtual()) { + throw new IllegalArgumentException("Token '" + token.getTokenString() + "' does not exist!"); + } + if (expiryValue == null) { + tokenNode.getNode(EXPIRY_NODE_NAME).setString(NO_EXPIRY_VALUE); + logger.debug("Set token expiry of '{}' to: {}", token.getTokenString(), NO_EXPIRY_VALUE); + } else if (ZonedDateTime.now().isAfter(expiryValue)) { + logger.warn("Revoking token '{}' as expiry was set to past.", token.getTokenString()); + revokeToken(token); + } else { + tokenNode.getNode(EXPIRY_NODE_NAME).setString(expiryValue.format(EXPIRY_FORMAT)); + } + } + + @Override + public synchronized void revokeToken(IAuthenticationToken token) { + var optEntry = getTokensNode().optMap().orElseGet(Collections::emptyMap) + .entrySet() + .stream() + .filter(e -> !e.getValue().getNode(token.getTokenString()).isVirtual()) + .findFirst(); + + if (optEntry.isEmpty()) { + logger.debug("Cannot remove token '{}'. Not found!", token.getTokenString()); + } else { + var subject = optEntry.get().getKey(); + logger.info("Revoking token '{}' of subject '{}'.", token.getTokenString(), subject); + getTokensNode().getNode(subject).remove(token.getTokenString()); + } + } + + @Override + public synchronized void revokeTokens(ISubject subject) { + revokeTokens(subject.getUniqueID().toString()); + } + + protected synchronized void revokeTokens(String subjectUUID) { + logger.info("Revoking tokens of subject '{}'", subjectUUID); + getTokensNode().remove(subjectUUID); + } + + @Listener(order = Listener.Orders.EARLY) + public synchronized void onInitialize(IBotStateEvent.IInitializeEvent event) { + if (!loadConfig()) { + event.cancel(); + } + } + + @Override + protected boolean populateDefaultConf(IConfigNode root) { + root.getNode("tokens").setMap(); + return true; + } + + @Override + protected String getDefaultResource() { + return null; + } +} From 7a6e5de005ccfef4156088cf6d37e96b4424c4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 11:33:54 +0200 Subject: [PATCH 03/13] Create draft for request contextual information --- .../fearnixx/jeak/reflect/Transactional.java | 8 ----- .../jeak/reflect/{ => http}/PathParam.java | 2 +- .../jeak/reflect/{ => http}/RequestBody.java | 2 +- .../jeak/reflect/http/RequestContext.java | 33 +++++++++++++++++++ .../reflect/{ => http}/RequestMapping.java | 2 +- .../jeak/reflect/{ => http}/RequestParam.java | 2 +- .../reflect/{ => http}/RestController.java | 2 +- .../controller/request/IRequestContext.java | 30 +++++++++++++++-- .../controller/RestControllerService.java | 1 + .../controller/connection/HttpServer.java | 4 +-- .../controller/ControllerContainer.java | 4 +-- .../controller/controller/SparkAdapter.java | 8 ++--- .../testImpls/SecondTestController.java | 8 ++--- .../controller/testImpls/TestController.java | 8 ++--- 14 files changed, 83 insertions(+), 31 deletions(-) delete mode 100644 src/api/java/de/fearnixx/jeak/reflect/Transactional.java rename src/api/java/de/fearnixx/jeak/reflect/{ => http}/PathParam.java (93%) rename src/api/java/de/fearnixx/jeak/reflect/{ => http}/RequestBody.java (89%) create mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java rename src/api/java/de/fearnixx/jeak/reflect/{ => http}/RequestMapping.java (94%) rename src/api/java/de/fearnixx/jeak/reflect/{ => http}/RequestParam.java (93%) rename src/api/java/de/fearnixx/jeak/reflect/{ => http}/RestController.java (94%) diff --git a/src/api/java/de/fearnixx/jeak/reflect/Transactional.java b/src/api/java/de/fearnixx/jeak/reflect/Transactional.java deleted file mode 100644 index 46430a99..00000000 --- a/src/api/java/de/fearnixx/jeak/reflect/Transactional.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.fearnixx.jeak.reflect; - -/** - * Indicates a listener method to be transactional. - * Events will be preceded by a call to - */ -public @interface Transactional { -} diff --git a/src/api/java/de/fearnixx/jeak/reflect/PathParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java similarity index 93% rename from src/api/java/de/fearnixx/jeak/reflect/PathParam.java rename to src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java index 6a8cdbd0..c07c6e80 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/PathParam.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect; +package de.fearnixx.jeak.reflect.http; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/reflect/RequestBody.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java similarity index 89% rename from src/api/java/de/fearnixx/jeak/reflect/RequestBody.java rename to src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java index 2cfdd660..94dd5d85 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/RequestBody.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect; +package de.fearnixx.jeak.reflect.http; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java new file mode 100644 index 00000000..16f57383 --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java @@ -0,0 +1,33 @@ +package de.fearnixx.jeak.reflect.http; + +import de.fearnixx.jeak.service.controller.request.IRequestContext; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes a parameter to be filled from the request context. + * + * @apiNote The parameter injection will perform type casting within the bounds of class assignability compatibility. + * It will also resolve {@link java.util.Optional} but (due to type erasure) cannot ensure the generic contract at runtime unless the optHint parameter is set. + * + * @implNote Some usages of this cause side-effects, please see the implementation notes on {@link IRequestContext.Attributes} + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestContext { + + /** + * Denotes the attribute name to be used for the lookup. + * If none, the parameter injection will attempt to insert the {@link IRequestContext} instance. + * @see IRequestContext.Attributes for more information on available attributes. + */ + String attribute() default ""; + + /** + * For parameters of {@link java.util.Optional} type, this param can be specified to re-enable type compatibility checks. + */ + Class optHint() default Object.class; +} diff --git a/src/api/java/de/fearnixx/jeak/reflect/RequestMapping.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java similarity index 94% rename from src/api/java/de/fearnixx/jeak/reflect/RequestMapping.java rename to src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java index 5196b889..20606a1f 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/RequestMapping.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect; +package de.fearnixx.jeak.reflect.http; import de.fearnixx.jeak.service.controller.RequestMethod; diff --git a/src/api/java/de/fearnixx/jeak/reflect/RequestParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java similarity index 93% rename from src/api/java/de/fearnixx/jeak/reflect/RequestParam.java rename to src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java index 3e64aa44..62fa59c1 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/RequestParam.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect; +package de.fearnixx.jeak.reflect.http; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/reflect/RestController.java b/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java similarity index 94% rename from src/api/java/de/fearnixx/jeak/reflect/RestController.java rename to src/api/java/de/fearnixx/jeak/reflect/http/RestController.java index 3fac1cfc..3d661ab8 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/RestController.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect; +package de.fearnixx.jeak.reflect.http; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java index 64ed67d3..31c495a4 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java +++ b/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java @@ -1,10 +1,36 @@ package de.fearnixx.jeak.service.controller.request; -import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; +import org.eclipse.jetty.http.HttpStatus; import java.util.Optional; public interface IRequestContext { - Optional getAuthenticationToken(); + /** + * Optionally retrieves a request attribute from the context. + * For officially supported attributes, see {@link Attributes}. + */ + Optional optAttribute(String name, Class hint); + + final class Attributes { + /** + * {@link de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken} + * + * @apiNote Optionally filled, if authentication is successful AND the "Token" authentication scheme is used. + * + * @implNote Required (non-optional) parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication. + */ + public static final String AUTHENTICATION_TOKEN = "auth:token:authenticationToken"; + + /** + * {@link de.fearnixx.jeak.teamspeak.data.IUser} + * + * @apiNote Optionally filled, if authentication is successful AND the subject is an user. + * @implNote Required (non-optional) parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication or {@link HttpStatus#FORBIDDEN_403} for principals that aren't users. + */ + public static final String AUTHENTICATION_USER = "auth:subject:authenticatedUser"; + + private Attributes() { + } + } } diff --git a/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java b/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java index b4309b67..515d81cc 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java @@ -2,6 +2,7 @@ import de.fearnixx.jeak.event.bot.IBotStateEvent; import de.fearnixx.jeak.reflect.*; +import de.fearnixx.jeak.reflect.http.RestController; import de.fearnixx.jeak.service.controller.connection.ControllerRequestVerifier; import de.fearnixx.jeak.service.controller.connection.HttpServer; import de.fearnixx.jeak.service.controller.connection.RestConfiguration; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java b/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java index 96928556..490f9530 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import de.fearnixx.jeak.reflect.PathParam; -import de.fearnixx.jeak.reflect.RequestParam; +import de.fearnixx.jeak.reflect.http.PathParam; +import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.service.controller.controller.ControllerContainer; import de.fearnixx.jeak.service.controller.controller.ControllerMethod; import de.fearnixx.jeak.service.controller.controller.MethodParameter; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java b/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java index f469ae3a..2d995415 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java @@ -1,7 +1,7 @@ package de.fearnixx.jeak.service.controller.controller; -import de.fearnixx.jeak.reflect.RequestMapping; -import de.fearnixx.jeak.reflect.RestController; +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RestController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java b/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java index c669371d..e2877da3 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java @@ -1,10 +1,10 @@ package de.fearnixx.jeak.service.controller.controller; import de.fearnixx.jeak.Main; -import de.fearnixx.jeak.reflect.PathParam; -import de.fearnixx.jeak.reflect.RequestBody; -import de.fearnixx.jeak.reflect.RequestMapping; -import de.fearnixx.jeak.reflect.RequestParam; +import de.fearnixx.jeak.reflect.http.PathParam; +import de.fearnixx.jeak.reflect.http.RequestBody; +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.service.controller.RequestMethod; import de.fearnixx.jeak.service.controller.ResponseEntity; import de.fearnixx.jeak.service.controller.connection.HttpServer; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java b/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java index a1f692b3..faf58138 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java @@ -1,9 +1,9 @@ package de.fearnixx.jeak.service.controller.testImpls; -import de.fearnixx.jeak.reflect.RequestBody; -import de.fearnixx.jeak.reflect.RequestMapping; -import de.fearnixx.jeak.reflect.RequestParam; -import de.fearnixx.jeak.reflect.RestController; +import de.fearnixx.jeak.reflect.http.RequestBody; +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RequestParam; +import de.fearnixx.jeak.reflect.http.RestController; import de.fearnixx.jeak.service.controller.RequestMethod; @RestController(endpoint = "/test", pluginId = "testPluginId") diff --git a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java b/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java index e21c7291..4876eddc 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java +++ b/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java @@ -1,9 +1,9 @@ package de.fearnixx.jeak.service.controller.testImpls; -import de.fearnixx.jeak.reflect.RequestBody; -import de.fearnixx.jeak.reflect.RequestMapping; -import de.fearnixx.jeak.reflect.RequestParam; -import de.fearnixx.jeak.reflect.RestController; +import de.fearnixx.jeak.reflect.http.RequestBody; +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RequestParam; +import de.fearnixx.jeak.reflect.http.RestController; import de.fearnixx.jeak.service.controller.IResponseEntity; import de.fearnixx.jeak.service.controller.RequestMethod; import de.fearnixx.jeak.service.controller.ResponseEntity; From 15219cc3c3651085712a7896a986b621fda7d7d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 11:46:01 +0200 Subject: [PATCH 04/13] Move "controller" package -> "http" --- .../jeak/reflect/http/RequestContext.java | 2 +- .../jeak/reflect/http/RequestMapping.java | 2 +- .../IControllerService.java} | 21 ++++++----- .../{controller => http}/IResponseEntity.java | 2 +- .../{controller => http}/RequestMethod.java | 2 +- .../{controller => http}/ResponseEntity.java | 2 +- .../RegisterControllerException.java | 2 +- .../request/IRequestContext.java | 5 +-- .../request/token/IAuthenticationToken.java | 2 +- .../request/token/ITokenAuthService.java | 2 +- src/main/java/de/fearnixx/jeak/JeakBot.java | 4 +-- .../ControllerService.java} | 36 ++++++++++--------- .../connection/ControllerRequestVerifier.java | 2 +- .../connection/HttpServer.java | 8 ++--- .../connection/IConnectionVerifier.java | 2 +- .../connection/RestConfiguration.java | 2 +- .../controller/ControllerContainer.java | 2 +- .../controller/ControllerMethod.java | 4 +-- .../controller/IncapableDummyAdapter.java | 6 ++-- .../controller/MethodParameter.java | 2 +- .../controller/SparkAdapter.java | 12 +++---- .../auth/token/AuthenticationToken.java | 4 +-- .../request/auth/token/TokenAuthService.java | 9 ++--- .../testImpls/DummyObject.java | 2 +- .../testImpls/SecondTestController.java | 4 +-- .../testImpls/TestController.java | 8 ++--- .../test/plugin/ControllerTestPlugin.java | 10 +++--- 27 files changed, 79 insertions(+), 80 deletions(-) rename src/api/java/de/fearnixx/jeak/service/{controller/IRestControllerService.java => http/IControllerService.java} (60%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/IResponseEntity.java (86%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/RequestMethod.java (66%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/ResponseEntity.java (98%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/exceptions/RegisterControllerException.java (89%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/request/IRequestContext.java (88%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/request/token/IAuthenticationToken.java (83%) rename src/api/java/de/fearnixx/jeak/service/{controller => http}/request/token/ITokenAuthService.java (89%) rename src/main/java/de/fearnixx/jeak/service/{controller/RestControllerService.java => http/ControllerService.java} (70%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/connection/ControllerRequestVerifier.java (94%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/connection/HttpServer.java (95%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/connection/IConnectionVerifier.java (88%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/connection/RestConfiguration.java (97%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/controller/ControllerContainer.java (97%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/controller/ControllerMethod.java (95%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/controller/IncapableDummyAdapter.java (83%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/controller/MethodParameter.java (97%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/controller/SparkAdapter.java (95%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/request/auth/token/AuthenticationToken.java (86%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/request/auth/token/TokenAuthService.java (93%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/testImpls/DummyObject.java (90%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/testImpls/SecondTestController.java (88%) rename src/main/java/de/fearnixx/jeak/service/{controller => http}/testImpls/TestController.java (84%) diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java index 16f57383..69085b2f 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java @@ -1,6 +1,6 @@ package de.fearnixx.jeak.reflect.http; -import de.fearnixx.jeak.service.controller.request.IRequestContext; +import de.fearnixx.jeak.service.http.request.IRequestContext; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java index 20606a1f..3a7d8bf9 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java @@ -1,6 +1,6 @@ package de.fearnixx.jeak.reflect.http; -import de.fearnixx.jeak.service.controller.RequestMethod; +import de.fearnixx.jeak.service.http.RequestMethod; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/api/java/de/fearnixx/jeak/service/controller/IRestControllerService.java b/src/api/java/de/fearnixx/jeak/service/http/IControllerService.java similarity index 60% rename from src/api/java/de/fearnixx/jeak/service/controller/IRestControllerService.java rename to src/api/java/de/fearnixx/jeak/service/http/IControllerService.java index 51952b61..d7ca78d7 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/IRestControllerService.java +++ b/src/api/java/de/fearnixx/jeak/service/http/IControllerService.java @@ -1,28 +1,27 @@ -package de.fearnixx.jeak.service.controller; +package de.fearnixx.jeak.service.http; import java.util.Map; import java.util.Optional; /** * The controller manager allows plugins to register services to a specified REST method. - * */ -public interface IRestControllerService { +public interface IControllerService { /** * Registers a new REST controller to the controller manager. * - * @param cntrlrClass The class the controller provides. - * @param restController The controller to be registered. - * @param The Type of the service. + * @param cntrlrClass The class the controller provides. + * @param instance The controller to be registered. + * @param The Type of the service. */ - void registerController(Class cntrlrClass, T restController); + void registerController(Class cntrlrClass, T instance); /** * Optionally get a controller of the specified class. * * @param cntrlrClass The desired controller class. - * @param The desired controller type. + * @param The desired controller type. * @return An {@link Optional} representing the result. */ Optional provide(Class cntrlrClass); @@ -33,16 +32,16 @@ public interface IRestControllerService { * This method performs no checks! * * @param cntrlrClass The desired service class. - * @param The desired controller type. + * @param The desired controller type. * @return Either the controller instance, - * or {@core null} and a {@link ClassCastException} is thrown. + * or {@code null} and a {@link ClassCastException} is thrown. */ T provideUnchecked(Class cntrlrClass); /** * Provide all the registered controllers. * - * @return A {@link Map, Object>} representing the controller. + * @return A {@code Map, Object>} representing the controller. */ Map, Object> provideAll(); } diff --git a/src/api/java/de/fearnixx/jeak/service/controller/IResponseEntity.java b/src/api/java/de/fearnixx/jeak/service/http/IResponseEntity.java similarity index 86% rename from src/api/java/de/fearnixx/jeak/service/controller/IResponseEntity.java rename to src/api/java/de/fearnixx/jeak/service/http/IResponseEntity.java index a79bfa42..f336ca6b 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/IResponseEntity.java +++ b/src/api/java/de/fearnixx/jeak/service/http/IResponseEntity.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller; +package de.fearnixx.jeak.service.http; import java.util.Map; diff --git a/src/api/java/de/fearnixx/jeak/service/controller/RequestMethod.java b/src/api/java/de/fearnixx/jeak/service/http/RequestMethod.java similarity index 66% rename from src/api/java/de/fearnixx/jeak/service/controller/RequestMethod.java rename to src/api/java/de/fearnixx/jeak/service/http/RequestMethod.java index ec784442..d8e4b54d 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/RequestMethod.java +++ b/src/api/java/de/fearnixx/jeak/service/http/RequestMethod.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller; +package de.fearnixx.jeak.service.http; public enum RequestMethod { POST, diff --git a/src/api/java/de/fearnixx/jeak/service/controller/ResponseEntity.java b/src/api/java/de/fearnixx/jeak/service/http/ResponseEntity.java similarity index 98% rename from src/api/java/de/fearnixx/jeak/service/controller/ResponseEntity.java rename to src/api/java/de/fearnixx/jeak/service/http/ResponseEntity.java index a533f86b..b994e305 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/ResponseEntity.java +++ b/src/api/java/de/fearnixx/jeak/service/http/ResponseEntity.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller; +package de.fearnixx.jeak.service.http; import java.util.HashMap; import java.util.Map; diff --git a/src/api/java/de/fearnixx/jeak/service/controller/exceptions/RegisterControllerException.java b/src/api/java/de/fearnixx/jeak/service/http/exceptions/RegisterControllerException.java similarity index 89% rename from src/api/java/de/fearnixx/jeak/service/controller/exceptions/RegisterControllerException.java rename to src/api/java/de/fearnixx/jeak/service/http/exceptions/RegisterControllerException.java index 2971a664..6b4bc9ac 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/exceptions/RegisterControllerException.java +++ b/src/api/java/de/fearnixx/jeak/service/http/exceptions/RegisterControllerException.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.exceptions; +package de.fearnixx.jeak.service.http.exceptions; public class RegisterControllerException extends RuntimeException{ public RegisterControllerException(String message) { diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java similarity index 88% rename from src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java rename to src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java index 31c495a4..03097a70 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/request/IRequestContext.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java @@ -1,5 +1,6 @@ -package de.fearnixx.jeak.service.controller.request; +package de.fearnixx.jeak.service.http.request; +import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; import org.eclipse.jetty.http.HttpStatus; import java.util.Optional; @@ -14,7 +15,7 @@ public interface IRequestContext { final class Attributes { /** - * {@link de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken} + * {@link IAuthenticationToken} * * @apiNote Optionally filled, if authentication is successful AND the "Token" authentication scheme is used. * diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java b/src/api/java/de/fearnixx/jeak/service/http/request/token/IAuthenticationToken.java similarity index 83% rename from src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java rename to src/api/java/de/fearnixx/jeak/service/http/request/token/IAuthenticationToken.java index faf5a615..f09186b0 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/request/token/IAuthenticationToken.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/token/IAuthenticationToken.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.request.token; +package de.fearnixx.jeak.service.http.request.token; import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.teamspeak.data.IUser; diff --git a/src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java similarity index 89% rename from src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java rename to src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java index 639f685b..cd65d81d 100644 --- a/src/api/java/de/fearnixx/jeak/service/controller/request/token/ITokenAuthService.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.request.token; +package de.fearnixx.jeak.service.http.request.token; import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.teamspeak.data.IUser; diff --git a/src/main/java/de/fearnixx/jeak/JeakBot.java b/src/main/java/de/fearnixx/jeak/JeakBot.java index c9e65d75..e13a31b0 100644 --- a/src/main/java/de/fearnixx/jeak/JeakBot.java +++ b/src/main/java/de/fearnixx/jeak/JeakBot.java @@ -13,7 +13,7 @@ import de.fearnixx.jeak.service.command.ICommandService; import de.fearnixx.jeak.service.command.TypedCommandService; import de.fearnixx.jeak.service.command.matcher.MatcherRegistry; -import de.fearnixx.jeak.service.controller.RestControllerService; +import de.fearnixx.jeak.service.http.ControllerService; import de.fearnixx.jeak.service.database.DatabaseService; import de.fearnixx.jeak.service.event.IEventService; import de.fearnixx.jeak.service.locale.LocalizationService; @@ -184,7 +184,7 @@ protected void doServiceStartup() { initializeService(new PermissionService()); initializeService(new UserService()); initializeService(new TokenService()); - initializeService(new RestControllerService()); + initializeService(new ControllerService()); // TODO: Remove eagerly loading by a better solution dbSvc.onLoad(null); diff --git a/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java similarity index 70% rename from src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java rename to src/main/java/de/fearnixx/jeak/service/http/ControllerService.java index 515d81cc..f9d5f1f3 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/RestControllerService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java @@ -1,22 +1,23 @@ -package de.fearnixx.jeak.service.controller; +package de.fearnixx.jeak.service.http; import de.fearnixx.jeak.event.bot.IBotStateEvent; import de.fearnixx.jeak.reflect.*; import de.fearnixx.jeak.reflect.http.RestController; -import de.fearnixx.jeak.service.controller.connection.ControllerRequestVerifier; -import de.fearnixx.jeak.service.controller.connection.HttpServer; -import de.fearnixx.jeak.service.controller.connection.RestConfiguration; -import de.fearnixx.jeak.service.controller.controller.ControllerContainer; -import de.fearnixx.jeak.service.controller.controller.IncapableDummyAdapter; -import de.fearnixx.jeak.service.controller.controller.SparkAdapter; -import de.fearnixx.jeak.service.controller.exceptions.RegisterControllerException; +import de.fearnixx.jeak.service.http.connection.ControllerRequestVerifier; +import de.fearnixx.jeak.service.http.connection.HttpServer; +import de.fearnixx.jeak.service.http.connection.RestConfiguration; +import de.fearnixx.jeak.service.http.controller.ControllerContainer; +import de.fearnixx.jeak.service.http.controller.IncapableDummyAdapter; +import de.fearnixx.jeak.service.http.controller.SparkAdapter; +import de.fearnixx.jeak.service.http.exceptions.RegisterControllerException; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; -@FrameworkService(serviceInterface = IRestControllerService.class) -public class RestControllerService implements IRestControllerService { +@FrameworkService(serviceInterface = IControllerService.class) +public class ControllerService implements IControllerService { private final Map, Object> controllers; private HttpServer httpServer; @@ -26,11 +27,11 @@ public class RestControllerService implements IRestControllerService { @Inject private IInjectionService injectionService; - public RestControllerService() { + public ControllerService() { this(new HashMap<>()); } - public RestControllerService(Map, Object> controllers) { + public ControllerService(Map, Object> controllers) { this.connectionVerifier = new ControllerRequestVerifier(); this.restConfiguration = new RestConfiguration(); this.controllers = controllers; @@ -50,10 +51,10 @@ public void onPreInt(IBotStateEvent.IPreInitializeEvent preInitializeEvent) { @Override - public void registerController(Class cntrlrClass, T restController) { - ControllerContainer controllerContainer = new ControllerContainer(restController); - if (!doesControllerAlreadyExist(restController)) { - controllers.put(cntrlrClass, restController); + public void registerController(Class cntrlrClass, T instance) { + ControllerContainer controllerContainer = new ControllerContainer(instance); + if (!doesControllerAlreadyExist(instance)) { + controllers.put(cntrlrClass, instance); httpServer.registerController(controllerContainer); } else { throw new RegisterControllerException("There is already a controller with the same endpoint"); @@ -68,7 +69,8 @@ public Optional provide(Class cntrlrClass) { @Override public T provideUnchecked(Class cntrlrClass) { - return (T) controllers.get(cntrlrClass); + Objects.requireNonNull(cntrlrClass, "Controller class type hint cannot be null!"); + return cntrlrClass.cast( controllers.get(cntrlrClass)); } @Override diff --git a/src/main/java/de/fearnixx/jeak/service/controller/connection/ControllerRequestVerifier.java b/src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java similarity index 94% rename from src/main/java/de/fearnixx/jeak/service/controller/connection/ControllerRequestVerifier.java rename to src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java index 6d9303f3..bc9321f4 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/connection/ControllerRequestVerifier.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.connection; +package de.fearnixx.jeak.service.http.connection; import de.fearnixx.jeak.reflect.Inject; import de.fearnixx.jeak.service.token.ITokenService; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java similarity index 95% rename from src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java rename to src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java index 490f9530..6b75806e 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/connection/HttpServer.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.connection; +package de.fearnixx.jeak.service.http.connection; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -6,9 +6,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import de.fearnixx.jeak.reflect.http.PathParam; import de.fearnixx.jeak.reflect.http.RequestParam; -import de.fearnixx.jeak.service.controller.controller.ControllerContainer; -import de.fearnixx.jeak.service.controller.controller.ControllerMethod; -import de.fearnixx.jeak.service.controller.controller.MethodParameter; +import de.fearnixx.jeak.service.http.controller.ControllerContainer; +import de.fearnixx.jeak.service.http.controller.ControllerMethod; +import de.fearnixx.jeak.service.http.controller.MethodParameter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.Request; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/connection/IConnectionVerifier.java b/src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java similarity index 88% rename from src/main/java/de/fearnixx/jeak/service/controller/connection/IConnectionVerifier.java rename to src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java index 26725d91..f526b698 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/connection/IConnectionVerifier.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.connection; +package de.fearnixx.jeak.service.http.connection; /** * Used to verify an authorization text. diff --git a/src/main/java/de/fearnixx/jeak/service/controller/connection/RestConfiguration.java b/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java similarity index 97% rename from src/main/java/de/fearnixx/jeak/service/controller/connection/RestConfiguration.java rename to src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java index e9d4e192..8ea23081 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/connection/RestConfiguration.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.connection; +package de.fearnixx.jeak.service.http.connection; import de.fearnixx.jeak.reflect.Config; import de.fearnixx.jeak.reflect.Inject; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java similarity index 97% rename from src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java rename to src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java index 2d995415..3daf60ab 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerContainer.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.controller; +package de.fearnixx.jeak.service.http.controller; import de.fearnixx.jeak.reflect.http.RequestMapping; import de.fearnixx.jeak.reflect.http.RestController; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerMethod.java b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerMethod.java similarity index 95% rename from src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerMethod.java rename to src/main/java/de/fearnixx/jeak/service/http/controller/ControllerMethod.java index 7120b8ef..3363cc58 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/ControllerMethod.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerMethod.java @@ -1,6 +1,6 @@ -package de.fearnixx.jeak.service.controller.controller; +package de.fearnixx.jeak.service.http.controller; -import de.fearnixx.jeak.service.controller.RequestMethod; +import de.fearnixx.jeak.service.http.RequestMethod; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/IncapableDummyAdapter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/IncapableDummyAdapter.java similarity index 83% rename from src/main/java/de/fearnixx/jeak/service/controller/controller/IncapableDummyAdapter.java rename to src/main/java/de/fearnixx/jeak/service/http/controller/IncapableDummyAdapter.java index 3e8d152e..5921b72c 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/IncapableDummyAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/IncapableDummyAdapter.java @@ -1,8 +1,8 @@ -package de.fearnixx.jeak.service.controller.controller; +package de.fearnixx.jeak.service.http.controller; import de.fearnixx.jeak.Main; -import de.fearnixx.jeak.service.controller.connection.HttpServer; -import de.fearnixx.jeak.service.controller.connection.RestConfiguration; +import de.fearnixx.jeak.service.http.connection.HttpServer; +import de.fearnixx.jeak.service.http.connection.RestConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/MethodParameter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java similarity index 97% rename from src/main/java/de/fearnixx/jeak/service/controller/controller/MethodParameter.java rename to src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java index fd30f85f..df041817 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/MethodParameter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.controller; +package de.fearnixx.jeak.service.http.controller; import java.lang.annotation.Annotation; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java similarity index 95% rename from src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java rename to src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java index e2877da3..6d9e6e4b 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/controller/SparkAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java @@ -1,15 +1,15 @@ -package de.fearnixx.jeak.service.controller.controller; +package de.fearnixx.jeak.service.http.controller; import de.fearnixx.jeak.Main; import de.fearnixx.jeak.reflect.http.PathParam; import de.fearnixx.jeak.reflect.http.RequestBody; import de.fearnixx.jeak.reflect.http.RequestMapping; import de.fearnixx.jeak.reflect.http.RequestParam; -import de.fearnixx.jeak.service.controller.RequestMethod; -import de.fearnixx.jeak.service.controller.ResponseEntity; -import de.fearnixx.jeak.service.controller.connection.HttpServer; -import de.fearnixx.jeak.service.controller.connection.IConnectionVerifier; -import de.fearnixx.jeak.service.controller.connection.RestConfiguration; +import de.fearnixx.jeak.service.http.RequestMethod; +import de.fearnixx.jeak.service.http.ResponseEntity; +import de.fearnixx.jeak.service.http.connection.HttpServer; +import de.fearnixx.jeak.service.http.connection.IConnectionVerifier; +import de.fearnixx.jeak.service.http.connection.RestConfiguration; import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java similarity index 86% rename from src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java rename to src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java index ff8a6000..452464ad 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/AuthenticationToken.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java @@ -1,6 +1,6 @@ -package de.fearnixx.jeak.service.controller.request.auth.token; +package de.fearnixx.jeak.service.http.request.auth.token; -import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; +import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; import de.fearnixx.jeak.teamspeak.data.IUser; import javax.annotation.Nonnull; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java similarity index 93% rename from src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java rename to src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java index 98349fb1..8c569e57 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/request/auth/token/TokenAuthService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java @@ -1,13 +1,11 @@ -package de.fearnixx.jeak.service.controller.request.auth.token; +package de.fearnixx.jeak.service.http.request.auth.token; import de.fearnixx.jeak.event.bot.IBotStateEvent; -import de.fearnixx.jeak.profile.IUserIdentity; -import de.fearnixx.jeak.profile.event.IProfileEvent; import de.fearnixx.jeak.reflect.Config; import de.fearnixx.jeak.reflect.Inject; import de.fearnixx.jeak.reflect.Listener; -import de.fearnixx.jeak.service.controller.request.token.IAuthenticationToken; -import de.fearnixx.jeak.service.controller.request.token.ITokenAuthService; +import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; +import de.fearnixx.jeak.service.http.request.token.ITokenAuthService; import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.service.teamspeak.IUserService; import de.fearnixx.jeak.teamspeak.data.IUser; @@ -21,7 +19,6 @@ import javax.annotation.Nullable; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Collection; import java.util.Collections; import java.util.Objects; import java.util.UUID; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/DummyObject.java b/src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java similarity index 90% rename from src/main/java/de/fearnixx/jeak/service/controller/testImpls/DummyObject.java rename to src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java index f95cb268..e1a6e7f6 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/DummyObject.java +++ b/src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.controller.testImpls; +package de.fearnixx.jeak.service.http.testImpls; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java b/src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java similarity index 88% rename from src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java rename to src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java index faf58138..3ffce148 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/SecondTestController.java +++ b/src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java @@ -1,10 +1,10 @@ -package de.fearnixx.jeak.service.controller.testImpls; +package de.fearnixx.jeak.service.http.testImpls; import de.fearnixx.jeak.reflect.http.RequestBody; import de.fearnixx.jeak.reflect.http.RequestMapping; import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.reflect.http.RestController; -import de.fearnixx.jeak.service.controller.RequestMethod; +import de.fearnixx.jeak.service.http.RequestMethod; @RestController(endpoint = "/test", pluginId = "testPluginId") public class SecondTestController { diff --git a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java b/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java similarity index 84% rename from src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java rename to src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java index 4876eddc..371e1d48 100644 --- a/src/main/java/de/fearnixx/jeak/service/controller/testImpls/TestController.java +++ b/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java @@ -1,12 +1,12 @@ -package de.fearnixx.jeak.service.controller.testImpls; +package de.fearnixx.jeak.service.http.testImpls; import de.fearnixx.jeak.reflect.http.RequestBody; import de.fearnixx.jeak.reflect.http.RequestMapping; import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.reflect.http.RestController; -import de.fearnixx.jeak.service.controller.IResponseEntity; -import de.fearnixx.jeak.service.controller.RequestMethod; -import de.fearnixx.jeak.service.controller.ResponseEntity; +import de.fearnixx.jeak.service.http.IResponseEntity; +import de.fearnixx.jeak.service.http.RequestMethod; +import de.fearnixx.jeak.service.http.ResponseEntity; import org.eclipse.jetty.http.HttpStatus; @RestController(pluginId = "testPluginId", endpoint = "/test") diff --git a/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java b/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java index 761ee979..1fa95b4e 100644 --- a/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java +++ b/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java @@ -4,17 +4,17 @@ import de.fearnixx.jeak.reflect.Inject; import de.fearnixx.jeak.reflect.JeakBotPlugin; import de.fearnixx.jeak.reflect.Listener; -import de.fearnixx.jeak.service.controller.IRestControllerService; -import de.fearnixx.jeak.service.controller.exceptions.RegisterControllerException; -import de.fearnixx.jeak.service.controller.testImpls.SecondTestController; -import de.fearnixx.jeak.service.controller.testImpls.TestController; +import de.fearnixx.jeak.service.http.IControllerService; +import de.fearnixx.jeak.service.http.exceptions.RegisterControllerException; +import de.fearnixx.jeak.service.http.testImpls.SecondTestController; +import de.fearnixx.jeak.service.http.testImpls.TestController; import de.fearnixx.jeak.test.AbstractTestPlugin; @JeakBotPlugin(id = "controllertestplugin") public class ControllerTestPlugin extends AbstractTestPlugin { @Inject - IRestControllerService restControllerService; + IControllerService restControllerService; public ControllerTestPlugin() { From ee3c049896b0252d28f4e31ef803c132aa52c277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 13:06:15 +0200 Subject: [PATCH 05/13] Implement request context parameter resolving --- .../jeak/reflect/http/RequestContext.java | 10 ++-- .../service/http/request/IRequestContext.java | 12 +++- .../service/http/connection/HttpServer.java | 3 + .../http/controller/MethodParameter.java | 5 +- .../service/http/controller/SparkAdapter.java | 57 ++++++++++++++++--- 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java index 69085b2f..6c90595b 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java @@ -2,6 +2,7 @@ import de.fearnixx.jeak.service.http.request.IRequestContext; +import javax.annotation.Nonnull; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -10,8 +11,7 @@ /** * Denotes a parameter to be filled from the request context. * - * @apiNote The parameter injection will perform type casting within the bounds of class assignability compatibility. - * It will also resolve {@link java.util.Optional} but (due to type erasure) cannot ensure the generic contract at runtime unless the optHint parameter is set. + * @apiNote The parameter injection will work within the bounds of class assignability compatibility. * * @implNote Some usages of this cause side-effects, please see the implementation notes on {@link IRequestContext.Attributes} */ @@ -24,10 +24,12 @@ * If none, the parameter injection will attempt to insert the {@link IRequestContext} instance. * @see IRequestContext.Attributes for more information on available attributes. */ + @Nonnull String attribute() default ""; /** - * For parameters of {@link java.util.Optional} type, this param can be specified to re-enable type compatibility checks. + * Whether or not this parameter has to be set. + * Unset, non-required values are passed as {@code null}. */ - Class optHint() default Object.class; + boolean required() default true; } diff --git a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java index 03097a70..385a51e3 100644 --- a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java @@ -14,12 +14,20 @@ public interface IRequestContext { Optional optAttribute(String name, Class hint); final class Attributes { + + /** + * {@link IRequestContext} + * + * @apiNote Self-reference, mainly for internal purposes. + */ + public static final String REQUEST_CONTEXT = "self"; + /** * {@link IAuthenticationToken} * * @apiNote Optionally filled, if authentication is successful AND the "Token" authentication scheme is used. * - * @implNote Required (non-optional) parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication. + * @implNote Required parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication. */ public static final String AUTHENTICATION_TOKEN = "auth:token:authenticationToken"; @@ -27,7 +35,7 @@ final class Attributes { * {@link de.fearnixx.jeak.teamspeak.data.IUser} * * @apiNote Optionally filled, if authentication is successful AND the subject is an user. - * @implNote Required (non-optional) parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication or {@link HttpStatus#FORBIDDEN_403} for principals that aren't users. + * @implNote Required parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication or {@link HttpStatus#FORBIDDEN_403} for principals that aren't users. */ public static final String AUTHENTICATION_USER = "auth:subject:authenticatedUser"; diff --git a/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java index 6b75806e..eb70dc5d 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java @@ -5,10 +5,13 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import de.fearnixx.jeak.reflect.http.PathParam; +import de.fearnixx.jeak.reflect.http.RequestContext; import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.service.http.controller.ControllerContainer; import de.fearnixx.jeak.service.http.controller.ControllerMethod; import de.fearnixx.jeak.service.http.controller.MethodParameter; +import de.fearnixx.jeak.service.http.request.IRequestContext; +import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.Request; diff --git a/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java index df041817..fcfa907c 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/MethodParameter.java @@ -48,10 +48,11 @@ public boolean hasAnnotation(Class clazz) { * @return The {@link Annotation} if the parameter is marked by the provided annotation, * {@code Optional.empty()} otherwise. */ - public Optional getAnnotation(Class clazz) { + public Optional getAnnotation(Class clazz) { return annotations.stream() .filter(o -> o.annotationType().equals(clazz)) - .findFirst(); + .findFirst() + .map(clazz::cast); } /** diff --git a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java index 6d9e6e4b..289fbc75 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java @@ -1,26 +1,25 @@ package de.fearnixx.jeak.service.http.controller; import de.fearnixx.jeak.Main; -import de.fearnixx.jeak.reflect.http.PathParam; -import de.fearnixx.jeak.reflect.http.RequestBody; -import de.fearnixx.jeak.reflect.http.RequestMapping; -import de.fearnixx.jeak.reflect.http.RequestParam; +import de.fearnixx.jeak.reflect.http.*; import de.fearnixx.jeak.service.http.RequestMethod; import de.fearnixx.jeak.service.http.ResponseEntity; import de.fearnixx.jeak.service.http.connection.HttpServer; import de.fearnixx.jeak.service.http.connection.IConnectionVerifier; import de.fearnixx.jeak.service.http.connection.RestConfiguration; +import de.fearnixx.jeak.service.http.request.IRequestContext; import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import spark.Request; -import spark.Response; -import spark.Route; -import spark.Service; +import spark.*; +import javax.transaction.UserTransaction; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; + +import static de.fearnixx.jeak.service.http.request.IRequestContext.*; public class SparkAdapter extends HttpServer { @@ -157,6 +156,8 @@ private Object[] extractParameters(List methodParameterList, Re retrievedParameter = transformRequestOption(request.queryMap(getRequestParamName(methodParameter)).value(), request, methodParameter); } else if (methodParameter.hasAnnotation(RequestBody.class)) { retrievedParameter = transformRequestOption(request.body(), request, methodParameter); + } else if (methodParameter.hasAnnotation(RequestContext.class)) { + retrievedParameter = transformRequestContext(request, methodParameter); } methodParameters[methodParameter.getPosition()] = retrievedParameter; } @@ -164,6 +165,46 @@ private Object[] extractParameters(List methodParameterList, Re return methodParameters; } + protected Object transformRequestContext(Request request, MethodParameter methodParameter) { + RequestContext annotation = methodParameter.getAnnotation(RequestContext.class).orElseThrow(); + var attributeIdent = annotation.attribute(); + + if (attributeIdent.isBlank()) { + return request.attribute(IRequestContext.Attributes.REQUEST_CONTEXT); + } else { + var storedValue = request.attribute(attributeIdent); + + if (annotation.required() && storedValue == null) { + switch (attributeIdent) { + case IRequestContext.Attributes.AUTHENTICATION_USER: + if (request.attribute(IRequestContext.Attributes.AUTHENTICATION_TOKEN) != null) { + // #halt throws RuntimeException! + service.halt(HttpStatus.FORBIDDEN_403, "Only users are allowed to use this endpoint."); + } else { + // #halt throws RuntimeException! + service.halt(HttpStatus.UNAUTHORIZED_401); + } + return null; + case IRequestContext.Attributes.AUTHENTICATION_TOKEN: + // #halt throws RuntimeException! + service.halt(HttpStatus.UNAUTHORIZED_401); + return null; + default: + var msg = String.format("Required context attribute \"%s\" is unset!", attributeIdent); + throw new IllegalStateException(msg); + } + } else if (storedValue == null) { + return null; + } else { + if (!methodParameter.getType().isAssignableFrom(storedValue.getClass())) { + var msg = String.format("Context attribute is not compatible with parameter type: \"%s\" vs. \"%s\"", storedValue.getClass(), methodParameter.getType()); + throw new IllegalStateException(msg); + } + return storedValue; + } + } + } + private void addBeforeHandlingCheck(String path, ControllerMethod controllerMethod) { service.before(path, (request, response) -> { if (controllerMethod.getAnnotation(RequestMapping.class).isSecured()) { From f6adadc5b2428e15e105edafb087c3a46443f341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 13:08:58 +0200 Subject: [PATCH 06/13] Update bugfix version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bf5b1166..9c6105cf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'de.fearnixx' -version '1.1.2' +version '1.1.3' def projectVersion = project.version apply plugin: 'java' From da05e5ded6077295ccf55e1cc31b15d4221f48a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 13:19:14 +0200 Subject: [PATCH 07/13] Remove javax.annotation --- .../java/de/fearnixx/jeak/reflect/http/RequestContext.java | 4 +--- .../fearnixx/jeak/service/http/request/IRequestContext.java | 5 ++--- .../jeak/service/http/request/token/ITokenAuthService.java | 5 +---- .../service/http/request/auth/token/AuthenticationToken.java | 4 +--- .../service/http/request/auth/token/TokenAuthService.java | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java index 6c90595b..01514a31 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java @@ -2,7 +2,6 @@ import de.fearnixx.jeak.service.http.request.IRequestContext; -import javax.annotation.Nonnull; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -12,7 +11,6 @@ * Denotes a parameter to be filled from the request context. * * @apiNote The parameter injection will work within the bounds of class assignability compatibility. - * * @implNote Some usages of this cause side-effects, please see the implementation notes on {@link IRequestContext.Attributes} */ @Target(ElementType.PARAMETER) @@ -22,9 +20,9 @@ /** * Denotes the attribute name to be used for the lookup. * If none, the parameter injection will attempt to insert the {@link IRequestContext} instance. + * * @see IRequestContext.Attributes for more information on available attributes. */ - @Nonnull String attribute() default ""; /** diff --git a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java index 385a51e3..dfbf4188 100644 --- a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java @@ -1,7 +1,6 @@ package de.fearnixx.jeak.service.http.request; import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; -import org.eclipse.jetty.http.HttpStatus; import java.util.Optional; @@ -27,7 +26,7 @@ final class Attributes { * * @apiNote Optionally filled, if authentication is successful AND the "Token" authentication scheme is used. * - * @implNote Required parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication. + * @implNote Required parameter injections cause {@link org.eclipse.jetty.http.HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication. */ public static final String AUTHENTICATION_TOKEN = "auth:token:authenticationToken"; @@ -35,7 +34,7 @@ final class Attributes { * {@link de.fearnixx.jeak.teamspeak.data.IUser} * * @apiNote Optionally filled, if authentication is successful AND the subject is an user. - * @implNote Required parameter injections cause {@link HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication or {@link HttpStatus#FORBIDDEN_403} for principals that aren't users. + * @implNote Required parameter injections cause {@link org.eclipse.jetty.http.HttpStatus#UNAUTHORIZED_401} on unsuccessful authentication or {@link org.eclipse.jetty.http.HttpStatus#FORBIDDEN_403} for principals that aren't users. */ public static final String AUTHENTICATION_USER = "auth:subject:authenticatedUser"; diff --git a/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java index cd65d81d..2642b295 100644 --- a/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java @@ -3,16 +3,13 @@ import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.teamspeak.data.IUser; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.time.ZonedDateTime; public interface ITokenAuthService { - @Nonnull IAuthenticationToken generateToken(IUser subject); - void setTokenExpiry(@Nonnull IAuthenticationToken token, @Nullable ZonedDateTime expiryValue); + void setTokenExpiry(IAuthenticationToken token, ZonedDateTime expiryValue); void revokeToken(IAuthenticationToken token); diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java index 452464ad..2703b899 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/AuthenticationToken.java @@ -3,8 +3,6 @@ import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; import de.fearnixx.jeak.teamspeak.data.IUser; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.time.ZonedDateTime; import java.util.Optional; @@ -14,7 +12,7 @@ public class AuthenticationToken implements IAuthenticationToken { private final IUser tokenOwner; private final ZonedDateTime tokenExpiry; - public AuthenticationToken(@Nonnull String tokenStr, @Nonnull IUser tokenOwner, @Nullable ZonedDateTime tokenExpiry) { + public AuthenticationToken(String tokenStr, IUser tokenOwner, ZonedDateTime tokenExpiry) { this.tokenStr = tokenStr; this.tokenOwner = tokenOwner; this.tokenExpiry = tokenExpiry; diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java index 8c569e57..7a3688cd 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java @@ -66,7 +66,7 @@ public synchronized IAuthenticationToken generateToken(IUser tokenOwner) { } @Override - public synchronized void setTokenExpiry(@Nonnull IAuthenticationToken token, @Nullable ZonedDateTime expiryValue) { + public synchronized void setTokenExpiry(@Nonnull IAuthenticationToken token, ZonedDateTime expiryValue) { Objects.requireNonNull(token, "Provided token cannot be null."); var tokenNode = getTokensNode().getNode(token.getTokenString()); From 50020193407a21533c268f81b0d2ef388c209562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 14:12:51 +0200 Subject: [PATCH 08/13] Implement request authentication via tokens --- .../jeak/reflect/http/RequestMapping.java | 18 ++++- .../service/http/request/IRequestContext.java | 2 +- .../http/request/token/ITokenAuthService.java | 5 +- .../jeak/service/http/ControllerService.java | 13 ++-- .../http/connection/RestConfiguration.java | 4 +- .../service/http/controller/SparkAdapter.java | 53 ++++++------- .../http/request/SparkRequestContext.java | 28 +++++++ .../request/auth/token/TokenAuthService.java | 76 ++++++++++++++++--- .../http/testImpls/TestController.java | 2 +- 9 files changed, 148 insertions(+), 53 deletions(-) create mode 100644 src/main/java/de/fearnixx/jeak/service/http/request/SparkRequestContext.java diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java index 65b066d5..b9ca02ff 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java @@ -14,13 +14,25 @@ * * endpoint(): REQUIRED Specify the endpoint for the annotated method. * - * isSecured(): Specify whether the calls to this endpoint should use an authorization scheme. + * requireAuth(): Specify whether the calls to this endpoint should use an authorization scheme. * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestMapping { - RequestMethod method(); + + /** + * The HTTP method associated with this endpoint. + */ + RequestMethod method() default RequestMethod.GET; + + /** + * URI appendix for this endpoint. + */ String endpoint(); - boolean isSecured() default true; + + /** + * Whether or not request against this endpoint MUST BE authenticated. + */ + boolean requireAuth() default true; } diff --git a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java index dfbf4188..e98500f3 100644 --- a/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/IRequestContext.java @@ -19,7 +19,7 @@ final class Attributes { * * @apiNote Self-reference, mainly for internal purposes. */ - public static final String REQUEST_CONTEXT = "self"; + public static final String REQUEST_CONTEXT = "http:requestContext"; /** * {@link IAuthenticationToken} diff --git a/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java index 2642b295..7b283b28 100644 --- a/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java +++ b/src/api/java/de/fearnixx/jeak/service/http/request/token/ITokenAuthService.java @@ -1,17 +1,16 @@ package de.fearnixx.jeak.service.http.request.token; -import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.teamspeak.data.IUser; import java.time.ZonedDateTime; public interface ITokenAuthService { - IAuthenticationToken generateToken(IUser subject); + IAuthenticationToken generateToken(IUser tokenOwner); void setTokenExpiry(IAuthenticationToken token, ZonedDateTime expiryValue); void revokeToken(IAuthenticationToken token); - void revokeTokens(ISubject subject); + void revokeTokens(IUser tokenOwner); } diff --git a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java index f9d5f1f3..22845e20 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java @@ -10,6 +10,7 @@ import de.fearnixx.jeak.service.http.controller.IncapableDummyAdapter; import de.fearnixx.jeak.service.http.controller.SparkAdapter; import de.fearnixx.jeak.service.http.exceptions.RegisterControllerException; +import de.fearnixx.jeak.service.http.request.auth.token.TokenAuthService; import java.util.HashMap; import java.util.Map; @@ -20,9 +21,9 @@ public class ControllerService implements IControllerService { private final Map, Object> controllers; - private HttpServer httpServer; - private ControllerRequestVerifier connectionVerifier; - private RestConfiguration restConfiguration; + private final HttpServer httpServer; + private final TokenAuthService tokenAuthService; + private final RestConfiguration restConfiguration; @Inject private IInjectionService injectionService; @@ -32,17 +33,17 @@ public ControllerService() { } public ControllerService(Map, Object> controllers) { - this.connectionVerifier = new ControllerRequestVerifier(); + this.tokenAuthService = new TokenAuthService(); this.restConfiguration = new RestConfiguration(); this.controllers = controllers; this.httpServer = IncapableDummyAdapter.EXPERIMENTAL_REST_ENABLED ? - new SparkAdapter(connectionVerifier, restConfiguration) + new SparkAdapter(restConfiguration, tokenAuthService) : new IncapableDummyAdapter(restConfiguration); } @Listener public void onPreInt(IBotStateEvent.IPreInitializeEvent preInitializeEvent) { - injectionService.injectInto(connectionVerifier); + injectionService.injectInto(tokenAuthService); injectionService.injectInto(restConfiguration); restConfiguration.loadConfig(); injectionService.injectInto(httpServer); diff --git a/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java b/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java index c8197be6..ac90c9e2 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/RestConfiguration.java @@ -84,8 +84,8 @@ private Optional getValueFromHttpsConfig(String value, Class type) { return Optional.ofNullable(getConfig().getNode(HTTPS_CONFIG).optValueMap(type).get(value)); } - public Optional isHttpsEnabled() { - return getValueFromHttpsConfig(HTTPS_ENABLED, Boolean.class); + public boolean isHttpsEnabled() { + return getValueFromHttpsConfig(HTTPS_ENABLED, Boolean.class).orElse(false); } public Optional isBehindSslProxy() { diff --git a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java index eba70811..62628df5 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java @@ -5,22 +5,24 @@ import de.fearnixx.jeak.service.http.RequestMethod; import de.fearnixx.jeak.service.http.ResponseEntity; import de.fearnixx.jeak.service.http.connection.HttpServer; -import de.fearnixx.jeak.service.http.connection.IConnectionVerifier; import de.fearnixx.jeak.service.http.connection.RestConfiguration; -import de.fearnixx.jeak.service.http.controller.MethodParameter; import de.fearnixx.jeak.service.http.request.IRequestContext; +import de.fearnixx.jeak.service.http.request.SparkRequestContext; +import de.fearnixx.jeak.service.http.request.auth.token.TokenAuthService; import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import spark.*; +import spark.Request; +import spark.Response; +import spark.Route; +import spark.Service; -import javax.transaction.UserTransaction; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import static de.fearnixx.jeak.service.http.request.IRequestContext.*; +import static de.fearnixx.jeak.service.http.request.IRequestContext.Attributes; public class SparkAdapter extends HttpServer { @@ -29,11 +31,12 @@ public class SparkAdapter extends HttpServer { public static final int MAX_THREADS = Main.getProperty("jeak.sparkadapter.maxpoolsize", 8); public static final int MIN_THREADS = Main.getProperty("jeak.sparkadapter.minpoolsize", 3); public static final int TIMEOUT_MILLIS = 30000; - private IConnectionVerifier connectionVerifier; + + private final TokenAuthService tokenAuthService; private Map headers; private Service service; - public SparkAdapter(IConnectionVerifier connectionVerifier, RestConfiguration restConfiguration) { + public SparkAdapter(RestConfiguration restConfiguration, TokenAuthService tokenAuthService) { super(restConfiguration); service = Service.ignite(); // only use NUM_THREADS, if it was configured @@ -42,7 +45,7 @@ public SparkAdapter(IConnectionVerifier connectionVerifier, RestConfiguration re } else { service.threadPool(MAX_THREADS, MIN_THREADS, TIMEOUT_MILLIS); } - this.connectionVerifier = connectionVerifier; + this.tokenAuthService = tokenAuthService; } /** @@ -210,19 +213,20 @@ private void addBeforeHandlingCheck(String path, ControllerContainer controllerC service.before(path, (request, response) -> { controllerContainer.getAnnotation(RestController.class).ifPresent(restController -> { if (getRestConfiguration().rejectUnencryptedTraffic().orElse(RestConfiguration.DEFAULT_HTTPS_REJECT_UNENCRYPTED) && !isProtocolHttps(request)) { - logger.debug("HTTPS enforcement enabled, non HTTPS request for {} blocked", path); + logger.info("HTTPS enforcement enabled, non HTTPS request for {} blocked", path); service.halt(426, "{\"errors\": [\"Use of HTTPS is mandatory for this endpoint\"]}"); } }); - controllerMethod.getAnnotation(RequestMapping.class).ifPresent(requestMapping -> { - if (requestMapping.isSecured()) { - boolean isAuthorized = connectionVerifier.verifyRequest(path, request.headers("Authorization")); - if (!isAuthorized) { - logger.debug("Authorization for Request to {} failed", path); - service.halt(401); - } + + if (!tokenAuthService.attemptAuthentication(request)) { + var mapping = controllerMethod.getAnnotation(RequestMapping.class); + if (mapping.isPresent() && mapping.get().requireAuth()) { + logger.info("Unauthenticated HTTP request blocked."); + service.halt(HttpStatus.UNAUTHORIZED_401, "{\"errors\": [\"Authentication is mandatory for this endpoint!\"]"); } - }); + } + + request.attribute(Attributes.REQUEST_CONTEXT, new SparkRequestContext(request)); }); } @@ -237,15 +241,12 @@ private boolean isProtocolHttps(Request request) { @Override public void start() { getRestConfiguration().getPort().ifPresent(service::port); - getRestConfiguration().isHttpsEnabled().ifPresent(isHttpsEnabled -> { - if (Boolean.TRUE.equals(isHttpsEnabled)) { - logger.info("Https enabled"); - initHttps(); - } else { - logger.info("HTTPS disabled"); - } - }); - + if (getRestConfiguration().isHttpsEnabled()) { + logger.info("HTTPS enabled"); + initHttps(); + } else { + logger.info("HTTPS disabled"); + } } private void initHttps() { diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/SparkRequestContext.java b/src/main/java/de/fearnixx/jeak/service/http/request/SparkRequestContext.java new file mode 100644 index 00000000..ae2d81c5 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/http/request/SparkRequestContext.java @@ -0,0 +1,28 @@ +package de.fearnixx.jeak.service.http.request; + +import spark.Request; + +import java.util.Objects; +import java.util.Optional; + +public class SparkRequestContext implements IRequestContext { + + private final Request sparkRequest; + + public SparkRequestContext(Request sparkRequest) { + this.sparkRequest = sparkRequest; + } + + @Override + public Optional optAttribute(String name, Class hint) { + Objects.requireNonNull(name, "Attribute name may not be null!"); + Objects.requireNonNull(hint, "Attribute type hint may not be null!"); + var sparkValue = sparkRequest.attribute(name); + + if (sparkValue == null || !hint.isAssignableFrom(sparkValue.getClass())) { + return Optional.empty(); + } else { + return Optional.of(hint.cast(sparkValue)); + } + } +} diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java index 7a3688cd..d7929bb7 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java @@ -4,9 +4,9 @@ import de.fearnixx.jeak.reflect.Config; import de.fearnixx.jeak.reflect.Inject; import de.fearnixx.jeak.reflect.Listener; +import de.fearnixx.jeak.service.http.request.IRequestContext; import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; import de.fearnixx.jeak.service.http.request.token.ITokenAuthService; -import de.fearnixx.jeak.service.permission.base.ISubject; import de.fearnixx.jeak.service.teamspeak.IUserService; import de.fearnixx.jeak.teamspeak.data.IUser; import de.fearnixx.jeak.util.Configurable; @@ -14,14 +14,15 @@ import de.mlessmann.confort.api.IConfigNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spark.Request; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Collections; import java.util.Objects; import java.util.UUID; +import java.util.regex.Pattern; public class TokenAuthService extends Configurable implements ITokenAuthService { @@ -30,6 +31,8 @@ public class TokenAuthService extends Configurable implements ITokenAuthService public static final String EXPIRY_NODE_NAME = "expiry"; public static final DateTimeFormatter EXPIRY_FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + private static final Pattern HEADER_EXTRACTION_PATTERN = Pattern.compile("Token (.+)$"); + @Inject @Config(category = "rest", id = "token-auth") private IConfig tokenConfig; @@ -50,14 +53,65 @@ protected IConfigNode getTokensNode() { return getConfig().getNode("tokens"); } - @Nonnull + public synchronized boolean attemptAuthentication(Request request) { + var authHeader = request.headers("Authorization"); + if (authHeader == null || authHeader.isBlank()) { + logger.debug("Request without Auth-Header."); + return false; + } + + var matcher = HEADER_EXTRACTION_PATTERN.matcher(authHeader); + if (!matcher.find()) { + logger.debug("Token scheme not found in header: {}", authHeader); + return false; + } + + String token = matcher.group(1); + var optTokenMapEntry = getTokensNode().optMap() + .orElseGet(Collections::emptyMap) + .entrySet() + .stream() + .filter(pair -> !pair.getValue().getNode(token).isVirtual()) + .findAny(); + if (optTokenMapEntry.isEmpty()) { + logger.debug("No match found for token: {}", token); + return false; + } + + String userUID = optTokenMapEntry.get().getKey(); + var tokenExpiryStr = optTokenMapEntry.get().getValue().getNode(token, EXPIRY_NODE_NAME).optString(""); + ZonedDateTime expiry = null; + if (!NO_EXPIRY_VALUE.equals(tokenExpiryStr)) { + try { + expiry = ZonedDateTime.parse(tokenExpiryStr, EXPIRY_FORMAT); + if (ZonedDateTime.now().isAfter(expiry)) { + logger.warn("Access with expired token: '{}' -> '{}'.", userUID, token); + return false; + } + + } catch (DateTimeParseException e) { + logger.warn("Cannot read expiry value of token: '{}' -> '{}'", userUID, token, e); + return false; + } + } + + var user = userService.findUserByUniqueID(userUID) + .stream() + .findFirst() + .orElseThrow(() -> new IllegalStateException("Token owner of token " + token + " could not be found! (" + userUID + ")")); + AuthenticationToken tokenInstance = new AuthenticationToken(token, user, expiry); + request.attribute(IRequestContext.Attributes.AUTHENTICATION_TOKEN, tokenInstance); + request.attribute(IRequestContext.Attributes.AUTHENTICATION_USER, user); + return true; + } + @Override public synchronized IAuthenticationToken generateToken(IUser tokenOwner) { Objects.requireNonNull(tokenOwner, "Token owner may not be null!"); var tokenStr = UUID.randomUUID().toString().replace("-", ""); var tokenInstance = new AuthenticationToken(tokenStr, tokenOwner, null); - var ownerNode = getTokensNode().getNode(tokenOwner.getUniqueID().toString()); + var ownerNode = getTokensNode().getNode(tokenOwner.getClientUniqueID()); var tokenNode = ownerNode.getNode(tokenStr); tokenNode.getNode(EXPIRY_NODE_NAME).setString(NO_EXPIRY_VALUE); @@ -66,7 +120,7 @@ public synchronized IAuthenticationToken generateToken(IUser tokenOwner) { } @Override - public synchronized void setTokenExpiry(@Nonnull IAuthenticationToken token, ZonedDateTime expiryValue) { + public synchronized void setTokenExpiry(IAuthenticationToken token, ZonedDateTime expiryValue) { Objects.requireNonNull(token, "Provided token cannot be null."); var tokenNode = getTokensNode().getNode(token.getTokenString()); @@ -102,13 +156,13 @@ public synchronized void revokeToken(IAuthenticationToken token) { } @Override - public synchronized void revokeTokens(ISubject subject) { - revokeTokens(subject.getUniqueID().toString()); + public synchronized void revokeTokens(IUser tokenOwner) { + revokeTokens(tokenOwner.getClientUniqueID()); } - protected synchronized void revokeTokens(String subjectUUID) { - logger.info("Revoking tokens of subject '{}'", subjectUUID); - getTokensNode().remove(subjectUUID); + protected synchronized void revokeTokens(String ts3uid) { + logger.info("Revoking tokens of subject '{}'", ts3uid); + getTokensNode().remove(ts3uid); } @Listener(order = Listener.Orders.EARLY) diff --git a/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java b/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java index 52c97c13..63696daa 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java +++ b/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java @@ -30,7 +30,7 @@ public String sendBody(@RequestBody String string) { return "this is the body " + string; } - @RequestMapping(method = RequestMethod.GET, endpoint = "/int", isSecured = false) + @RequestMapping(method = RequestMethod.GET, endpoint = "/int", requireAuth = false) public String sendStuff(@RequestParam(name = "num", type = Integer.class) Integer num) { return "received" + num; } From e76a3346b8cf4b5347df77411fc1881cd6016002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 14:19:07 +0200 Subject: [PATCH 09/13] Replace previous authentication mechanism --- .../request/auth}/token/RandomString.java | 2 +- .../request/auth/token/TokenAuthService.java | 8 +- .../jeak/service/token/ITokenService.java | 32 ------ .../service/token/TokenConfiguration.java | 106 ------------------ .../jeak/service/token/TokenScope.java | 52 --------- .../jeak/service/token/TokenService.java | 62 ---------- .../restService/token/defaultToken.json | 7 -- 7 files changed, 7 insertions(+), 262 deletions(-) rename src/main/java/de/fearnixx/jeak/service/{ => http/request/auth}/token/RandomString.java (96%) delete mode 100644 src/main/java/de/fearnixx/jeak/service/token/ITokenService.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/token/TokenConfiguration.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/token/TokenScope.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/token/TokenService.java delete mode 100644 src/main/resources/restService/token/defaultToken.json diff --git a/src/main/java/de/fearnixx/jeak/service/token/RandomString.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/RandomString.java similarity index 96% rename from src/main/java/de/fearnixx/jeak/service/token/RandomString.java rename to src/main/java/de/fearnixx/jeak/service/http/request/auth/token/RandomString.java index e3d458e8..c73dfe78 100644 --- a/src/main/java/de/fearnixx/jeak/service/token/RandomString.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/RandomString.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.token; +package de.fearnixx.jeak.service.http.request.auth.token; import java.security.SecureRandom; import java.util.Locale; diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java index d7929bb7..44e64ad7 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java @@ -1,5 +1,6 @@ package de.fearnixx.jeak.service.http.request.auth.token; +import de.fearnixx.jeak.Main; import de.fearnixx.jeak.event.bot.IBotStateEvent; import de.fearnixx.jeak.reflect.Config; import de.fearnixx.jeak.reflect.Inject; @@ -21,18 +22,21 @@ import java.time.format.DateTimeParseException; import java.util.Collections; import java.util.Objects; -import java.util.UUID; import java.util.regex.Pattern; public class TokenAuthService extends Configurable implements ITokenAuthService { private static final Logger logger = LoggerFactory.getLogger(TokenAuthService.class); + private static final int TOKEN_LENGTH = Main.getProperty("jeak.http.auth.token_length", 128); + private static final String NO_EXPIRY_VALUE = "never"; public static final String EXPIRY_NODE_NAME = "expiry"; public static final DateTimeFormatter EXPIRY_FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME; private static final Pattern HEADER_EXTRACTION_PATTERN = Pattern.compile("Token (.+)$"); + private final RandomString tokenGenerator = new RandomString(TOKEN_LENGTH); + @Inject @Config(category = "rest", id = "token-auth") private IConfig tokenConfig; @@ -108,7 +112,7 @@ public synchronized boolean attemptAuthentication(Request request) { @Override public synchronized IAuthenticationToken generateToken(IUser tokenOwner) { Objects.requireNonNull(tokenOwner, "Token owner may not be null!"); - var tokenStr = UUID.randomUUID().toString().replace("-", ""); + var tokenStr = tokenGenerator.nextString(); var tokenInstance = new AuthenticationToken(tokenStr, tokenOwner, null); var ownerNode = getTokensNode().getNode(tokenOwner.getClientUniqueID()); diff --git a/src/main/java/de/fearnixx/jeak/service/token/ITokenService.java b/src/main/java/de/fearnixx/jeak/service/token/ITokenService.java deleted file mode 100644 index aaea392c..00000000 --- a/src/main/java/de/fearnixx/jeak/service/token/ITokenService.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.fearnixx.jeak.service.token; - -import java.util.Set; - -public interface ITokenService { - /** - * Verify a given token. - * - * @param endpoint The endpoint to verify the token for. - * @param token The token as {@link String} to verify. - * @return true if the token could be verified, - * false otherwise - */ - boolean verifyToken(String endpoint, String token); - - /** - * Generate a new token for the verification of requests. - * - * @param endpointSet The endpoints to register the token for. The List needs to have at least one item. - * @return The generated token. - */ - String generateToken(Set endpointSet); - - /** - * Revoke a token. - * - * @param token The token to be revoked. - * @return true if the token was successfully revoked, - * false otherwise - */ - boolean revokeToken(String token); -} diff --git a/src/main/java/de/fearnixx/jeak/service/token/TokenConfiguration.java b/src/main/java/de/fearnixx/jeak/service/token/TokenConfiguration.java deleted file mode 100644 index 5d3c6a70..00000000 --- a/src/main/java/de/fearnixx/jeak/service/token/TokenConfiguration.java +++ /dev/null @@ -1,106 +0,0 @@ -package de.fearnixx.jeak.service.token; - -import de.fearnixx.jeak.reflect.Config; -import de.fearnixx.jeak.reflect.Inject; -import de.fearnixx.jeak.util.Configurable; -import de.mlessmann.confort.api.IConfig; -import de.mlessmann.confort.api.IConfigNode; -import de.mlessmann.confort.node.ConfigNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.MessageFormat; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -public class TokenConfiguration extends Configurable { - private static final Logger logger = LoggerFactory.getLogger(TokenConfiguration.class); - private static final String DEFAULT_TOKEN_CONFIG = "/restService/token/defaultToken.json"; - - @Inject - @Config(id = "tokens") - private IConfig configRef; - - public TokenConfiguration() { - super(TokenConfiguration.class); - } - - @Override - public boolean loadConfig() { - return super.loadConfig(); - } - - @Override - protected void onDefaultConfigLoaded() { - saveConfig(); - } - - @Override - protected IConfig getConfigRef() { - return configRef; - } - - @Override - protected String getDefaultResource() { - return DEFAULT_TOKEN_CONFIG; - } - - @Override - protected boolean populateDefaultConf(IConfigNode root) { - return false; - } - - /** - * Check for the {@link TokenScope} of a given token. - * - * @param token The token to find the scopes for. - * @return An instance of {@link TokenScope} with the scopes of the token. - */ - public TokenScope getTokenScopes(String token) { - logger.debug(MessageFormat.format("reading token {0}", token)); - IConfigNode tokenNode = getConfig().getNode(token); - Set tokenScopes = new HashSet<>(); - if (!tokenNode.isVirtual()) { - Optional> iConfigNodes = tokenNode.optList(); - iConfigNodes.ifPresent(localIConfigNodes -> - localIConfigNodes.stream() - .map(IConfigNode::optString) - .filter(Optional::isPresent) - .map(Optional::get) - .forEach(tokenScopes::add) - ); - } - return new TokenScope(tokenScopes); - } - - /** - * Save the provided token for the provided scope. - * - * @param token The token as {@link String} to save. - * @param tokenScope The scope as {@link TokenScope} the token is valid for. - */ - public void saveToken(String token, TokenScope tokenScope) { - ConfigNode child = new ConfigNode(); - child.setList(); - child.appendValue(tokenScope.getScopeSet()); - getConfig().put(token, child); - } - - /** - * Delete the provided token. Deleting a not existing token is considered a successful removal, - * since the token isn't existing after the {@link TokenConfiguration#deleteToken} method call. - * - * @param token The token as {@link String} to be deleted. - * @return true if the token was successfully deleted or doesn't exist, - * false otherwise - */ - public boolean deleteToken(String token) { - boolean deleteSuccessful = true; - if (!getConfig().getNode(token).isVirtual()) { - deleteSuccessful = getConfig().remove(token) != null; - } - return deleteSuccessful; - } -} diff --git a/src/main/java/de/fearnixx/jeak/service/token/TokenScope.java b/src/main/java/de/fearnixx/jeak/service/token/TokenScope.java deleted file mode 100644 index 158420e5..00000000 --- a/src/main/java/de/fearnixx/jeak/service/token/TokenScope.java +++ /dev/null @@ -1,52 +0,0 @@ -package de.fearnixx.jeak.service.token; - -import java.util.Arrays; -import java.util.Optional; -import java.util.Set; - -/** - * pluginId/ - * pluginId/Controller/ - * pluginId/Controller/method1 - */ -public class TokenScope { - private Set scopeSet; - - public TokenScope(Set scopeSet) { - this.scopeSet = scopeSet; - } - - /** - * - * @param scope - * @return - */ - public boolean isInScope(String scope) { - String[] splittedStrings = splitScope(scope); - for (int i = 0; i < splittedStrings.length; i++) { - Optional optionalShortenedScope = combineStrings(Arrays.copyOf(splittedStrings, splittedStrings.length - i)); - if (optionalShortenedScope.isPresent()) { - String localScope = optionalShortenedScope.get(); - if (scopeSet.contains(localScope)) { - return true; - } - } else { - return false; - } - } - return false; - } - - public Set getScopeSet() { - return scopeSet; - } - - private Optional combineStrings(String ... strings) { - return Arrays.stream(strings) - .reduce((s, s2) -> s+"/"+s2); - } - - private String[] splitScope(String scope) { - return scope.split("/"); - } -} diff --git a/src/main/java/de/fearnixx/jeak/service/token/TokenService.java b/src/main/java/de/fearnixx/jeak/service/token/TokenService.java deleted file mode 100644 index 646dec2b..00000000 --- a/src/main/java/de/fearnixx/jeak/service/token/TokenService.java +++ /dev/null @@ -1,62 +0,0 @@ -package de.fearnixx.jeak.service.token; - -import de.fearnixx.jeak.event.bot.IBotStateEvent; -import de.fearnixx.jeak.reflect.FrameworkService; -import de.fearnixx.jeak.reflect.IInjectionService; -import de.fearnixx.jeak.reflect.Inject; -import de.fearnixx.jeak.reflect.Listener; -import de.fearnixx.jeak.except.InvokationBeforeInitializationException; - -import java.util.Set; - -@FrameworkService(serviceInterface = ITokenService.class) -public class TokenService implements ITokenService { - - @Inject - private IInjectionService injectionService; - - private TokenConfiguration tokenConfiguration; - - @Override - public boolean verifyToken(String endpoint, String token) { - if (tokenConfiguration == null) { - throw new InvokationBeforeInitializationException("The TokenConfiguration is not initialized"); - } - boolean isVerified = false; - TokenScope tokenScopes = tokenConfiguration.getTokenScopes(token); - if (tokenScopes.isInScope(endpoint)) { - isVerified = true; - } - return isVerified; - } - - @Override - public String generateToken(Set endpointSet) { - if (tokenConfiguration == null) { - throw new InvokationBeforeInitializationException("The TokenConfiguration is not initialized"); - } - String token = createToken(); - tokenConfiguration.saveToken(token, new TokenScope(endpointSet)); - return token; - } - - @Override - public boolean revokeToken(String token) { - if (tokenConfiguration == null) { - throw new InvokationBeforeInitializationException("The TokenConfiguration is not initialized"); - } - return tokenConfiguration.deleteToken(token); - } - - @Listener - public void onPreInit(IBotStateEvent.IPreInitializeEvent preInitializeEvent) { - tokenConfiguration = new TokenConfiguration(); - injectionService.injectInto(tokenConfiguration); - tokenConfiguration.loadConfig(); - } - - private String createToken() { - RandomString tickets = new RandomString(30); - return tickets.nextString(); - } -} diff --git a/src/main/resources/restService/token/defaultToken.json b/src/main/resources/restService/token/defaultToken.json deleted file mode 100644 index 38f6e230..00000000 --- a/src/main/resources/restService/token/defaultToken.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Hallo": [ - "/api/testPluginId/test/hello", - "/api/testPluginId/test/info", - "/api/testPluginId/test/body" - ] -} \ No newline at end of file From 4a01ad55c3654ffceb2d8ecf68e5914f3b578a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 14:35:42 +0200 Subject: [PATCH 10/13] Fix compile errors IDEA did not tell me about --- src/main/java/de/fearnixx/jeak/JeakBot.java | 4 +-- .../jeak/service/http/ControllerService.java | 23 ++++++++++--- .../connection/ControllerRequestVerifier.java | 34 ------------------- .../http/connection/IConnectionVerifier.java | 17 ---------- 4 files changed, 20 insertions(+), 58 deletions(-) delete mode 100644 src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java diff --git a/src/main/java/de/fearnixx/jeak/JeakBot.java b/src/main/java/de/fearnixx/jeak/JeakBot.java index 63d9e9e0..b5bd67cd 100644 --- a/src/main/java/de/fearnixx/jeak/JeakBot.java +++ b/src/main/java/de/fearnixx/jeak/JeakBot.java @@ -13,9 +13,9 @@ import de.fearnixx.jeak.service.command.ICommandService; import de.fearnixx.jeak.service.command.TypedCommandService; import de.fearnixx.jeak.service.command.matcher.MatcherRegistry; -import de.fearnixx.jeak.service.http.ControllerService; import de.fearnixx.jeak.service.database.DatabaseService; import de.fearnixx.jeak.service.event.IEventService; +import de.fearnixx.jeak.service.http.ControllerService; import de.fearnixx.jeak.service.locale.LocalizationService; import de.fearnixx.jeak.service.mail.MailService; import de.fearnixx.jeak.service.notification.NotificationService; @@ -23,7 +23,6 @@ import de.fearnixx.jeak.service.profile.ProfileService; import de.fearnixx.jeak.service.task.ITaskService; import de.fearnixx.jeak.service.teamspeak.UserService; -import de.fearnixx.jeak.service.token.TokenService; import de.fearnixx.jeak.service.util.UtilCommands; import de.fearnixx.jeak.task.TaskService; import de.fearnixx.jeak.teamspeak.IServer; @@ -187,7 +186,6 @@ protected void doServiceStartup() { initializeService(new ProfileService(new File(confDir, "profiles"))); initializeService(new PermissionService()); initializeService(new UserService()); - initializeService(new TokenService()); initializeService(new ControllerService()); if (ENABLE_VOICE_CONNECTIONS) { diff --git a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java index 22845e20..ca9bac0a 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java @@ -1,9 +1,13 @@ package de.fearnixx.jeak.service.http; import de.fearnixx.jeak.event.bot.IBotStateEvent; -import de.fearnixx.jeak.reflect.*; +import de.fearnixx.jeak.reflect.FrameworkService; +import de.fearnixx.jeak.reflect.IInjectionService; +import de.fearnixx.jeak.reflect.Inject; +import de.fearnixx.jeak.reflect.Listener; import de.fearnixx.jeak.reflect.http.RestController; -import de.fearnixx.jeak.service.http.connection.ControllerRequestVerifier; +import de.fearnixx.jeak.service.IServiceManager; +import de.fearnixx.jeak.service.event.IEventService; import de.fearnixx.jeak.service.http.connection.HttpServer; import de.fearnixx.jeak.service.http.connection.RestConfiguration; import de.fearnixx.jeak.service.http.controller.ControllerContainer; @@ -11,6 +15,7 @@ import de.fearnixx.jeak.service.http.controller.SparkAdapter; import de.fearnixx.jeak.service.http.exceptions.RegisterControllerException; import de.fearnixx.jeak.service.http.request.auth.token.TokenAuthService; +import de.fearnixx.jeak.service.http.request.token.ITokenAuthService; import java.util.HashMap; import java.util.Map; @@ -22,9 +27,16 @@ public class ControllerService implements IControllerService { private final Map, Object> controllers; private final HttpServer httpServer; - private final TokenAuthService tokenAuthService; private final RestConfiguration restConfiguration; + private TokenAuthService tokenAuthService; + + @Inject + private IServiceManager serviceManager; + + @Inject + private IEventService eventService; + @Inject private IInjectionService injectionService; @@ -44,6 +56,9 @@ public ControllerService(Map, Object> controllers) { @Listener public void onPreInt(IBotStateEvent.IPreInitializeEvent preInitializeEvent) { injectionService.injectInto(tokenAuthService); + eventService.registerListener(tokenAuthService); + serviceManager.registerService(ITokenAuthService.class, tokenAuthService); + injectionService.injectInto(restConfiguration); restConfiguration.loadConfig(); injectionService.injectInto(httpServer); @@ -71,7 +86,7 @@ public Optional provide(Class cntrlrClass) { @Override public T provideUnchecked(Class cntrlrClass) { Objects.requireNonNull(cntrlrClass, "Controller class type hint cannot be null!"); - return cntrlrClass.cast( controllers.get(cntrlrClass)); + return cntrlrClass.cast(controllers.get(cntrlrClass)); } @Override diff --git a/src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java b/src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java deleted file mode 100644 index bc9321f4..00000000 --- a/src/main/java/de/fearnixx/jeak/service/http/connection/ControllerRequestVerifier.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.fearnixx.jeak.service.http.connection; - -import de.fearnixx.jeak.reflect.Inject; -import de.fearnixx.jeak.service.token.ITokenService; - -public class ControllerRequestVerifier implements IConnectionVerifier { - private static final String TOKEN_TEXT = "Token "; - - @Inject - private ITokenService tokenService; - - @Override - public boolean verifyRequest(String endpoint, String authorizationText) { - boolean isAuthorized = false; - if (isToken(authorizationText)) { - isAuthorized = tokenService.verifyToken(endpoint, extractToken(authorizationText)); - } - return isAuthorized; - } - - private boolean isToken(String authorizationText) { - return authorizationText.contains(TOKEN_TEXT); - } - - /** - * Extract the token from a given String. This requires that it actually is a token. - * - * @param authorizationText The text as {@links String}. - * @return The token as {@link String} - */ - private String extractToken(String authorizationText) { - return authorizationText.replace(TOKEN_TEXT, ""); - } -} diff --git a/src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java b/src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java deleted file mode 100644 index f526b698..00000000 --- a/src/main/java/de/fearnixx/jeak/service/http/connection/IConnectionVerifier.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.fearnixx.jeak.service.http.connection; - -/** - * Used to verify an authorization text. - * - */ -public interface IConnectionVerifier { - /** - * Verify an HTTP-Authorization text. - * - * @param endpoint The endpoint to verify the token for. - * @param authorizationText The text from the HTTP-Authorization header. - * @return true if the request could be verified, - * false otherwise. - */ - boolean verifyRequest(String endpoint, String authorizationText); -} From 22f35dfb43b055417f5382f711d0310e4ec82383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann=20=28MarkL4YG=29?= Date: Sat, 27 Jun 2020 16:37:09 +0200 Subject: [PATCH 11/13] Bump bugfix version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 940f41e2..63f780c4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'de.fearnixx' -version '1.1.3' +version '1.1.4' def projectVersion = project.version apply plugin: 'java' From a01e876c39299cc8327db4ec6d9bfa0865c371c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann?= Date: Wed, 1 Jul 2020 21:25:11 +0200 Subject: [PATCH 12/13] Add command to request authentication tokens --- .../authentication/IAuthorisationService.java | 13 ----- .../AuthorisationEntityManager.java | 12 ----- .../authentication/AuthorisationService.java | 22 --------- .../request/auth/token/TokenAuthService.java | 47 +++++++++++++++++++ src/main/resources/localization/http.json | 10 ++++ 5 files changed, 57 insertions(+), 47 deletions(-) delete mode 100644 src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java create mode 100644 src/main/resources/localization/http.json diff --git a/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java b/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java deleted file mode 100644 index f91e9881..00000000 --- a/src/api/java/de/fearnixx/jeak/service/authentication/IAuthorisationService.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.fearnixx.jeak.service.authentication; - -import de.fearnixx.jeak.teamspeak.data.IUser; - -/** - * A Service for handling authentication of clients for resources. - * - */ -public interface IAuthorisationService { - String authorisationGrant(IUser user); - String requestAccessToken(String authorizationGrant); - boolean validateToken(String token); -} diff --git a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java deleted file mode 100644 index 98b2cb09..00000000 --- a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationEntityManager.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.fearnixx.jeak.service.authentication; - -import javax.persistence.EntityManager; - -public class AuthorisationEntityManager { - private EntityManager entityManager; - - public EntityManager getEntityManager() { - return entityManager; - } - -} diff --git a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java b/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java deleted file mode 100644 index bd783149..00000000 --- a/src/main/java/de/fearnixx/jeak/service/authentication/AuthorisationService.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.fearnixx.jeak.service.authentication; - -import de.fearnixx.jeak.teamspeak.data.IUser; - -public class AuthorisationService implements IAuthorisationService { - private AuthorisationEntityManager authorisationEntityManager; - - @Override - public String authorisationGrant(IUser user) { - return null; - } - - @Override - public String requestAccessToken(String authorizationGrant) { - return null; - } - - @Override - public boolean validateToken(String token) { - return false; - } -} diff --git a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java index 44e64ad7..5acc584a 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/request/auth/token/TokenAuthService.java @@ -5,9 +5,13 @@ import de.fearnixx.jeak.reflect.Config; import de.fearnixx.jeak.reflect.Inject; import de.fearnixx.jeak.reflect.Listener; +import de.fearnixx.jeak.reflect.LocaleUnit; +import de.fearnixx.jeak.service.command.ICommandExecutionContext; +import de.fearnixx.jeak.service.command.ICommandService; import de.fearnixx.jeak.service.http.request.IRequestContext; import de.fearnixx.jeak.service.http.request.token.IAuthenticationToken; import de.fearnixx.jeak.service.http.request.token.ITokenAuthService; +import de.fearnixx.jeak.service.locale.ILocalizationUnit; import de.fearnixx.jeak.service.teamspeak.IUserService; import de.fearnixx.jeak.teamspeak.data.IUser; import de.fearnixx.jeak.util.Configurable; @@ -21,13 +25,21 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Collections; +import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; +import static de.fearnixx.jeak.service.command.spec.Commands.commandSpec; +import static de.fearnixx.jeak.service.command.spec.Commands.paramSpec; + public class TokenAuthService extends Configurable implements ITokenAuthService { private static final Logger logger = LoggerFactory.getLogger(TokenAuthService.class); private static final int TOKEN_LENGTH = Main.getProperty("jeak.http.auth.token_length", 128); + private static final String AUTHENTICATION_TOKEN_PERMISSION = "jeak.command.http.authToken"; + public static final String AUTHENTICATION_TOKEN_PERMISSION_BASE = AUTHENTICATION_TOKEN_PERMISSION + ".base"; + public static final String AUTHENTICATION_TOKEN_PERMISSION_OTHER = AUTHENTICATION_TOKEN_PERMISSION + ".other"; + private static final String MSG_TOKEN_GENERATED = "http.auth.token.generated"; private static final String NO_EXPIRY_VALUE = "never"; public static final String EXPIRY_NODE_NAME = "expiry"; @@ -44,6 +56,13 @@ public class TokenAuthService extends Configurable implements ITokenAuthService @Inject private IUserService userService; + @Inject + private ICommandService commandService; + + @Inject + @LocaleUnit(value = "jeak.http", defaultResource = "localization/http.json") + private ILocalizationUnit localizationUnit; + public TokenAuthService() { super(TokenAuthService.class); } @@ -174,6 +193,34 @@ public synchronized void onInitialize(IBotStateEvent.IInitializeEvent event) { if (!loadConfig()) { event.cancel(); } + + // Register commands + commandService.registerCommand( + commandSpec("auth-token", "http:auth-token", "http:authenticationToken") + .permission(AUTHENTICATION_TOKEN_PERMISSION_BASE) + .parameters(paramSpec().optional(paramSpec("user", IUser.class))) + .executor(this::onUserRequestedPermission) + .build()); + } + + @Listener(order = Listener.Orders.EARLIER) + public synchronized void onShutdown(IBotStateEvent.IPreShutdown shutdownEvent) { + if (!saveConfig()) { + logger.error("Configuration save failed, see preceding error."); + } + } + + protected void onUserRequestedPermission(ICommandExecutionContext execCtx) { + var optTarget = execCtx.getOne("user", IUser.class); + if (optTarget.isPresent() && !execCtx.getSender().hasPermission(AUTHENTICATION_TOKEN_PERMISSION_OTHER)) { + execCtx.getSender().sendMessage(String.format("You're not allowed to request tokens for others (%s)", AUTHENTICATION_TOKEN_PERMISSION_OTHER)); + return; + } + + var generatedToken = generateToken(optTarget.orElseGet(execCtx::getSender)); + String notifyMessage = localizationUnit.getContext(execCtx.getSender()) + .getMessage(MSG_TOKEN_GENERATED, Map.of("token", generatedToken.getTokenString())); + execCtx.getSender().sendMessage(notifyMessage); } @Override diff --git a/src/main/resources/localization/http.json b/src/main/resources/localization/http.json new file mode 100644 index 00000000..c42bae4d --- /dev/null +++ b/src/main/resources/localization/http.json @@ -0,0 +1,10 @@ +{ + "langs": { + "en": { + "http.auth.token.generated": "Auth token generated: %[token]" + }, + "de": { + "http.auth.token.generated": "Auth-Token erstellt: %[token]" + } + } +} \ No newline at end of file From a5f573f9c7205e18c29a2cc2c40b03f171cbf0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Le=C3=9Fmann?= Date: Tue, 18 May 2021 19:14:20 +0200 Subject: [PATCH 13/13] WIP - reset me --- build.gradle | 7 ++++- .../jeak/reflect/http/Authenticated.java | 23 ++++++++++++++++ .../fearnixx/jeak/reflect/http/PathParam.java | 20 -------------- .../jeak/reflect/http/RequestBody.java | 15 ----------- .../jeak/reflect/http/RequestMapping.java | 14 ++-------- .../jeak/reflect/http/RequestParam.java | 21 --------------- .../jeak/reflect/http/RestController.java | 15 ++++++----- .../jeak/reflect/http/params/PathParam.java | 20 ++++++++++++++ .../jeak/reflect/http/params/QueryParam.java | 19 ++++++++++++++ .../jeak/reflect/http/params/RequestBody.java | 19 ++++++++++++++ .../http/{ => params}/RequestContext.java | 4 +-- .../jeak/service/http/ControllerService.java | 18 ++++++------- .../jeak/service/http/ControllerUtil.java | 15 +++++++++++ .../service/http/connection/HttpServer.java | 21 ++++++--------- .../http/controller/ControllerContainer.java | 5 ++-- .../service/http/controller/SparkAdapter.java | 11 +++++--- .../http/testImpls/SecondTestController.java | 26 ------------------- .../jeak/test/plugin/http}/DummyObject.java | 2 +- .../HTTPTestPlugin.java} | 11 +++----- .../plugin/http/SecondTestController.java | 26 +++++++++++++++++++ .../test/plugin/http}/TestController.java | 21 ++++++++------- 21 files changed, 185 insertions(+), 148 deletions(-) create mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/Authenticated.java delete mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java delete mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java delete mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java create mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/params/PathParam.java create mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/params/QueryParam.java create mode 100644 src/api/java/de/fearnixx/jeak/reflect/http/params/RequestBody.java rename src/api/java/de/fearnixx/jeak/reflect/http/{ => params}/RequestContext.java (87%) create mode 100644 src/main/java/de/fearnixx/jeak/service/http/ControllerUtil.java delete mode 100644 src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java rename src/{main/java/de/fearnixx/jeak/service/http/testImpls => test/java/de/fearnixx/jeak/test/plugin/http}/DummyObject.java (91%) rename src/test/java/de/fearnixx/jeak/test/plugin/{ControllerTestPlugin.java => http/HTTPTestPlugin.java} (76%) create mode 100644 src/test/java/de/fearnixx/jeak/test/plugin/http/SecondTestController.java rename src/{main/java/de/fearnixx/jeak/service/http/testImpls => test/java/de/fearnixx/jeak/test/plugin/http}/TestController.java (58%) diff --git a/build.gradle b/build.gradle index a1e5ee99..3395138d 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ def projectVersion = project.version sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 +def isRelease = true if (project.hasProperty("preRelease")) { apply from: './pre-release.gradle' @@ -21,6 +22,7 @@ if (project.hasProperty("preRelease")) { projectVersion += "-rc." + i } else if (!project.hasProperty('release')) { + isRelease = false projectVersion += '-SNAPSHOT' } @@ -125,7 +127,10 @@ task processSourceReplacements(type: org.gradle.api.tasks.Sync) { } compileJava { - source = processSourceReplacements.outputs + if (isRelease) { + source = processSourceReplacements.outputs + } + options.compilerArgs << "-Xlint:deprecation" } diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/Authenticated.java b/src/api/java/de/fearnixx/jeak/reflect/http/Authenticated.java new file mode 100644 index 00000000..78733daa --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/reflect/http/Authenticated.java @@ -0,0 +1,23 @@ +package de.fearnixx.jeak.reflect.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Designates a method annotated with {@link RequestMapping} as requiring authentication. + * To facilitate basic authorization checks, a permission can be set as required for the endpoint. + * + * @since 1.2.0 (experimental) + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Authenticated { + + /** + * The permissions required to access the endpoint at all. + * The requesting party must have all the given permissions in order to access the endpoint (AND). + */ + String[] permissions() default {}; +} diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java deleted file mode 100644 index c07c6e80..00000000 --- a/src/api/java/de/fearnixx/jeak/reflect/http/PathParam.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.fearnixx.jeak.reflect.http; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Mark a parameter to be filled by a request parameter from a call. - *

- * type(): REQUIRED if its something else than a {@link String} Specify the type of the expected variable. - *

- * name(): REQUIRED Specify the name of the expected variable. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface PathParam { - Class type() default String.class; - String name(); -} \ No newline at end of file diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java deleted file mode 100644 index 94dd5d85..00000000 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestBody.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.fearnixx.jeak.reflect.http; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Mark a parameter to be filled by the request body of a call. - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RequestBody { -} diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java index b9ca02ff..dbf28c31 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RequestMapping.java @@ -8,14 +8,9 @@ import java.lang.annotation.Target; /** - * Mark a method as method to be available via the controller. - * - * method(): REQUIRED Specify the used HTTP-method. - * - * endpoint(): REQUIRED Specify the endpoint for the annotated method. - * - * requireAuth(): Specify whether the calls to this endpoint should use an authorization scheme. + * Designates a method as being a receiver for HTTP-requests. * + * @since 1.2.0 (experimental) */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @@ -30,9 +25,4 @@ * URI appendix for this endpoint. */ String endpoint(); - - /** - * Whether or not request against this endpoint MUST BE authenticated. - */ - boolean requireAuth() default true; } diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java deleted file mode 100644 index 62fa59c1..00000000 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestParam.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.fearnixx.jeak.reflect.http; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Mark a parameter to be filled by a query parameter from a call. - * - * type(): REQUIRED if its something else than a {@link String} Specify the type of the expected variable. - * - * name(): REQUIRED Specify the name of the expected variable. - * - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface RequestParam { - Class type() default String.class; - String name(); -} diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java b/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java index 3d661ab8..53aca251 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/RestController.java @@ -8,15 +8,18 @@ /** * Marks a class as a REST controller. One plugin can have multiple controllers, so the controller determines * to which plugin it belongs by using the pluginId. - * - * endpoint(): REQUIRE if you use more than one controller. Specify the endpoint of the controller. - * - * pluginId(): Specify the id of the used plugin. This is independent of the pluginId specified in {@link de.fearnixx.jeak.reflect.JeakBotPlugin}. - * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface RestController { + + /** + * Your plugin id, since controllers are grouped by plugin ID to avoid path collisions between plugins. + */ String pluginId(); - String endpoint(); + + /** + * An endpoint path prefix for all request mappings within this class. + */ + String path(); } diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/params/PathParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/params/PathParam.java new file mode 100644 index 00000000..ddd00a7b --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/reflect/http/params/PathParam.java @@ -0,0 +1,20 @@ +package de.fearnixx.jeak.reflect.http.params; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies an endpoint method parameter to be derived from the requests path pattern. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface PathParam { + + /** + * If the parameter name from the path pattern differs from the method parameter name, this should be the + * name used in the path pattern. + */ + String name() default ""; +} \ No newline at end of file diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/params/QueryParam.java b/src/api/java/de/fearnixx/jeak/reflect/http/params/QueryParam.java new file mode 100644 index 00000000..f11756ec --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/reflect/http/params/QueryParam.java @@ -0,0 +1,19 @@ +package de.fearnixx.jeak.reflect.http.params; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Designates a methods parameter to be filled by a request parameter. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface QueryParam { + + /** + * If the parameter name differs from the HTTP-contract, this should be the name used in the contract. + */ + String name() default ""; +} diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/params/RequestBody.java b/src/api/java/de/fearnixx/jeak/reflect/http/params/RequestBody.java new file mode 100644 index 00000000..2f9fa515 --- /dev/null +++ b/src/api/java/de/fearnixx/jeak/reflect/http/params/RequestBody.java @@ -0,0 +1,19 @@ +package de.fearnixx.jeak.reflect.http.params; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Designates an endpoint method parameter to be filled with the request body received. + * This is only applicable to {@link de.fearnixx.jeak.service.http.RequestMethod#POST}, {@link de.fearnixx.jeak.service.http.RequestMethod#PUT} + * and {@link de.fearnixx.jeak.service.http.RequestMethod#PATCH} + * + * @implNote Currently, only two method parameter types are supported! If the type is {@link String}, the serialized content will be used. + * If the type is of a custom class, Jackson will be used to attempt JSON deserialization of the request body. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface RequestBody { +} diff --git a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java b/src/api/java/de/fearnixx/jeak/reflect/http/params/RequestContext.java similarity index 87% rename from src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java rename to src/api/java/de/fearnixx/jeak/reflect/http/params/RequestContext.java index 01514a31..396b183b 100644 --- a/src/api/java/de/fearnixx/jeak/reflect/http/RequestContext.java +++ b/src/api/java/de/fearnixx/jeak/reflect/http/params/RequestContext.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.reflect.http; +package de.fearnixx.jeak.reflect.http.params; import de.fearnixx.jeak.service.http.request.IRequestContext; @@ -19,7 +19,7 @@ /** * Denotes the attribute name to be used for the lookup. - * If none, the parameter injection will attempt to insert the {@link IRequestContext} instance. + * If empty, the parameter injection will attempt to insert the {@link IRequestContext} instance. * * @see IRequestContext.Attributes for more information on available attributes. */ diff --git a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java index ca9bac0a..dcdb3e71 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java +++ b/src/main/java/de/fearnixx/jeak/service/http/ControllerService.java @@ -17,10 +17,7 @@ import de.fearnixx.jeak.service.http.request.auth.token.TokenAuthService; import de.fearnixx.jeak.service.http.request.token.ITokenAuthService; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; @FrameworkService(serviceInterface = IControllerService.class) public class ControllerService implements IControllerService { @@ -62,9 +59,12 @@ public void onPreInt(IBotStateEvent.IPreInitializeEvent preInitializeEvent) { injectionService.injectInto(restConfiguration); restConfiguration.loadConfig(); injectionService.injectInto(httpServer); - httpServer.start(); } + @Listener + public void onInit(IBotStateEvent.IInitializeEvent event) { + httpServer.start(); + } @Override public void registerController(Class cntrlrClass, T instance) { @@ -91,7 +91,7 @@ public T provideUnchecked(Class cntrlrClass) { @Override public Map, Object> provideAll() { - return controllers; + return Collections.unmodifiableMap(controllers); } private boolean doesControllerAlreadyExist(T restController) { @@ -101,11 +101,11 @@ private boolean doesControllerAlreadyExist(T restController) { } return controllers.keySet().stream() .filter(aClass -> extractPluginId(aClass).equals(extractPluginId(controllerClass))) - .anyMatch(aClass -> extractControllerName(aClass).equals(extractControllerName(controllerClass))); + .anyMatch(aClass -> extractControllerPath(aClass).equals(extractControllerPath(controllerClass))); } - private String extractControllerName(Class clazz) { - return clazz.getAnnotation(RestController.class).endpoint(); + private String extractControllerPath(Class clazz) { + return clazz.getAnnotation(RestController.class).path(); } private String extractPluginId(Class clazz) { diff --git a/src/main/java/de/fearnixx/jeak/service/http/ControllerUtil.java b/src/main/java/de/fearnixx/jeak/service/http/ControllerUtil.java new file mode 100644 index 00000000..11977aa7 --- /dev/null +++ b/src/main/java/de/fearnixx/jeak/service/http/ControllerUtil.java @@ -0,0 +1,15 @@ +package de.fearnixx.jeak.service.http; + +import java.util.regex.Pattern; + +public class ControllerUtil { + + private static final Pattern startWithPattern = Pattern.compile("^/*(.+)$"); + private static final Pattern endWithPattern = Pattern.compile("^(.+?)/*$"); + + public static String joinWithSlash(String a, String b) { + final var first = a != null && !a.isBlank() ? endWithPattern.matcher(a).group(1) : ""; + final var last = b != null && !b.isBlank() ? startWithPattern.matcher(b).group(1) : ""; + return first + "/" + last; + } +} diff --git a/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java index 0f07eef3..a0533db2 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java +++ b/src/main/java/de/fearnixx/jeak/service/http/connection/HttpServer.java @@ -4,17 +4,13 @@ import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import de.fearnixx.jeak.reflect.http.PathParam; -import de.fearnixx.jeak.reflect.http.RequestContext; -import de.fearnixx.jeak.reflect.http.RequestParam; +import de.fearnixx.jeak.reflect.http.params.PathParam; +import de.fearnixx.jeak.reflect.http.params.QueryParam; import de.fearnixx.jeak.service.http.controller.ControllerContainer; import de.fearnixx.jeak.service.http.controller.ControllerMethod; import de.fearnixx.jeak.service.http.controller.MethodParameter; -import de.fearnixx.jeak.service.http.request.IRequestContext; -import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.xbill.DNS.RPRecord; import spark.Request; import java.io.IOException; @@ -97,20 +93,19 @@ protected Object transformRequestOption(String string, Request request, MethodPa } /** - * Retrieve the name from a {@link RequestParam} annotated value. Only call the method, if you are sure the used - * {@link MethodParameter} is annotated with an {@link RequestParam}. + * Retrieve the name from a {@link QueryParam} annotated value. Only call the method, if you are sure the used + * {@link MethodParameter} is annotated with an {@link QueryParam}. * * @param methodParameter * @return The name of the annotated variable. */ protected String getRequestParamName(MethodParameter methodParameter) { - Function function = annotation -> ((RequestParam) annotation).name(); - return (String) methodParameter.callAnnotationFunction(function, RequestParam.class).get(); + Function function = annotation -> ((QueryParam) annotation).name(); + return (String) methodParameter.callAnnotationFunction(function, QueryParam.class).get(); } - protected Object getRequestParamType(MethodParameter methodParameter) { - Function function = annotation -> ((RequestParam) annotation).type(); - return methodParameter.callAnnotationFunction(function, RequestParam.class).orElse(String.class); + protected Class getRequestParamType(MethodParameter methodParameter) { + return methodParameter.getType(); } protected String getPathParamName(MethodParameter methodParameter) { diff --git a/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java index dfe1a962..a8a1eb26 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/ControllerContainer.java @@ -2,6 +2,7 @@ import de.fearnixx.jeak.reflect.http.RequestMapping; import de.fearnixx.jeak.reflect.http.RestController; +import de.fearnixx.jeak.service.http.ControllerUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,7 +52,7 @@ public Optional getAnnotation(Class annotationClass private String extractControllerRoute(Object o) { RestController annotation = o.getClass().getAnnotation(RestController.class); - return annotation.pluginId().concat(annotation.endpoint()); + return ControllerUtil.joinWithSlash(annotation.pluginId(), annotation.path()); } /** @@ -67,7 +68,7 @@ public Object invoke(ControllerMethod controllerMethod, Object... methodParamete return controllerMethod.invoke(controllerObject, methodParameters); } - public Object getControllerObject() { + public Object getControllerInstance() { return controllerObject; } diff --git a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java index 62628df5..66c8e920 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java +++ b/src/main/java/de/fearnixx/jeak/service/http/controller/SparkAdapter.java @@ -1,7 +1,12 @@ package de.fearnixx.jeak.service.http.controller; import de.fearnixx.jeak.Main; -import de.fearnixx.jeak.reflect.http.*; +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RestController; +import de.fearnixx.jeak.reflect.http.params.PathParam; +import de.fearnixx.jeak.reflect.http.params.QueryParam; +import de.fearnixx.jeak.reflect.http.params.RequestBody; +import de.fearnixx.jeak.reflect.http.params.RequestContext; import de.fearnixx.jeak.service.http.RequestMethod; import de.fearnixx.jeak.service.http.ResponseEntity; import de.fearnixx.jeak.service.http.connection.HttpServer; @@ -156,7 +161,7 @@ private Object[] extractParameters(List methodParameterList, Re Object retrievedParameter = null; if (methodParameter.hasAnnotation(PathParam.class)) { retrievedParameter = transformRequestOption(request.params(getPathParamName(methodParameter)), request, methodParameter); - } else if (methodParameter.hasAnnotation(RequestParam.class)) { + } else if (methodParameter.hasAnnotation(QueryParam.class)) { retrievedParameter = transformRequestOption(request.queryMap(getRequestParamName(methodParameter)).value(), request, methodParameter); } else if (methodParameter.hasAnnotation(RequestBody.class)) { retrievedParameter = transformRequestOption(request.body(), request, methodParameter); @@ -220,7 +225,7 @@ private void addBeforeHandlingCheck(String path, ControllerContainer controllerC if (!tokenAuthService.attemptAuthentication(request)) { var mapping = controllerMethod.getAnnotation(RequestMapping.class); - if (mapping.isPresent() && mapping.get().requireAuth()) { + if (mapping.isPresent()) { logger.info("Unauthenticated HTTP request blocked."); service.halt(HttpStatus.UNAUTHORIZED_401, "{\"errors\": [\"Authentication is mandatory for this endpoint!\"]"); } diff --git a/src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java b/src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java deleted file mode 100644 index 3ffce148..00000000 --- a/src/main/java/de/fearnixx/jeak/service/http/testImpls/SecondTestController.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.fearnixx.jeak.service.http.testImpls; - -import de.fearnixx.jeak.reflect.http.RequestBody; -import de.fearnixx.jeak.reflect.http.RequestMapping; -import de.fearnixx.jeak.reflect.http.RequestParam; -import de.fearnixx.jeak.reflect.http.RestController; -import de.fearnixx.jeak.service.http.RequestMethod; - -@RestController(endpoint = "/test", pluginId = "testPluginId") -public class SecondTestController { - - @RequestMapping(method = RequestMethod.GET, endpoint = "/hello") - public DummyObject hello() { - return new DummyObject("second", 20); - } - - @RequestMapping(method = RequestMethod.GET, endpoint = "/info/:name") - public String returnSentInfo(@RequestParam(name = "name") String name) { - return "second" + name; - } - - @RequestMapping(method = RequestMethod.POST, endpoint = "/body") - public String sendBody(@RequestBody() String string) { - return "second body " + string; - } -} diff --git a/src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java b/src/test/java/de/fearnixx/jeak/test/plugin/http/DummyObject.java similarity index 91% rename from src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java rename to src/test/java/de/fearnixx/jeak/test/plugin/http/DummyObject.java index e1a6e7f6..deeabe11 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/testImpls/DummyObject.java +++ b/src/test/java/de/fearnixx/jeak/test/plugin/http/DummyObject.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.service.http.testImpls; +package de.fearnixx.jeak.test.plugin.http; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java b/src/test/java/de/fearnixx/jeak/test/plugin/http/HTTPTestPlugin.java similarity index 76% rename from src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java rename to src/test/java/de/fearnixx/jeak/test/plugin/http/HTTPTestPlugin.java index 1fa95b4e..5cfb7bab 100644 --- a/src/test/java/de/fearnixx/jeak/test/plugin/ControllerTestPlugin.java +++ b/src/test/java/de/fearnixx/jeak/test/plugin/http/HTTPTestPlugin.java @@ -1,4 +1,4 @@ -package de.fearnixx.jeak.test.plugin; +package de.fearnixx.jeak.test.plugin.http; import de.fearnixx.jeak.event.bot.IBotStateEvent; import de.fearnixx.jeak.reflect.Inject; @@ -6,18 +6,16 @@ import de.fearnixx.jeak.reflect.Listener; import de.fearnixx.jeak.service.http.IControllerService; import de.fearnixx.jeak.service.http.exceptions.RegisterControllerException; -import de.fearnixx.jeak.service.http.testImpls.SecondTestController; -import de.fearnixx.jeak.service.http.testImpls.TestController; import de.fearnixx.jeak.test.AbstractTestPlugin; -@JeakBotPlugin(id = "controllertestplugin") -public class ControllerTestPlugin extends AbstractTestPlugin { +@JeakBotPlugin(id = "httptestplugin") +public class HTTPTestPlugin extends AbstractTestPlugin { @Inject IControllerService restControllerService; - public ControllerTestPlugin() { + public HTTPTestPlugin() { super(); addTest("register_successful"); addTest("register_duplicated_controller"); @@ -32,6 +30,5 @@ public void onInitialize(IBotStateEvent.IInitializeEvent event) { } catch (RegisterControllerException e) { success("register_duplicated_controller"); } - } } diff --git a/src/test/java/de/fearnixx/jeak/test/plugin/http/SecondTestController.java b/src/test/java/de/fearnixx/jeak/test/plugin/http/SecondTestController.java new file mode 100644 index 00000000..22d05aea --- /dev/null +++ b/src/test/java/de/fearnixx/jeak/test/plugin/http/SecondTestController.java @@ -0,0 +1,26 @@ +package de.fearnixx.jeak.test.plugin.http; + +import de.fearnixx.jeak.reflect.http.RequestMapping; +import de.fearnixx.jeak.reflect.http.RestController; +import de.fearnixx.jeak.reflect.http.params.QueryParam; +import de.fearnixx.jeak.reflect.http.params.RequestBody; +import de.fearnixx.jeak.service.http.RequestMethod; + +@RestController(path = "/test", pluginId = "httptestplugin") +public class SecondTestController { + + @RequestMapping(endpoint = "/hello") + public DummyObject hello() { + return new DummyObject("second", 20); + } + + @RequestMapping(endpoint = "/info/:name") + public String returnSentInfo(@QueryParam(name = "name") String name) { + return "second" + name; + } + + @RequestMapping(method = RequestMethod.POST, endpoint = "/body") + public String sendBody(@RequestBody String string) { + return "second body " + string; + } +} diff --git a/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java b/src/test/java/de/fearnixx/jeak/test/plugin/http/TestController.java similarity index 58% rename from src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java rename to src/test/java/de/fearnixx/jeak/test/plugin/http/TestController.java index 63696daa..faece67c 100644 --- a/src/main/java/de/fearnixx/jeak/service/http/testImpls/TestController.java +++ b/src/test/java/de/fearnixx/jeak/test/plugin/http/TestController.java @@ -1,27 +1,27 @@ -package de.fearnixx.jeak.service.http.testImpls; +package de.fearnixx.jeak.test.plugin.http; -import de.fearnixx.jeak.reflect.http.RequestBody; import de.fearnixx.jeak.reflect.http.RequestMapping; -import de.fearnixx.jeak.reflect.http.RequestParam; import de.fearnixx.jeak.reflect.http.RestController; +import de.fearnixx.jeak.reflect.http.params.QueryParam; +import de.fearnixx.jeak.reflect.http.params.RequestBody; import de.fearnixx.jeak.service.http.IResponseEntity; import de.fearnixx.jeak.service.http.RequestMethod; import de.fearnixx.jeak.service.http.ResponseEntity; import org.eclipse.jetty.http.HttpStatus; -@RestController(pluginId = "testPluginId", endpoint = "/test") +@RestController(pluginId = "testPluginId", path = "/test") public class TestController { - @RequestMapping(method = RequestMethod.GET, endpoint = "/hello") + @RequestMapping(endpoint = "/hello") public ResponseEntity hello() { - return new ResponseEntity.Builder(new DummyObject("Finn", 20)) + return new ResponseEntity.Builder<>(new DummyObject("Finn", 20)) .withHeader("Cache-Control", "max-age=0") .withStatus(HttpStatus.OK_200) .build(); } - @RequestMapping(method = RequestMethod.GET, endpoint = "/info") - public String returnSentInfo(@RequestParam(name = "name") String name) { + @RequestMapping(endpoint = "/info") + public String returnSentInfo(@QueryParam(name = "name") String name) { return "received " + name; } @@ -30,11 +30,12 @@ public String sendBody(@RequestBody String string) { return "this is the body " + string; } - @RequestMapping(method = RequestMethod.GET, endpoint = "/int", requireAuth = false) - public String sendStuff(@RequestParam(name = "num", type = Integer.class) Integer num) { + @RequestMapping(endpoint = "/int") + public String sendStuff(@QueryParam(name = "num") Integer num) { return "received" + num; } + @RequestMapping(endpoint = "/hallo") public IResponseEntity hallo() { return new ResponseEntity.Builder() .withHeader("some-header", "GET")