diff --git a/config-layers/common/in/erail/route/LoadUserFromAccessTokenRouteBuilder.properties b/config-layers/common/in/erail/route/LoadUserFromAccessTokenRouteBuilder.properties index cc19a08..0024b38 100644 --- a/config-layers/common/in/erail/route/LoadUserFromAccessTokenRouteBuilder.properties +++ b/config-layers/common/in/erail/route/LoadUserFromAccessTokenRouteBuilder.properties @@ -3,5 +3,4 @@ $class=in.erail.route.LoadUserFromAccessTokenRouteBuillder vertx=/io/vertx/core/Vertx log=true -oAuth2Auth=/io/vertx/ext/auth/oauth2/OAuth2Auth -enable^=/in/erail/common/FrameworkConfiguration.oAuth2AuthEnable +userProvider=/in/erail/user/UserProvider diff --git a/config-layers/common/in/erail/user/UserProvider.properties b/config-layers/common/in/erail/user/UserProvider.properties new file mode 100644 index 0000000..a4b15a8 --- /dev/null +++ b/config-layers/common/in/erail/user/UserProvider.properties @@ -0,0 +1,4 @@ +#/in/erail/user/UserProvider +$class=in.erail.user.oauth2.OAuth2AuthUserProvider + +oAuth2Auth=/io/vertx/ext/auth/oauth2/OAuth2Auth diff --git a/src/main/java/in/erail/model/RequestEvent.java b/src/main/java/in/erail/model/RequestEvent.java index 9a9cf7d..d6d828b 100644 --- a/src/main/java/in/erail/model/RequestEvent.java +++ b/src/main/java/in/erail/model/RequestEvent.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.google.common.io.BaseEncoding; import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; import java.util.Map; /** @@ -23,9 +24,10 @@ public class RequestEvent { private Map mPathParameters; private Map mStageVariables; @SuppressWarnings("rawtypes") - private Map mRequestContext; + private Map mRequestContext; private byte[] mBody = new byte[0]; private boolean mIsBase64Encoded = false; + private Map mPrincipal; public String getResource() { return mResource; @@ -100,12 +102,12 @@ public void setStageVariables(Map pStageVariables) { } @SuppressWarnings("rawtypes") - public Map getRequestContext() { + public Map getRequestContext() { return mRequestContext; } @SuppressWarnings("rawtypes") - public void setRequestContext(Map pRequestContext) { + public void setRequestContext(Map pRequestContext) { this.mRequestContext = pRequestContext; } @@ -124,11 +126,25 @@ public byte[] getBody() { public void setBody(byte[] pBody) { this.mBody = pBody; } - - public String bodyAsString(){ - if(isIsBase64Encoded()){ + + public String bodyAsString() { + if (isIsBase64Encoded()) { return new String(BaseEncoding.base64().decode(new String(getBody()))); } return new String(getBody()); } + + public Map getPrincipal() { + return mPrincipal; + } + + public void setPrincipal(Map pPrincipal) { + this.mPrincipal = pPrincipal; + } + + @Override + public String toString() { + return JsonObject.mapFrom(this).toString(); + } + } diff --git a/src/main/java/in/erail/model/ResponseEvent.java b/src/main/java/in/erail/model/ResponseEvent.java index 9f16d60..455cfda 100644 --- a/src/main/java/in/erail/model/ResponseEvent.java +++ b/src/main/java/in/erail/model/ResponseEvent.java @@ -3,6 +3,7 @@ import com.google.common.base.Preconditions; import com.google.common.net.HttpHeaders; import com.google.common.net.MediaType; +import io.vertx.core.json.JsonObject; import io.vertx.reactivex.core.MultiMap; import java.util.Arrays; import java.util.Collections; @@ -166,17 +167,18 @@ public ResponseEvent setContentType(MediaType pMediaType) { return this; } - public ResponseEvent addHeader(String pHeaderName, String pMediaType) { - mMultiValueHeaders.add(HttpHeaders.CONTENT_TYPE, pMediaType); - return this; - } - - public ResponseEvent addHeader(String pHeaderName, MediaType pMediaType) { - addHeader(HttpHeaders.CONTENT_TYPE, pMediaType.toString()); + public ResponseEvent addHeader(String pHeaderName, String pValue) { + mMultiValueHeaders.add(pHeaderName, pValue); return this; } public String headerValue(String pHeaderName) { return mMultiValueHeaders.get(pHeaderName); } + + @Override + public String toString() { + return JsonObject.mapFrom(this).toString(); + } + } diff --git a/src/main/java/in/erail/route/LoadUserFromAccessTokenRouteBuillder.java b/src/main/java/in/erail/route/LoadUserFromAccessTokenRouteBuillder.java index d0abb91..89b0414 100644 --- a/src/main/java/in/erail/route/LoadUserFromAccessTokenRouteBuillder.java +++ b/src/main/java/in/erail/route/LoadUserFromAccessTokenRouteBuillder.java @@ -2,13 +2,12 @@ import com.google.common.base.Strings; import com.google.common.net.HttpHeaders; +import in.erail.user.UserProvider; import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl; -import io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl; -import io.vertx.reactivex.ext.auth.oauth2.AccessToken; -import io.vertx.reactivex.ext.auth.oauth2.OAuth2Auth; import io.vertx.reactivex.ext.web.Router; import io.vertx.reactivex.ext.web.RoutingContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @@ -16,41 +15,47 @@ */ public class LoadUserFromAccessTokenRouteBuillder extends AbstractRouterBuilderImpl { - private OAuth2Auth mOAuth2Auth; - - public OAuth2Auth getOAuth2Auth() { - return mOAuth2Auth; - } - - public void setOAuth2Auth(OAuth2Auth pOAuth2Auth) { - this.mOAuth2Auth = pOAuth2Auth; - } + private Pattern AUTH_TOKEN = Pattern.compile("^Bearer\\s(?.*)"); + private UserProvider mUserProvider; @Override public Router getRouter(Router pRouter) { pRouter.route().handler(this::handle); return pRouter; - } public void handle(RoutingContext pRoutingContext) { - + if (pRoutingContext.user() == null) { String access_token = pRoutingContext.request().getHeader(HttpHeaders.AUTHORIZATION); if (!Strings.isNullOrEmpty(access_token)) { - OAuth2AuthProviderImpl provider = (OAuth2AuthProviderImpl) getOAuth2Auth().getDelegate(); - JsonObject accessToken = new JsonObject().put("access_token", access_token.split(" ")[1]); - try { - OAuth2TokenImpl token = new OAuth2TokenImpl(provider, accessToken); - pRoutingContext.setUser(new AccessToken(token)); - } catch (RuntimeException e) { - getLog().error(e); - pRoutingContext.fail(401); - return; + Matcher token = AUTH_TOKEN.matcher(access_token); + if (token.find()) { + JsonObject accessToken = new JsonObject().put("access_token", token.group("token")); + try { + pRoutingContext + .setUser(getUserProvider() + .getUser(accessToken) + .blockingGet()); + } catch (RuntimeException e) { + getLog().error(e); + pRoutingContext.fail(401); + return; + } + } else { + getLog().warn(() -> "Invalid Auth Header:" + access_token); } } } pRoutingContext.next(); } + public UserProvider getUserProvider() { + return mUserProvider; + } + + public void setUserProvider(UserProvider pUserProvider) { + this.mUserProvider = pUserProvider; + } + } diff --git a/src/main/java/in/erail/route/OpenAPI3RouteBuilder.java b/src/main/java/in/erail/route/OpenAPI3RouteBuilder.java index 2227116..72beb9f 100644 --- a/src/main/java/in/erail/route/OpenAPI3RouteBuilder.java +++ b/src/main/java/in/erail/route/OpenAPI3RouteBuilder.java @@ -126,6 +126,13 @@ public JsonObject serialiseRoutingContext(RoutingContext pContext) { request.setQueryStringParameters(convertMultiMapIntoMap(pContext.queryParams())); request.setPathParameters(convertMultiMapIntoMap(pContext.request().params())); + JsonObject principal = Optional + .ofNullable(pContext.user()) + .flatMap((t) -> Optional.ofNullable(t.principal())) + .orElse(new JsonObject()); + + request.setPrincipal(principal.getMap()); + JsonObject result = JsonObject.mapFrom(request); getLog().debug(() -> "Context to JSON:" + result.toString()); @@ -147,8 +154,8 @@ public HttpServerResponse buildResponseFromReply(JsonObject pReplyResponse, Rout Optional contentType = Optional.ofNullable(response.headerValue(HttpHeaders.CONTENT_TYPE)); - if (contentType.isPresent()) { - response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.OCTET_STREAM); + if (!contentType.isPresent()) { + response.setContentType(MediaType.OCTET_STREAM); } response diff --git a/src/main/java/in/erail/service/RESTServiceImpl.java b/src/main/java/in/erail/service/RESTServiceImpl.java index 2f29adf..39e795e 100644 --- a/src/main/java/in/erail/service/RESTServiceImpl.java +++ b/src/main/java/in/erail/service/RESTServiceImpl.java @@ -1,5 +1,6 @@ package in.erail.service; +import com.google.common.net.MediaType; import io.reactivex.schedulers.Schedulers; import io.vertx.core.json.JsonObject; import io.vertx.reactivex.core.Vertx; @@ -7,6 +8,7 @@ import in.erail.glue.annotation.StartService; import in.erail.model.RequestEvent; import in.erail.model.ResponseEvent; +import io.netty.handler.codec.http.HttpResponseStatus; import io.reactivex.Scheduler; import io.reactivex.Single; import io.vertx.reactivex.core.eventbus.Message; @@ -50,7 +52,14 @@ public Single handleRequest(Message pMessage) { .flatMapMaybe(req -> process(req)) .toSingle(new ResponseEvent()) .map(resp -> JsonObject.mapFrom(resp)) - .doOnSuccess(resp -> pMessage.reply(resp)); + .doOnSuccess(resp -> pMessage.reply(resp)) + .doOnError(err -> { + ResponseEvent resp = new ResponseEvent() + .setStatusCode(HttpResponseStatus.BAD_REQUEST.code()) + .setContentType(MediaType.PLAIN_TEXT_UTF_8) + .setBody(ExceptionUtils.getMessage(err).getBytes()); + pMessage.reply(JsonObject.mapFrom(resp)); + }); } @Override diff --git a/src/main/java/in/erail/user/UserProvider.java b/src/main/java/in/erail/user/UserProvider.java new file mode 100644 index 0000000..496c9b8 --- /dev/null +++ b/src/main/java/in/erail/user/UserProvider.java @@ -0,0 +1,16 @@ +package in.erail.user; + +import io.reactivex.Maybe; +import io.vertx.core.json.JsonObject; +import io.vertx.reactivex.ext.auth.User; + +/** + * + * @author vinay + */ +public interface UserProvider { + + default Maybe getUser(JsonObject pPrincipal) { + return Maybe.empty(); + } +} diff --git a/src/main/java/in/erail/user/oauth2/OAuth2AuthUserProvider.java b/src/main/java/in/erail/user/oauth2/OAuth2AuthUserProvider.java new file mode 100644 index 0000000..5cf625a --- /dev/null +++ b/src/main/java/in/erail/user/oauth2/OAuth2AuthUserProvider.java @@ -0,0 +1,35 @@ +package in.erail.user.oauth2; + +import in.erail.user.UserProvider; +import io.reactivex.Maybe; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl; +import io.vertx.ext.auth.oauth2.impl.OAuth2TokenImpl; +import io.vertx.reactivex.ext.auth.User; +import io.vertx.reactivex.ext.auth.oauth2.AccessToken; +import io.vertx.reactivex.ext.auth.oauth2.OAuth2Auth; + +/** + * + * @author vinay + */ +public class OAuth2AuthUserProvider implements UserProvider { + + private OAuth2Auth mOAuth2Auth; + + @Override + public Maybe getUser(JsonObject pPrincipal) { + OAuth2AuthProviderImpl provider = (OAuth2AuthProviderImpl) getOAuth2Auth().getDelegate(); + OAuth2TokenImpl token = new OAuth2TokenImpl(provider, pPrincipal); + return Maybe.just(new AccessToken(token)); + } + + public OAuth2Auth getOAuth2Auth() { + return mOAuth2Auth; + } + + public void setOAuth2Auth(OAuth2Auth pOAuth2Auth) { + this.mOAuth2Auth = pOAuth2Auth; + } + +} diff --git a/src/test/java/in/erail/service/BinaryBodyService.java b/src/test/java/in/erail/service/BinaryBodyService.java index 63c86b3..bb075e2 100644 --- a/src/test/java/in/erail/service/BinaryBodyService.java +++ b/src/test/java/in/erail/service/BinaryBodyService.java @@ -1,7 +1,6 @@ package in.erail.service; import com.google.common.base.Strings; -import com.google.common.net.HttpHeaders; import com.google.common.net.MediaType; import in.erail.model.RequestEvent; @@ -28,7 +27,7 @@ public Maybe process(RequestEvent pRequest) { } ResponseEvent response = new ResponseEvent(); - response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.PLAIN_TEXT_UTF_8); + response.setContentType(MediaType.PLAIN_TEXT_UTF_8); JsonObject jsonBody = new JsonObject(Buffer.buffer(pRequest.getBody())); diff --git a/src/test/java/in/erail/service/BroadcastService.java b/src/test/java/in/erail/service/BroadcastService.java index fb8d21e..04d5c12 100644 --- a/src/test/java/in/erail/service/BroadcastService.java +++ b/src/test/java/in/erail/service/BroadcastService.java @@ -1,14 +1,14 @@ package in.erail.service; import com.google.common.base.Strings; -import com.google.common.net.HttpHeaders; import com.google.common.net.MediaType; -import in.erail.test.TestConstants; -import io.vertx.core.json.JsonObject; + import in.erail.model.RequestEvent; import in.erail.model.ResponseEvent; +import in.erail.test.TestConstants; import io.netty.handler.codec.http.HttpResponseStatus; import io.reactivex.Maybe; +import io.vertx.core.json.JsonObject; /** * @@ -35,7 +35,7 @@ public Maybe process(RequestEvent pRequest) { ResponseEvent response = new ResponseEvent(); response.setBody(TestConstants.Service.Message.successMessage().toString().getBytes()); - response.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8); + response.setContentType(MediaType.JSON_UTF_8); return Maybe.just(response); }