Merge remote-tracking branch 'origin/master' into 6.0.x

This commit is contained in:
William Cheng
2022-01-03 18:03:00 +08:00
904 changed files with 98009 additions and 10293 deletions

View File

@@ -19,6 +19,7 @@ app/controllers/UserApiControllerImp.java
app/controllers/UserApiControllerImpInterface.java
app/openapitools/ApiCall.java
app/openapitools/OpenAPIUtils.java
app/openapitools/SecurityAPIUtils.java
build.sbt
conf/application.conf
conf/logback.xml

View File

@@ -1,6 +1,7 @@
import com.google.inject.AbstractModule;
import controllers.*;
import openapitools.SecurityAPIUtils;
public class Module extends AbstractModule {
@@ -9,5 +10,6 @@ public class Module extends AbstractModule {
bind(PetApiControllerImpInterface.class).to(PetApiControllerImp.class);
bind(StoreApiControllerImpInterface.class).to(StoreApiControllerImp.class);
bind(UserApiControllerImpInterface.class).to(UserApiControllerImp.class);
bind(SecurityAPIUtils.class);
}
}

View File

@@ -15,7 +15,9 @@ import play.mvc.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import openapitools.OpenAPIUtils;
import openapitools.SecurityAPIUtils;
import static play.mvc.Results.ok;
import static play.mvc.Results.unauthorized;
import play.libs.Files.TemporaryFile;
import javax.validation.constraints.*;
@@ -23,47 +25,70 @@ import javax.validation.constraints.*;
@SuppressWarnings("RedundantThrows")
public abstract class PetApiControllerImpInterface {
@Inject private Config configuration;
@Inject private SecurityAPIUtils securityAPIUtils;
private ObjectMapper mapper = new ObjectMapper();
public Result addPetHttp(Http.Request request, Pet body) {
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
addPet(request, body);
return ok();
return ok();
}
public abstract void addPet(Http.Request request, Pet body) ;
public Result deletePetHttp(Http.Request request, Long petId, String apiKey) {
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
deletePet(request, petId, apiKey);
return ok();
return ok();
}
public abstract void deletePet(Http.Request request, Long petId, String apiKey) ;
public Result findPetsByStatusHttp(Http.Request request, @NotNull List<String> status) {
List<Pet> obj = findPetsByStatus(request, status);
if (configuration.getBoolean("useOutputBeanValidation")) {
for (Pet curItem : obj) {
OpenAPIUtils.validate(curItem);
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
List<Pet> obj = findPetsByStatus(request, status);
if (configuration.getBoolean("useOutputBeanValidation")) {
for (Pet curItem : obj) {
OpenAPIUtils.validate(curItem);
}
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
public abstract List<Pet> findPetsByStatus(Http.Request request, @NotNull List<String> status) ;
public Result findPetsByTagsHttp(Http.Request request, @NotNull List<String> tags) {
List<Pet> obj = findPetsByTags(request, tags);
if (configuration.getBoolean("useOutputBeanValidation")) {
for (Pet curItem : obj) {
OpenAPIUtils.validate(curItem);
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
List<Pet> obj = findPetsByTags(request, tags);
if (configuration.getBoolean("useOutputBeanValidation")) {
for (Pet curItem : obj) {
OpenAPIUtils.validate(curItem);
}
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
@@ -71,39 +96,57 @@ return ok(result);
public Result getPetByIdHttp(Http.Request request, Long petId) {
Pet obj = getPetById(request, petId);
if (configuration.getBoolean("useOutputBeanValidation")) {
if (configuration.getBoolean("useOutputBeanValidation")) {
OpenAPIUtils.validate(obj);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
public abstract Pet getPetById(Http.Request request, Long petId) ;
public Result updatePetHttp(Http.Request request, Pet body) {
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
updatePet(request, body);
return ok();
return ok();
}
public abstract void updatePet(Http.Request request, Pet body) ;
public Result updatePetWithFormHttp(Http.Request request, Long petId, String name, String status) {
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
updatePetWithForm(request, petId, name, status);
return ok();
return ok();
}
public abstract void updatePetWithForm(Http.Request request, Long petId, String name, String status) ;
public Result uploadFileHttp(Http.Request request, Long petId, String additionalMetadata, Http.MultipartFormData.FilePart<TemporaryFile> file) {
if (!securityAPIUtils.isRequestTokenValid(request, "petstore_auth")) {
return unauthorized();
}
ModelApiResponse obj = uploadFile(request, petId, additionalMetadata, file);
if (configuration.getBoolean("useOutputBeanValidation")) {
if (configuration.getBoolean("useOutputBeanValidation")) {
OpenAPIUtils.validate(obj);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}

View File

@@ -14,7 +14,9 @@ import play.mvc.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import openapitools.OpenAPIUtils;
import openapitools.SecurityAPIUtils;
import static play.mvc.Results.ok;
import static play.mvc.Results.unauthorized;
import play.libs.Files.TemporaryFile;
import javax.validation.constraints.*;
@@ -22,11 +24,12 @@ import javax.validation.constraints.*;
@SuppressWarnings("RedundantThrows")
public abstract class StoreApiControllerImpInterface {
@Inject private Config configuration;
@Inject private SecurityAPIUtils securityAPIUtils;
private ObjectMapper mapper = new ObjectMapper();
public Result deleteOrderHttp(Http.Request request, String orderId) {
deleteOrder(request, orderId);
return ok();
return ok();
}
@@ -34,8 +37,9 @@ return ok();
public Result getInventoryHttp(Http.Request request) {
Map<String, Integer> obj = getInventory(request);
JsonNode result = mapper.valueToTree(obj);
return ok(result);
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
@@ -43,11 +47,14 @@ return ok(result);
public Result getOrderByIdHttp(Http.Request request, @Min(1) @Max(5)Long orderId) {
Order obj = getOrderById(request, orderId);
if (configuration.getBoolean("useOutputBeanValidation")) {
if (configuration.getBoolean("useOutputBeanValidation")) {
OpenAPIUtils.validate(obj);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
@@ -55,11 +62,14 @@ return ok(result);
public Result placeOrderHttp(Http.Request request, Order body) {
Order obj = placeOrder(request, body);
if (configuration.getBoolean("useOutputBeanValidation")) {
if (configuration.getBoolean("useOutputBeanValidation")) {
OpenAPIUtils.validate(obj);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}

View File

@@ -15,7 +15,9 @@ import play.mvc.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import openapitools.OpenAPIUtils;
import openapitools.SecurityAPIUtils;
import static play.mvc.Results.ok;
import static play.mvc.Results.unauthorized;
import play.libs.Files.TemporaryFile;
import javax.validation.constraints.*;
@@ -23,11 +25,12 @@ import javax.validation.constraints.*;
@SuppressWarnings("RedundantThrows")
public abstract class UserApiControllerImpInterface {
@Inject private Config configuration;
@Inject private SecurityAPIUtils securityAPIUtils;
private ObjectMapper mapper = new ObjectMapper();
public Result createUserHttp(Http.Request request, User body) {
createUser(request, body);
return ok();
return ok();
}
@@ -35,7 +38,7 @@ return ok();
public Result createUsersWithArrayInputHttp(Http.Request request, List<User> body) {
createUsersWithArrayInput(request, body);
return ok();
return ok();
}
@@ -43,7 +46,7 @@ return ok();
public Result createUsersWithListInputHttp(Http.Request request, List<User> body) {
createUsersWithListInput(request, body);
return ok();
return ok();
}
@@ -51,7 +54,7 @@ return ok();
public Result deleteUserHttp(Http.Request request, String username) {
deleteUser(request, username);
return ok();
return ok();
}
@@ -59,11 +62,14 @@ return ok();
public Result getUserByNameHttp(Http.Request request, String username) {
User obj = getUserByName(request, username);
if (configuration.getBoolean("useOutputBeanValidation")) {
if (configuration.getBoolean("useOutputBeanValidation")) {
OpenAPIUtils.validate(obj);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
@@ -71,8 +77,9 @@ return ok(result);
public Result loginUserHttp(Http.Request request, @NotNull String username, @NotNull String password) {
String obj = loginUser(request, username, password);
JsonNode result = mapper.valueToTree(obj);
return ok(result);
JsonNode result = mapper.valueToTree(obj);
return ok(result);
}
@@ -80,7 +87,7 @@ return ok(result);
public Result logoutUserHttp(Http.Request request) {
logoutUser(request);
return ok();
return ok();
}
@@ -88,7 +95,7 @@ return ok();
public Result updateUserHttp(Http.Request request, String username, User body) {
updateUser(request, username, body);
return ok();
return ok();
}

View File

@@ -0,0 +1,165 @@
package openapitools;
import com.auth0.jwk.Jwk;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.typesafe.config.Config;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import play.mvc.Http;
import java.net.URL;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@Singleton
public class SecurityAPIUtils {
private final String bearerPrefix = "Bearer ";
private final ObjectMapper mapper;
private boolean useOnlineValidation = false;
// Online validation
private HashMap<String, String> tokenIntrospectEndpoints = new HashMap<>();
private final String clientId;
private final String clientSecret;
// Offline validation
private HashMap<String, String> jwksEndpoints = new HashMap<>();
private String tokenKeyId = "";
private JWTVerifier tokenVerifier; //Reusable verifier instance until tokenKeyId changes.
@Inject
SecurityAPIUtils(Config configuration) {
mapper = new ObjectMapper();
clientId = configuration.hasPath("oauth.clientId") ? configuration.getString("oauth.clientId") : "";
clientSecret = configuration.hasPath("oauth.clientSecret") ? configuration.getString("oauth.clientSecret") : "";
tokenIntrospectEndpoints.put("petstore_auth", "");
jwksEndpoints.put("petstore_auth", "");
}
private boolean isRequestTokenValidByOnlineCheck(Http.Request request, String securityMethodName) {
try {
Optional<String> authToken = request.getHeaders().get(HttpHeaders.AUTHORIZATION);
if (authToken.isPresent()) {
String tokenWithoutBearerPrefix = authToken.get().substring(bearerPrefix.length());
HttpClientBuilder builder = HttpClientBuilder.create();
HttpClient httpClient = builder.build();
HttpPost httppost = new HttpPost(this.tokenIntrospectEndpoints.get(securityMethodName));
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("token", tokenWithoutBearerPrefix));
params.add(new BasicNameValuePair("client_id", clientId));
params.add(new BasicNameValuePair("client_secret", clientSecret));
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpResponse response = httpClient.execute(httppost);
String responseJsonString = EntityUtils.toString(response.getEntity());
HashMap responseJsonObject = mapper.readValue(responseJsonString, HashMap.class);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK && (boolean) responseJsonObject.get("active");
}
} catch (Exception exception) {
return false;
}
return false;
}
private boolean isRequestTokenValidByOfflineCheck(Http.Request request, String securityMethodName) {
try {
Optional<String> authHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION);
if (authHeader.isPresent()) {
String bearerToken = authHeader.get().substring(bearerPrefix.length());
return isTokenValidByOfflineCheck(bearerToken, securityMethodName);
}
} catch (Exception exception) {
return false;
}
return false;
}
public boolean isTokenValidByOfflineCheck(String bearerToken, String securityMethodName) {
try {
DecodedJWT jwt = JWT.decode(bearerToken);
String issuer = jwt.getIssuer();
String keyId = jwt.getKeyId();
if (!tokenKeyId.equals(keyId)) {
if (securityMethodName == null) {
securityMethodName = jwksEndpoints.keySet().stream().findFirst().get();
}
Jwk jwk = new UrlJwkProvider(new URL(this.jwksEndpoints.get(securityMethodName))).get(keyId);
final PublicKey publicKey = jwk.getPublicKey();
if (!(publicKey instanceof RSAPublicKey)) {
throw new IllegalArgumentException(String.format("Key with ID %s was found in JWKS but is not a RSA-key.", keyId));
}
Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) publicKey, null);
tokenVerifier = JWT.require(algorithm)
.withIssuer(issuer)
.build();
tokenKeyId = keyId;
}
DecodedJWT verifiedJWT = tokenVerifier.verify(bearerToken);
return true;
} catch (Exception exception) {
return false;
}
}
public String getOAuthUserIdFromRequestToken(Http.Request requestWithPreviouslyVerifiedToken) {
try {
Optional<String> authHeader = requestWithPreviouslyVerifiedToken.getHeaders().get(HttpHeaders.AUTHORIZATION);
if (authHeader.isPresent()) {
String bearerToken = authHeader.get().substring(bearerPrefix.length());
return getOAuthUserIdFromToken(bearerToken);
}
} catch (Exception exception) {
return null;
}
return null;
}
public String getOAuthUserIdFromToken(String bearerToken) {
try {
DecodedJWT jwt = JWT.decode(bearerToken);
return jwt.getSubject();
} catch (Exception exception) {
return null;
}
}
public boolean isRequestTokenValid(Http.Request request, String securityMethodName) {
return useOnlineValidation ? isRequestTokenValidByOnlineCheck(request, securityMethodName) : isRequestTokenValidByOfflineCheck(request, securityMethodName);
}
}

View File

@@ -9,3 +9,6 @@ scalaVersion := "2.12.6"
libraryDependencies += "org.webjars" % "swagger-ui" % "3.32.5"
libraryDependencies += "javax.validation" % "validation-api" % "2.0.1.Final"
libraryDependencies += guice
libraryDependencies += "com.auth0" % "java-jwt" % "3.18.1"
libraryDependencies += "com.auth0" % "jwks-rsa" % "0.19.0"
libraryDependencies += "org.apache.httpcomponents" % "httpclient" % "4.5.6"