Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion custom-runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>..</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>custom-runtime</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion env-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>..</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>env-config</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion lambda-interceptor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>..</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>lambda-interceptor</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion lambda-invoker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>..</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>lambda-invoker</artifactId>
Expand Down
58 changes: 58 additions & 0 deletions lambda-utility/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!--
~ Copyright (c) 2016 Network New Technologies Inc.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>lambda-utility</artifactId>
<packaging>jar</packaging>
<description>Lambda function utility module</description>

<dependencies>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>utility</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.networknt.aws.lambda.utility;

public class HeaderKey {

/* unique header keys used by light-4j */
public static final String TRACEABILITY = "X-Traceability-Id";
public static final String CORRELATION = "X-Correlation-Id";
public static final String AUTHORIZATION = "Authorization";
public static final String SCOPE_TOKEN = "X-Scope-Token";
/* common header keys */
public static final String CONTENT_TYPE = "Content-Type";
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
/* Amazon header keys */
public static final String PARAMETER_SECRET_TOKEN = "X-Aws-Parameters-Secrets-Token";
public static final String AMZ_TARGET = "X-Amz-Target";

public static final String SERVICE_ID = "service_id";
public static final String SERVICE_URL = "service_url";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.networknt.aws.lambda.utility;

public class HeaderValue {

public static final String APPLICATION_JSON = "application/json";
public static final String APPLICATION_AMZ = "application/x-amz-json-1.1";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.networknt.aws.lambda.utility;

public class LambdaEnvVariables {
public static final String LAMBDA_SESSION_TOKEN = "AWS_SESSION_TOKEN";
public static final String AWS_REGION = "AWS_REGION";
public static final String CLEAR_AWS_DYNAMO_DB_TABLES = "CLEAR_AWS_DYNAMO_DB_TABLES";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.networknt.aws.lambda.utility;

public class LoggerKey {
public static final String TRACEABILITY = "tid";
public static final String CORRELATION = "cid";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.networknt.aws.lambda.utility;

import org.junit.jupiter.api.Test;

public class HeaderKeyTest {
@Test
public void testHeaderKey() {
assert HeaderKey.TRACEABILITY.equals("X-Traceability-Id");
}
}
72 changes: 72 additions & 0 deletions lambda-utility/src/test/resources/logback-test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2016 Network New Technologies Inc.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<configuration>
TODO create logger for audit only.
http://stackoverflow.com/questions/2488558/logback-to-log-different-messages-to-two-files
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Marker>PROFILER</Marker>
<!--<OnMatch>DENY</OnMatch>-->
<OnMatch>NEUTRAL</OnMatch>
</turboFilter>

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5marker %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<appender name="log" class="ch.qos.logback.core.FileAppender">
<File>target/test.log</File>
<Append>false</Append>
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %class{36}:%L %M - %msg%n</Pattern>
</layout>
</appender>

<!--audit log-->
<appender name="audit" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>target/audit.log</file> <!-- logfile location -->
<encoder>
<pattern>%-5level [%thread] %date{ISO8601} %F:%L - %msg%n</pattern> <!-- the layout pattern used to format log entries -->
<immediateFlush>true</immediateFlush>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>target/audit.log.%i.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex> <!-- max number of archived logs that are kept -->
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>200MB</maxFileSize> <!-- The size of the logfile that triggers a switch to a new logfile, and the current one archived -->
</triggeringPolicy>
</appender>

<root level="info">
<appender-ref ref="stdout" />
</root>

<logger name="com.networknt" level="info">
<appender-ref ref="log"/>
</logger>

<logger name="Audit" level="info" additivity="false">
<appender-ref ref="audit"/>
</logger>

</configuration>
16 changes: 12 additions & 4 deletions lambda-validator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,33 @@
<groupId>com.networknt</groupId>
<artifactId>light-aws-lambda</artifactId>
<version>2.1.35-SNAPSHOT</version>
<relativePath>..</relativePath>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>lambda-validator</artifactId>
<packaging>jar</packaging>
<description>Validate the lambda funcation request against the OpenAPI specification</description>

<dependencies>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>utility</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>status</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>openapi-parser</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<artifactId>schema-validator</artifactId>
</dependency>
<dependency>
<groupId>com.mservicetech</groupId>
<artifactId>openapi-schema-validation</artifactId>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.mservicetech.openapi.common.RequestEntity;
import com.mservicetech.openapi.common.Status;

import com.mservicetech.openapi.validation.OpenApiValidator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.networknt.aws.lambda.validator.RequestValidator;
import com.networknt.aws.lambda.validator.SchemaValidator;
import com.networknt.config.Config;
import com.networknt.oas.model.Operation;
import com.networknt.oas.model.Path;
import com.networknt.openapi.*;
import com.networknt.status.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -24,28 +28,70 @@
*/
public class LambdaSchemaValidator {
static final Logger logger = LoggerFactory.getLogger(LambdaSchemaValidator.class);
private static final String STATUS_METHOD_NOT_ALLOWED = "ERR10008";

static final String CONTENT_TYPE = "application/json";
private static final String CONFIG_NAME = "openapi";
private static final String SPEC_INJECT = "openapi-inject";
public static ValidatorConfig config;
public static OpenApiHelper helper;
RequestValidator requestValidator;


public LambdaSchemaValidator() {
if (logger.isInfoEnabled()) logger.info("LambdaSchemaValidator is constructed");
config = ValidatorConfig.load();
Map<String, Object> inject = Config.getInstance().getJsonMapConfig(SPEC_INJECT);
Map<String, Object> openapi = Config.getInstance().getJsonMapConfigNoCache(CONFIG_NAME);
openapi = OpenApiHelper.merge(openapi, inject);
try {
String openapiString = Config.getInstance().getMapper().writeValueAsString(openapi);
if(logger.isTraceEnabled()) logger.trace("OpenApiMiddleware openapiString: " + openapiString);
helper = new OpenApiHelper(openapiString);
} catch (JsonProcessingException e) {
logger.error("merge specification failed");
throw new RuntimeException("merge specification failed");
}
final SchemaValidator schemaValidator = new SchemaValidator(helper.openApi3);
this.requestValidator = new RequestValidator(schemaValidator, config);
}

/**
* Validate the request based on the openapi.yaml specification
*
* @param requestEvent request event
* @return responseEvent if error and null if pass.
*/
public APIGatewayProxyResponseEvent validateRequest(APIGatewayProxyRequestEvent requestEvent) {
OpenApiValidator openApiValidator = new OpenApiValidator("openapi.yaml");
RequestEntity requestEntity = new RequestEntity();
requestEntity.setQueryParameters(requestEvent.getQueryStringParameters());
requestEntity.setPathParameters(requestEvent.getPathParameters());
requestEntity.setHeaderParameters(requestEvent.getHeaders());
if (requestEvent.getBody()!=null) {
requestEntity.setRequestBody(requestEvent.getBody());
requestEntity.setContentType(CONTENT_TYPE);
if(logger.isTraceEnabled()) logger.trace("validateRequest starts");
String reqPath = requestEvent.getPath();
// if request path is in the skipPathPrefixes in the config, call the next handler directly to skip the validation.
if(config.getSkipPathPrefixes() != null && config.getSkipPathPrefixes().stream().anyMatch(s -> reqPath.startsWith(s))) {
if (logger.isDebugEnabled()) {
logger.debug("validateRequest ends with skipped path {}", reqPath);
}
return null;
}
final NormalisedPath requestPath = new ApiNormalisedPath(reqPath, getBasePath(reqPath));
if (logger.isTraceEnabled()) {
logger.trace("requestPath original {} and normalized {}", requestPath.original(), requestPath.normalised());
}
final Optional<NormalisedPath> maybeApiPath = helper.findMatchingApiPath(requestPath);

final NormalisedPath openApiPathString = maybeApiPath.get();
final Path path = helper.openApi3.getPath(openApiPathString.original());

final String httpMethod = requestEvent.getHttpMethod().toLowerCase();
final Operation operation = path.getOperation(httpMethod);

if (operation == null) {
Status status = new Status(STATUS_METHOD_NOT_ALLOWED, httpMethod, openApiPathString.normalised());
return createErrorResponse(status.getStatusCode(), status.getCode(), status.getDescription());
}
Status status = openApiValidator.validateRequestPath(requestEvent.getPath(), requestEvent.getHttpMethod(), requestEntity);

// This handler can identify the openApiOperation and endpoint only. Other info will be added by JwtVerifyHandler.
final OpenApiOperation openApiOperation = new OpenApiOperation(openApiPathString, path, httpMethod, operation);
Status status = requestValidator.validateRequest(requestPath, requestEvent, openApiOperation);
if (status !=null) {
return createErrorResponse(status.getStatusCode(), status.getCode(), status.getDescription());
}
Expand All @@ -65,5 +111,15 @@ private APIGatewayProxyResponseEvent createErrorResponse(int statusCode, String
.withBody(body);
}


// this is used to get the basePath from the OpenApiMiddleware.
public static String getBasePath(String requestPath) {
String basePath = "";
// assume there is a single spec.
if (helper != null) {
basePath = helper.basePath;
if (logger.isTraceEnabled())
logger.trace("Found basePath for single spec from OpenApiMiddleware helper: {}", basePath);
}
return basePath;
}
}
Loading