├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ingesting click logs from web application.png ├── source └── clicklogger │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── clicklogs │ │ │ ├── Authorizer │ │ │ ├── AuthPolicy.java │ │ │ └── TokenAuthorizerContext.java │ │ │ ├── Handlers │ │ │ ├── APIGatewayAuthorizerHandler.java │ │ │ ├── ClickLoggerHandler.java │ │ │ └── ClickLoggerStreamHandler.java │ │ │ └── model │ │ │ ├── ClickLogRequest.java │ │ │ ├── ClickLogResponse.java │ │ │ └── ResponseBuilder.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── clicklogs │ ├── ClickLoggerHandlerTest.java │ └── ClickLoggerStreamHanderTest.java └── terraform └── templates ├── apigateway.tf ├── cloudwatch.tf ├── configs.tf ├── dynamodb.tf ├── environments.tf ├── firehose.tf ├── glue.tf ├── lambda.tf ├── policies.tf ├── providers.tf ├── roles.tf └── s3.tf /.gitignore: -------------------------------------------------------------------------------- 1 | source/clicklogger/src/main/.DS_Store 2 | source/clicklogger/src/main/java/.DS_Store 3 | source/clicklogger/src/main/java/com/.DS_Store 4 | source/clicklogger/src/main/java/com/clicklogs/.DS_Store 5 | source/clicklogger/src/main/.DS_Store 6 | source/clicklogger/src/main/java/.DS_Store 7 | source/clicklogger/src/main/java/com/.DS_Store 8 | source/clicklogger/src/main/java/com/clicklogs/.DS_Store 9 | terraform/templates/terraform.tfstate 10 | terraform/templates/.terraform/plugins/darwin_amd64/lock.json 11 | terraform/templates/.terraform/plugins/darwin_amd64/terraform-provider-aws_v2.61.0_x4 12 | terraform/templates/.terraform.tfstate.lock.info 13 | source/clicklogger/src/test/.DS_Store 14 | source/clicklogger/src/test/java/.DS_Store 15 | source/clicklogger/src/test/java/com/.DS_Store 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ingesting Web Application Click Logs into AWS using Terraform (By HashiCorp) 2 | 3 | This post provides an API based ingestion application system for websites & applications to push user interactions, click actions from their website into AWS. The ingestion process will be exposed using a web-based interaction with an API Gateway endpoint. 4 | 5 | The Amazon API Gateway (https://aws.amazon.com/api-gateway/) processes the incoming data into an AWS Lambda (https://aws.amazon.com/lambda/) during which the system validates the request using a Lambda Authorizer and pushes the data to a Amazon Kinesis Data Firehose (https://aws.amazon.com/kinesis/data-firehose/). Leverage Firehose’s capability to convert the incoming data and convert it into a Parquet file before pushing it to Amazon S3 (https://aws.amazon.com/s3/). AWS Glue catalog (https://aws.amazon.com/glue/) is used for the conversion. Additionally, a transformational/consumer lambda does additional processing by pushing it to Amazon DynamoDB (https://aws.amazon.com/dynamodb/). 6 | 7 | The data hosted in Amazon S3 (Parquet file) and DynamoDB can be eventually used for generating reports and metrics depending on customer needs to monitor user experience, behavior and additionally provide better recommendations on their website. 8 | 9 | The following steps provide an overview of this implementation: 10 | 11 | 1. Java source build – Provided code is packaged & build using Apache Maven (https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html) 12 | 13 | 2. Terraform commands are initiated (provided below) to deploy the infrastructure in AWS. 14 | 3. An API Gateway, S3 bucket, Dynamo table, following Lambdas are built and deployed in AWS. 15 | a. Lambda Authorizer – This lambda validates the incoming request for header authorization from API gateway to processing lambda. 16 | * ClickLogger Lamba – This lambda processes the incoming request and pushes the data into Firehose stream 17 | * Transformational Lambda – This lambda listens to the Firehose stream data and processes this to DynamoDB. In real world these lambda can more additional filtering, processing etc., 18 | b. Once the data “POST” is performed to the API Gateway exposed endpoint, the data traverses through the lambda and Firehose stream converts the incoming stream into a Parquet file. We use AWS Glue to perform this operation. 19 | c. The incoming click logs are eventually saved as Parquet files in S3 bucket and additionally in the DynamoDB 20 | 21 | ![Alt text](ingesting%20click%20logs%20from%20web%20application.png?raw=true "Title") 22 | 23 | ### Prerequisites 24 | 25 | - Make sure to have Java installed and running on your machine. For instructions, see Java Development Kit (https://www.oracle.com/java/technologies/javase-downloads.html) 26 | - Set up Terraform. For steps, see Terraform downloads (https://www.terraform.io/downloads.html). 27 | 28 | ### Steps 29 | 30 | 1. Clone this repository and execute the below command to spin up the infrastructure and the application 31 | 32 | 2. Execute the below commands 33 | 34 | ``` 35 | $ cd aws-ingesting-click-logs-using-terraform 36 | $ cd source\clicklogger 37 | $ mvn clean package 38 | $ cd .. 39 | $ cd .. 40 | $ cd terraform\templates 41 | $ terraform init 42 | $ terraform plan 43 | $ terraform apply –auto-approve 44 | ``` 45 | 46 | ### Test 47 | 48 | 1. In AWS Console, select “API Gateway”. Select “click-logger-api” 49 | 2. Select “Stages” on the left pane 50 | 3. Click “dev” > “POST” (within the “/clicklogger” route) 51 | 4. Copy the invoke Url. A sample url will be like this - https://qvu8vlu0u4.execute-api.us-east-1.amazonaws.com/dev/clicklogger 52 | 5. Use REST API tool like Postman or Chrome based web extension like RestMan to post data to your endpoint 53 | 54 | Add Header: Key “Authorization” with value “ALLOW=ORDERAPP”. 55 | 56 | Sample Json Request: 57 | ``` 58 | { 59 | "requestid": "OAP-guid-05122020-1345-12345-678910", 60 | "contextid": "OAP-guid-05122020-1345-1234-5678", 61 | "callerid": "OrderingApplication", 62 | "component": "login", 63 | "action": "click", 64 | "type": "webpage" 65 | } 66 | ``` 67 | 6. Output - You should see the output in both S3 bucket and DynamoDB 68 | a. S3 – Navigate to the bucket created as part of the stack 69 | * Select the file and view the file from “Select From” sub tab . You should see something ingested stream got converted into parquet file. 70 | * Select the file and view the data 71 | b. DynamoDB table - Select “clickloggertable” and view the “items” to see data. 72 | 73 | ## Cleanup 74 | 75 | **Make sure to check the following are deleted before the delete stacks are performed** 76 | 77 | - Contents of the S3 files are deleted 78 | - Go to "Resources" tab, select the s3 bucket created as part of the stack and delete the S3 bucket manually. 79 | - Select all the contents & delete the contents manually 80 | 81 | 82 | **S3 and created services can be deleted using CLI also. Execute the below commands:** 83 | 84 | ``` 85 | # CLI Commands to delete the S3 86 | $ aws s3 rb s3://click-logger-firehose-delivery-bucket- --force 87 | $ terraform destroy –-auto-approve 88 | ``` 89 | 90 | 91 | ## References 92 | 93 | * Terraform: Beyond the basics with AWS 94 | https://aws.amazon.com/blogs/apn/terraform-beyond-the-basics-with-aws/ 95 | 96 | * API Management strategies 97 | https://aws.amazon.com/api-gateway/api-management/ 98 | 99 | * Amazon Kinesis 100 | https://aws.amazon.com/kinesis/ 101 | 102 | * API Gateway with Lambda Authorizers 103 | https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html 104 | 105 | * Kinesis Data Streams (KDS) 106 | https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html 107 | 108 | 109 | 110 | 111 | 112 | 113 | ## License 114 | 115 | This library is licensed under the MIT-0 License. See the LICENSE file. 116 | -------------------------------------------------------------------------------- /ingesting click logs from web application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-ingesting-click-logs-using-terraform/d2f16b864b4fad90c92cb68d642bd20d505e5d60/ingesting click logs from web application.png -------------------------------------------------------------------------------- /source/clicklogger/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /source/clicklogger/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /source/clicklogger/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-ingesting-click-logs-using-terraform/d2f16b864b4fad90c92cb68d642bd20d505e5d60/source/clicklogger/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /source/clicklogger/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /source/clicklogger/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /source/clicklogger/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /source/clicklogger/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.clicklogs 5 | clicklogger 6 | jar 7 | 1.0-SNAPSHOT 8 | clicklogger 9 | 10 | UTF-8 11 | 1.8 12 | 1.8 13 | 14 | 15 | 16 | com.amazonaws 17 | aws-lambda-java-core 18 | 1.2.1 19 | 20 | 21 | com.amazonaws 22 | aws-lambda-java-events 23 | 2.2.8 24 | 25 | 26 | com.amazonaws 27 | aws-java-sdk-dynamodb 28 | 1.11.774 29 | 30 | 31 | com.amazonaws 32 | aws-java-sdk-kinesis 33 | 1.11.774 34 | 35 | 40 | 41 | org.apache.commons 42 | commons-lang3 43 | 3.10 44 | 45 | 46 | com.google.code.gson 47 | gson 48 | 2.8.9 49 | 50 | 51 | org.apache.logging.log4j 52 | log4j-api 53 | 2.17.1 54 | test 55 | 56 | 57 | org.apache.logging.log4j 58 | log4j-core 59 | 2.17.1 60 | test 61 | 62 | 63 | org.apache.logging.log4j 64 | log4j-slf4j18-impl 65 | 2.13.0 66 | test 67 | 68 | 69 | org.junit.jupiter 70 | junit-jupiter-api 71 | 5.6.0 72 | test 73 | 74 | 75 | org.junit.jupiter 76 | junit-jupiter-engine 77 | 5.6.0 78 | test 79 | 80 | 86 | 87 | org.mockito 88 | mockito-core 89 | 2.22.0 90 | test 91 | 92 | 93 | junit 94 | junit 95 | 4.13.1 96 | 97 | 98 | 99 | 100 | 101 | 102 | maven-surefire-plugin 103 | 2.22.2 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-shade-plugin 108 | 3.2.2 109 | 110 | false 111 | 112 | 113 | 114 | package 115 | 116 | shade 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-compiler-plugin 124 | 3.8.1 125 | 126 | 1.8 127 | 1.8 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/Authorizer/AuthPolicy.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.Authorizer; 2 | 3 | 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class AuthPolicy { 11 | 12 | public static final String VERSION = "Version"; 13 | public static final String STATEMENT = "Statement"; 14 | public static final String EFFECT = "Effect"; 15 | public static final String ACTION = "Action"; 16 | public static final String NOT_ACTION = "NotAction"; 17 | public static final String RESOURCE = "Resource"; 18 | public static final String NOT_RESOURCE = "NotResource"; 19 | public static final String CONDITION = "Condition"; 20 | 21 | String principalId; 22 | transient AuthPolicy.PolicyDocument policyDocumentObject; 23 | Map policyDocument; 24 | 25 | public AuthPolicy(String principalId, AuthPolicy.PolicyDocument policyDocumentObject) { 26 | this.principalId = principalId; 27 | this.policyDocumentObject = policyDocumentObject; 28 | } 29 | 30 | public AuthPolicy() { 31 | } 32 | 33 | public String getPrincipalId() { 34 | return principalId; 35 | } 36 | 37 | public void setPrincipalId(String principalId) { 38 | this.principalId = principalId; 39 | } 40 | 41 | public Map getPolicyDocument() { 42 | Map serializablePolicy = new HashMap<>(); 43 | serializablePolicy.put(VERSION, policyDocumentObject.Version); 44 | Statement[] statements = policyDocumentObject.getStatement(); 45 | Map[] serializableStatementArray = new Map[statements.length]; 46 | for (int i = 0; i < statements.length; i++) { 47 | Map serializableStatement = new HashMap<>(); 48 | AuthPolicy.Statement statement = statements[i]; 49 | serializableStatement.put(EFFECT, statement.Effect); 50 | serializableStatement.put(ACTION, statement.Action); 51 | serializableStatement.put(RESOURCE, statement.getResource()); 52 | serializableStatement.put(CONDITION, statement.getCondition()); 53 | serializableStatementArray[i] = serializableStatement; 54 | } 55 | serializablePolicy.put(STATEMENT, serializableStatementArray); 56 | return serializablePolicy; 57 | } 58 | 59 | public void setPolicyDocument(PolicyDocument policyDocumentObject) { 60 | this.policyDocumentObject = policyDocumentObject; 61 | } 62 | 63 | public static class PolicyDocument { 64 | 65 | static final String EXECUTE_API_ARN_FORMAT = "arn:aws:execute-api:%s:%s:%s/%s/%s/%s"; 66 | 67 | String Version = "2012-10-17"; 68 | 69 | private Statement allowStatement; 70 | private Statement denyStatement; 71 | private List statements; 72 | 73 | // context metadata 74 | transient String region; 75 | transient String awsAccountId; 76 | transient String restApiId; 77 | transient String stage; 78 | 79 | /** 80 | * Creates a new PolicyDocument with the given context, 81 | * and initializes two base Statement objects for allowing and denying access to API Gateway methods 82 | * 83 | * @param region the region where the RestApi is configured 84 | * @param awsAccountId the AWS Account ID that owns the RestApi 85 | * @param restApiId the RestApi identifier 86 | * @param stage and the Stage on the RestApi that the Policy will apply to 87 | */ 88 | public PolicyDocument(String region, String awsAccountId, String restApiId, String stage) { 89 | this.region = region; 90 | this.awsAccountId = awsAccountId; 91 | this.restApiId = restApiId; 92 | this.stage = stage; 93 | allowStatement = Statement.getEmptyInvokeStatement("Allow"); 94 | denyStatement = Statement.getEmptyInvokeStatement("Deny"); 95 | this.statements = new ArrayList<>(); 96 | statements.add(allowStatement); 97 | statements.add(denyStatement); 98 | } 99 | 100 | public String getVersion() { 101 | return Version; 102 | } 103 | 104 | public void setVersion(String version) { 105 | Version = version; 106 | } 107 | 108 | public AuthPolicy.Statement[] getStatement() { 109 | return statements.toArray(new AuthPolicy.Statement[statements.size()]); 110 | } 111 | 112 | public void allowMethod(HttpMethod httpMethod, String resourcePath) { 113 | addResourceToStatement(allowStatement, httpMethod, resourcePath); 114 | } 115 | 116 | public void denyMethod(HttpMethod httpMethod, String resourcePath) { 117 | addResourceToStatement(denyStatement, httpMethod, resourcePath); 118 | } 119 | 120 | public void addStatement(AuthPolicy.Statement statement) { 121 | statements.add(statement); 122 | } 123 | 124 | private void addResourceToStatement(Statement statement, HttpMethod httpMethod, String resourcePath) { 125 | if (resourcePath.equals("/")) { 126 | resourcePath = ""; 127 | } 128 | String resource = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; 129 | String method = httpMethod == HttpMethod.ALL ? "*" : httpMethod.toString(); 130 | statement.addResource(String.format(EXECUTE_API_ARN_FORMAT, region, awsAccountId, restApiId, stage, method, resource)); 131 | } 132 | 133 | // Static methods 134 | 135 | /** 136 | * Generates a new PolicyDocument with a single statement that allows the requested method/resourcePath 137 | * 138 | * @param region API Gateway region 139 | * @param awsAccountId AWS Account that owns the API Gateway RestApi 140 | * @param restApiId RestApi identifier 141 | * @param stage Stage name 142 | * @param method HttpMethod to allow 143 | * @param resourcePath Resource path to allow 144 | * @return new PolicyDocument that allows the requested method/resourcePath 145 | */ 146 | public static PolicyDocument getAllowOnePolicy(String region, String awsAccountId, String restApiId, String stage, HttpMethod method, String resourcePath) { 147 | AuthPolicy.PolicyDocument policyDocument = new AuthPolicy.PolicyDocument(region, awsAccountId, restApiId, stage); 148 | policyDocument.allowMethod(method, resourcePath); 149 | return policyDocument; 150 | 151 | } 152 | 153 | 154 | /** 155 | * Generates a new PolicyDocument with a single statement that denies the requested method/resourcePath 156 | * 157 | * @param region API Gateway region 158 | * @param awsAccountId AWS Account that owns the API Gateway RestApi 159 | * @param restApiId RestApi identifier 160 | * @param stage Stage name 161 | * @param method HttpMethod to deny 162 | * @param resourcePath Resource path to deny 163 | * @return new PolicyDocument that denies the requested method/resourcePath 164 | */ 165 | public static PolicyDocument getDenyOnePolicy(String region, String awsAccountId, String restApiId, String stage, HttpMethod method, String resourcePath) { 166 | AuthPolicy.PolicyDocument policyDocument = new AuthPolicy.PolicyDocument(region, awsAccountId, restApiId, stage); 167 | policyDocument.denyMethod(method, resourcePath); 168 | return policyDocument; 169 | 170 | } 171 | 172 | public static AuthPolicy.PolicyDocument getAllowAllPolicy(String region, String awsAccountId, String restApiId, String stage) { 173 | return getAllowOnePolicy(region, awsAccountId, restApiId, stage, HttpMethod.ALL, "*"); 174 | } 175 | 176 | public static PolicyDocument getDenyAllPolicy(String region, String awsAccountId, String restApiId, String stage) { 177 | return getDenyOnePolicy(region, awsAccountId, restApiId, stage, HttpMethod.ALL, "*"); 178 | } 179 | } 180 | 181 | public enum HttpMethod { 182 | GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, ALL 183 | } 184 | 185 | static class Statement { 186 | 187 | String Effect; 188 | String Action; 189 | Map> Condition; 190 | 191 | private List resourceList; 192 | 193 | public Statement() { 194 | 195 | } 196 | 197 | public Statement(String effect, String action, List resourceList, Map> condition) { 198 | this.Effect = effect; 199 | this.Action = action; 200 | this.resourceList = resourceList; 201 | this.Condition = condition; 202 | } 203 | 204 | public static Statement getEmptyInvokeStatement(String effect) { 205 | return new Statement(effect, "execute-api:Invoke", new ArrayList<>(), new HashMap<>()); 206 | } 207 | 208 | public String getEffect() { 209 | return Effect; 210 | } 211 | 212 | public void setEffect(String effect) { 213 | this.Effect = effect; 214 | } 215 | 216 | public String getAction() { 217 | return Action; 218 | } 219 | 220 | public void setAction(String action) { 221 | this.Action = action; 222 | } 223 | 224 | public String[] getResource() { 225 | return resourceList.toArray(new String[resourceList.size()]); 226 | } 227 | 228 | public void addResource(String resource) { 229 | resourceList.add(resource); 230 | } 231 | 232 | public Map> getCondition() { 233 | return Condition; 234 | } 235 | 236 | public void addCondition(String operator, String key, Object value) { 237 | Condition.put(operator, Collections.singletonMap(key, value)); 238 | } 239 | 240 | } 241 | 242 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/Authorizer/TokenAuthorizerContext.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.Authorizer; 2 | 3 | public class TokenAuthorizerContext { 4 | 5 | String type; 6 | String authorizationToken; 7 | String methodArn; 8 | 9 | /** 10 | * JSON input is deserialized into this object representation 11 | * @param type Static value - TOKEN 12 | * @param authorizationToken - Incoming bearer token sent by a client 13 | * @param methodArn - The API Gateway method ARN that a client requested 14 | */ 15 | public TokenAuthorizerContext(String type, String authorizationToken, String methodArn) { 16 | this.type = type; 17 | this.authorizationToken = authorizationToken; 18 | this.methodArn = methodArn; 19 | } 20 | 21 | public TokenAuthorizerContext() { 22 | 23 | } 24 | 25 | public String getType() { 26 | return type; 27 | } 28 | 29 | public void setType(String type) { 30 | this.type = type; 31 | } 32 | 33 | public String getAuthorizationToken() { 34 | return authorizationToken; 35 | } 36 | 37 | public void setAuthorizationToken(String authorizationToken) { 38 | this.authorizationToken = authorizationToken; 39 | } 40 | 41 | public String getMethodArn() { 42 | return methodArn; 43 | } 44 | 45 | public void setMethodArn(String methodArn) { 46 | this.methodArn = methodArn; 47 | } 48 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/Handlers/APIGatewayAuthorizerHandler.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.Handlers; 2 | 3 | import java.util.Arrays; 4 | import com.clicklogs.Authorizer.AuthPolicy; 5 | import com.clicklogs.Authorizer.TokenAuthorizerContext; 6 | 7 | import com.amazonaws.services.lambda.runtime.Context; 8 | import com.amazonaws.services.lambda.runtime.RequestHandler; 9 | 10 | public class APIGatewayAuthorizerHandler implements RequestHandler { 11 | 12 | @Override 13 | public AuthPolicy handleRequest(TokenAuthorizerContext input, Context context) { 14 | 15 | System.out.println("Reached lambda authorizer"); 16 | String token = input.getAuthorizationToken(); 17 | System.out.println("received token - " + token); 18 | 19 | String env_auth_tokens = System.getenv("AUTH_TOKENS"); 20 | String[] env_token_split = env_auth_tokens.split(";"); 21 | 22 | Boolean is_valid_token = Arrays.asList(env_token_split).contains(token); 23 | String principalId = "xxxx"; 24 | 25 | if(!is_valid_token){ 26 | throw new RuntimeException("Unauthorized"); 27 | } 28 | 29 | String methodArn = input.getMethodArn(); 30 | String[] arnPartials = methodArn.split(":"); 31 | String region = arnPartials[3]; 32 | String awsAccountId = arnPartials[4]; 33 | String[] apiGatewayArnPartials = arnPartials[5].split("/"); 34 | String restApiId = apiGatewayArnPartials[0]; 35 | String stage = apiGatewayArnPartials[1]; 36 | 37 | System.out.println("methodArn - " + methodArn + " restApiId - " + restApiId); 38 | 39 | System.out.println("Reached lambda authorizer. Returning allow all policy"); 40 | return new AuthPolicy(principalId, AuthPolicy.PolicyDocument.getAllowAllPolicy(region, awsAccountId, restApiId, stage)); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/Handlers/ClickLoggerHandler.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.Handlers; 2 | 3 | import com.amazonaws.services.kinesisfirehose.model.Record; 4 | import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehose; 5 | import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehoseClientBuilder; 6 | import com.amazonaws.services.kinesisfirehose.model.PutRecordRequest; 7 | import com.amazonaws.services.kinesisfirehose.model.PutRecordResult; 8 | 9 | import com.amazonaws.services.lambda.runtime.Context; 10 | import com.amazonaws.services.lambda.runtime.RequestHandler; 11 | import com.amazonaws.services.lambda.runtime.LambdaLogger; 12 | 13 | import com.google.gson.Gson; 14 | import com.google.gson.GsonBuilder; 15 | import org.apache.commons.lang3.StringUtils; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.text.Format; 19 | import java.text.SimpleDateFormat; 20 | import java.util.Date; 21 | 22 | import com.clicklogs.model.ClickLogRequest; 23 | import com.clicklogs.model.ClickLogResponse; 24 | import com.clicklogs.model.ResponseBuilder; 25 | 26 | // Handler value: example.Handler 27 | public class ClickLoggerHandler implements RequestHandler { 28 | 29 | private String stream_name = "click-logger-firehose-delivery-stream"; 30 | private String region = "us-east-1"; 31 | 32 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 33 | 34 | @Override 35 | public ClickLogResponse handleRequest(final ClickLogRequest clickLogRequest, final Context context) { 36 | final LambdaLogger logger = context.getLogger(); 37 | final String success_response = new String("200 OK"); 38 | final String fail_response = new String("200 OK"); 39 | 40 | ResponseBuilder responseBuilder = new ResponseBuilder(); 41 | ClickLogResponse response = responseBuilder.badRequest(fail_response).build(); 42 | 43 | String req = clickLogRequest.getRequestid() + " - " + clickLogRequest.getCallerid() + " - " 44 | + clickLogRequest.getComponent() + " - " + clickLogRequest.getType() + " - " + clickLogRequest.getAction() 45 | + " - " + clickLogRequest.getUser() + " - " + clickLogRequest.getClientip() + " - " 46 | + clickLogRequest.getCreatedtime(); 47 | 48 | logger.log("Incoming request variables - " + req); 49 | 50 | String env_stream_name = System.getenv("STREAM_NAME"); 51 | if(!StringUtils.isBlank(env_stream_name)) 52 | { 53 | stream_name = env_stream_name; 54 | } 55 | 56 | String env_region = System.getenv("AWS_REGION");// System.getenv("REGION"); 57 | logger.log("Environment region name - " + env_region); 58 | if(!StringUtils.isBlank(env_region)) 59 | { 60 | region = env_region; 61 | } 62 | if (clickLogRequest != null) { 63 | logger.log("Validating inputs"); 64 | if (StringUtils.isBlank(clickLogRequest.getRequestid())) { 65 | logger.log("error occurred - requestid missing"); 66 | return response; 67 | } 68 | if (StringUtils.isBlank(clickLogRequest.getContextid())) { 69 | logger.log("error occurred - contextid missing"); 70 | return response; 71 | } 72 | if (StringUtils.isBlank(clickLogRequest.getCallerid())) { 73 | logger.log("error occurred - caller missing"); 74 | return response; 75 | } 76 | if (StringUtils.isBlank(clickLogRequest.getType())) { 77 | logger.log("error occurred - type missing"); 78 | return response; 79 | } 80 | if (StringUtils.isBlank(clickLogRequest.getAction())) { 81 | logger.log("error occurred - action missing"); 82 | return response; 83 | } 84 | if (StringUtils.isBlank(clickLogRequest.getComponent())) { 85 | logger.log("error occurred - component missing"); 86 | return response; 87 | } 88 | 89 | String user = "GUEST"; 90 | if (StringUtils.isBlank(clickLogRequest.getUser())) { 91 | logger.log("setting default user"); 92 | clickLogRequest.setUser(user); 93 | } 94 | 95 | String clientip = "APIGWY"; 96 | if (StringUtils.isBlank(clickLogRequest.getClientip())) { 97 | logger.log("setting default clientip"); 98 | clickLogRequest.setClientip(clientip); 99 | } 100 | 101 | String datetime = ""; 102 | if (StringUtils.isBlank(clickLogRequest.getCreatedtime())) { 103 | logger.log("setting default createdtime"); 104 | Format f = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss"); 105 | datetime = f.format(new Date()); 106 | clickLogRequest.setCreatedtime(datetime); 107 | } 108 | logger.log("Validated inputs"); 109 | } 110 | 111 | System.out.println("Calling updateclicklogs method for the received clicklogrequest"); 112 | 113 | updateClickLogRequestToStream(clickLogRequest); 114 | logger.log(success_response); 115 | responseBuilder = new ResponseBuilder(); 116 | responseBuilder.ok(); 117 | response = responseBuilder.originHeader("*").build(); 118 | return response; 119 | } 120 | 121 | private Boolean updateClickLogRequestToStream(ClickLogRequest clickLogRequest) { 122 | System.out.println("Inside updateClickLogRequestToStream method for the input"); 123 | try { 124 | 125 | AmazonKinesisFirehose amazonKinesisFirehoseClient = AmazonKinesisFirehoseClientBuilder.standard().withRegion(region).build(); 126 | 127 | PutRecordRequest putRecordRequest = new PutRecordRequest(); 128 | putRecordRequest.setDeliveryStreamName(stream_name); 129 | Gson gson = new Gson(); 130 | String messageJson = gson.toJson(clickLogRequest); 131 | System.out.println("gson - " + messageJson); 132 | Record record = new Record().withData(ByteBuffer.wrap(messageJson.toString().getBytes())); 133 | putRecordRequest.setRecord(record); 134 | PutRecordResult putRecordResult = amazonKinesisFirehoseClient.putRecord( putRecordRequest ); 135 | System.out.println("updated the stream for recordid - " + putRecordResult.getRecordId()); 136 | return true; 137 | } catch (Exception e) { 138 | System.out.println("Error occurred - " + e.getMessage()); 139 | } 140 | return false; 141 | } 142 | 143 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/Handlers/ClickLoggerStreamHandler.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.Handlers; 2 | 3 | import com.amazonaws.services.lambda.runtime.Context; 4 | import com.amazonaws.services.lambda.runtime.RequestHandler; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import com.amazonaws.services.lambda.runtime.LambdaLogger; 8 | 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | import com.google.gson.JsonSyntaxException; 12 | 13 | import java.text.Format; 14 | import java.text.SimpleDateFormat; 15 | import java.util.ArrayList; 16 | import java.util.Date; 17 | import java.util.List; 18 | 19 | import com.clicklogs.model.ClickLogRequest; 20 | 21 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 22 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; 23 | import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; 24 | import com.amazonaws.services.dynamodbv2.document.DynamoDB; 25 | import com.amazonaws.services.dynamodbv2.document.Item; 26 | import com.amazonaws.services.dynamodbv2.document.PutItemOutcome; 27 | import com.amazonaws.services.dynamodbv2.document.spec.PutItemSpec; 28 | import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException; 29 | import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; 30 | import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent; 31 | import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse.Record; 32 | import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse.Result; 33 | 34 | 35 | // Handler value: example.Handler 36 | public class ClickLoggerStreamHandler implements RequestHandler{ 37 | 38 | private DynamoDB dynamoDb; 39 | private String dynamo_table_name = "clickLoggertable"; 40 | private String region = "us-east-1"; 41 | 42 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 43 | 44 | @Override 45 | public KinesisAnalyticsInputPreprocessingResponse handleRequest(final KinesisFirehoseEvent event, final Context context) { 46 | final LambdaLogger logger = context.getLogger(); 47 | final String success_response = new String("200 OK"); 48 | logger.log("EVENT: " + gson.toJson(event)); 49 | 50 | 51 | String env_table = System.getenv("DB_TABLE"); 52 | if(!StringUtils.isBlank(env_table)) 53 | { 54 | dynamo_table_name = env_table; 55 | } 56 | 57 | String env_region = System.getenv("AWS_REGION");// System.getenv("REGION"); 58 | logger.log("Environment region name - " + env_region); 59 | if(!StringUtils.isBlank(env_region)) 60 | { 61 | region = env_region; 62 | } 63 | 64 | List records = new ArrayList<>(); 65 | 66 | 67 | event.getRecords().forEach(kinesisRecord -> { 68 | String clickJson = new String(kinesisRecord.getData().array()); 69 | logger.log("Individual record: " + kinesisRecord.getData()); 70 | Gson gson = new Gson(); 71 | try 72 | { 73 | ClickLogRequest clickLogRequest = gson.fromJson(clickJson, ClickLogRequest.class); 74 | 75 | String req = clickLogRequest.getRequestid() + " - " + clickLogRequest.getCallerid() + " - " + clickLogRequest.getComponent() + " - " 76 | + clickLogRequest.getType() + " - " + clickLogRequest.getAction() + " - " 77 | + clickLogRequest.getUser() + " - " + clickLogRequest.getClientip() + " - " + clickLogRequest.getCreatedtime(); 78 | 79 | Boolean valid_input = true; 80 | logger.log("Incoming request variables - " + req); 81 | 82 | if(clickLogRequest != null){ 83 | 84 | logger.log("Validating inputs"); 85 | if (StringUtils.isBlank(clickLogRequest.getRequestid())) { 86 | logger.log("error occurred - requestid missing"); 87 | valid_input = false; 88 | } 89 | if (StringUtils.isBlank(clickLogRequest.getContextid())) { 90 | logger.log("error occurred - contextid missing"); 91 | valid_input = false; 92 | } 93 | if (StringUtils.isBlank(clickLogRequest.getCallerid())) { 94 | logger.log("error occurred - caller missing"); 95 | valid_input = false; 96 | } 97 | if (StringUtils.isBlank(clickLogRequest.getType())) { 98 | logger.log("error occurred - type missing"); 99 | valid_input = false; 100 | } 101 | if (StringUtils.isBlank(clickLogRequest.getAction())) { 102 | logger.log("error occurred - action missing"); 103 | valid_input = false; 104 | } 105 | if (StringUtils.isBlank(clickLogRequest.getComponent())) { 106 | logger.log("error occurred - component missing"); 107 | valid_input = false; 108 | } 109 | 110 | String user = "GUEST"; 111 | if (StringUtils.isBlank(clickLogRequest.getUser())) { 112 | logger.log("setting default user"); 113 | clickLogRequest.setUser(user); 114 | } 115 | 116 | String clientip = "APIGWY"; 117 | if (StringUtils.isBlank(clickLogRequest.getClientip())) { 118 | logger.log("setting default clientip"); 119 | clickLogRequest.setClientip(clientip); 120 | } 121 | 122 | String datetime = ""; 123 | if (StringUtils.isBlank(clickLogRequest.getCreatedtime())) { 124 | logger.log("setting default createdtime"); 125 | Format f = new SimpleDateFormat("mm-dd-yyyy hh:mm:ss"); 126 | datetime = f.format(new Date()); 127 | clickLogRequest.setCreatedtime(datetime); 128 | } 129 | logger.log("Validated inputs"); 130 | } 131 | 132 | req = clickLogRequest.getRequestid() + " - " + clickLogRequest.getCallerid() + " - " + clickLogRequest.getComponent() + " - " 133 | + clickLogRequest.getType() + " - " + clickLogRequest.getAction() + " - " 134 | + clickLogRequest.getUser() + " - " + clickLogRequest.getClientip() + " - " + clickLogRequest.getCreatedtime(); 135 | 136 | logger.log("Modified request variables - " + req); 137 | logger.log("Valid Input - " + String.valueOf(valid_input)); 138 | System.out.println("Calling updateclicklogs method for the received clicklogrequest"); 139 | if(valid_input){ 140 | updateClickLogs(clickLogRequest); 141 | } 142 | 143 | Record record = new Record(); 144 | record.setRecordId(kinesisRecord.getRecordId()); 145 | record.setData(kinesisRecord.getData()); 146 | Result result = KinesisAnalyticsInputPreprocessingResponse.Result.Ok; 147 | record.setResult(result); 148 | records.add(record); 149 | } 150 | catch(JsonSyntaxException jsEx){ 151 | System.out.println(jsEx); 152 | } 153 | }); 154 | 155 | 156 | KinesisAnalyticsInputPreprocessingResponse response = new KinesisAnalyticsInputPreprocessingResponse(records); 157 | logger.log(success_response); 158 | return response; 159 | } 160 | 161 | private Boolean updateClickLogs(final ClickLogRequest clickLogRequest) { 162 | this.initDynamoDbClient(); 163 | updateClickData(clickLogRequest); 164 | System.out.println("Completed update click logs method"); 165 | return true; 166 | } 167 | 168 | private PutItemOutcome updateClickData(final ClickLogRequest clickLogRequest) 169 | throws ConditionalCheckFailedException { 170 | return dynamoDb.getTable(dynamo_table_name) 171 | .putItem( 172 | new PutItemSpec().withItem(new Item() 173 | .withString("requestid", clickLogRequest.getRequestid()) 174 | .withString("contextid", clickLogRequest.getContextid()) 175 | .withString("callerid", clickLogRequest.getCallerid()) 176 | .withString("type", clickLogRequest.getType()) 177 | .withString("component", clickLogRequest.getComponent()) 178 | .withString("action", clickLogRequest.getAction()) 179 | .withString("createdtime", clickLogRequest.getCreatedtime()) 180 | .withString("clientip", clickLogRequest.getClientip()) 181 | .withString("user", clickLogRequest.getUser()))); 182 | } 183 | 184 | private void initDynamoDbClient() { 185 | System.out.println("Inside DynamoDBClient method"); 186 | try { 187 | AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withRegion(region).build(); 188 | this.dynamoDb = new DynamoDB(client); 189 | System.out.println("set DynamoDBClient method"); 190 | } catch (Exception e) { 191 | System.out.println("Error occurred - " + e.getMessage()); 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/model/ClickLogRequest.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.model; 2 | 3 | public class ClickLogRequest { 4 | 5 | public String requestid; 6 | public String contextid; 7 | public String callerid; 8 | public String type; 9 | public String component; 10 | public String action; 11 | public String user; 12 | public String clientip; 13 | public String createdtime; 14 | 15 | public String getRequestid() { 16 | return requestid; 17 | } 18 | 19 | public String getContextid() { 20 | return contextid; 21 | } 22 | 23 | public String getCallerid() { 24 | return callerid; 25 | } 26 | 27 | public String getType() { 28 | return type; 29 | } 30 | 31 | public String getComponent() { 32 | return component; 33 | } 34 | 35 | public String getAction() { 36 | return action; 37 | } 38 | 39 | public String getCreatedtime() { 40 | return createdtime; 41 | } 42 | 43 | public String getUser() { 44 | return user; 45 | } 46 | 47 | public String getClientip() { 48 | return clientip; 49 | } 50 | 51 | public void setRequestid(String requestid) { 52 | this.requestid = requestid; 53 | } 54 | 55 | public void setContextid(String contextid) { 56 | this.contextid = contextid; 57 | } 58 | 59 | public void setCallerid(String callerid) { 60 | this.callerid = callerid; 61 | } 62 | 63 | public void setType(String type) { 64 | this.type = type; 65 | } 66 | 67 | 68 | public void setComponent(String component) { 69 | this.component = component; 70 | } 71 | 72 | public void setAction(String action) { 73 | this.action = action; 74 | } 75 | 76 | public void setCreatedtime(String createdtime) { 77 | this.createdtime = createdtime; 78 | } 79 | 80 | public void setUser(String user) { 81 | this.user = user; 82 | } 83 | 84 | public void setClientip(String clientip) { 85 | this.clientip = clientip; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/model/ClickLogResponse.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.model; 2 | import java.util.Map; 3 | 4 | public class ClickLogResponse { 5 | 6 | private int statusCode; 7 | 8 | private Map headers; 9 | 10 | private String body; 11 | 12 | public ClickLogResponse(int statusCode, Map headers, String body) { 13 | this.statusCode = statusCode; 14 | this.headers = headers; 15 | this.body = body; 16 | } 17 | 18 | public int getStatusCode() { 19 | return statusCode; 20 | } 21 | 22 | public Map getHeaders() { 23 | return headers; 24 | } 25 | 26 | public String getBody() { 27 | return body; 28 | } 29 | 30 | public void setStatusCode(int statusCode) { 31 | this.statusCode = statusCode; 32 | } 33 | 34 | public void setHeaders(Map headers) { 35 | this.headers = headers; 36 | } 37 | 38 | public void setBody(String body) { 39 | this.body = body; 40 | } 41 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/java/com/clicklogs/model/ResponseBuilder.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ResponseBuilder { 7 | private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; 8 | 9 | private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; 10 | 11 | private int statusCode; 12 | 13 | private Map headers = new HashMap<>(); 14 | 15 | private String body; 16 | 17 | public ResponseBuilder headers(Map headers) { 18 | this.headers = headers; 19 | return this; 20 | } 21 | 22 | public ResponseBuilder body(String body) { 23 | this.body = body; 24 | return this; 25 | } 26 | 27 | public ResponseBuilder ok() { 28 | this.statusCode = 200; 29 | return this; 30 | } 31 | 32 | public ResponseBuilder badRequest(String body) { 33 | this.body = buildErrorMsg(body); 34 | this.statusCode = 400; 35 | return this; 36 | } 37 | 38 | private String buildErrorMsg(String body) { 39 | return "{\"message\": \"" + body + "\"}"; 40 | } 41 | 42 | public ResponseBuilder originHeader(String domain) { 43 | headers.put(ACCESS_CONTROL_ALLOW_ORIGIN, domain); 44 | return this; 45 | } 46 | 47 | private void initDefaultHeaders() { 48 | headers.put(ACCESS_CONTROL_ALLOW_HEADERS, "Origin, Access-Control-Allow-Headers, X-Requested-With, Content-Type, Accept"); 49 | } 50 | 51 | public ClickLogResponse build() { 52 | this.initDefaultHeaders(); 53 | return new ClickLogResponse(statusCode, headers, body); 54 | } 55 | } -------------------------------------------------------------------------------- /source/clicklogger/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/clicklogger/src/test/java/com/clicklogs/ClickLoggerHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs; 2 | 3 | import com.amazonaws.services.kinesisfirehose.AmazonKinesisFirehose; 4 | import com.amazonaws.services.kinesisfirehose.model.PutRecordRequest; 5 | import com.amazonaws.services.kinesisfirehose.model.PutRecordResult; 6 | import com.amazonaws.services.kinesisfirehose.model.Record; 7 | import com.amazonaws.services.lambda.runtime.Context; 8 | import com.clicklogs.Handlers.ClickLoggerHandler; 9 | import com.clicklogs.model.ClickLogRequest; 10 | 11 | import org.junit.Assert; 12 | import java.nio.ByteBuffer; 13 | import com.google.gson.Gson; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.mockito.Mockito; 18 | import org.mockito.junit.MockitoJUnitRunner; 19 | 20 | import static org.mockito.ArgumentMatchers.any; 21 | import static org.mockito.Mockito.when; 22 | 23 | @RunWith(MockitoJUnitRunner.class) 24 | public class ClickLoggerHandlerTest { 25 | 26 | private static String deliveryStreamName = "delivery-stream"; 27 | Gson gson = new Gson(); 28 | ClickLogRequest clickLogRequest = new ClickLogRequest(); 29 | PutRecordRequest putRecordRequest = new PutRecordRequest(); 30 | Record record = new Record(); 31 | PutRecordResult putRecordResult = new PutRecordResult(); 32 | 33 | Context context = Mockito.mock(Context.class); 34 | 35 | protected AmazonKinesisFirehose amazonKinesisFirehoseClient = Mockito.mock(AmazonKinesisFirehose.class); 36 | ClickLoggerHandler clickLoggerHandler = Mockito.mock(ClickLoggerHandler.class); 37 | 38 | @Before 39 | public void setup() { 40 | clickLogRequest = new ClickLogRequest(); 41 | clickLogRequest.setAction("ACTION"); 42 | clickLogRequest.setCallerid("CALLERID"); 43 | clickLogRequest.setClientip("CLIENTIP"); 44 | clickLogRequest.setComponent("COMPONENT"); 45 | clickLogRequest.setContextid("CONTEXTID"); 46 | clickLogRequest.setCreatedtime("CREATEDTIME"); 47 | clickLogRequest.setRequestid("REQUESTID"); 48 | clickLogRequest.setType("TYPE"); 49 | clickLogRequest.setUser("USER"); 50 | 51 | when(clickLogRequest).thenReturn(this.clickLogRequest); 52 | 53 | amazonKinesisFirehoseClient = Mockito.mock(AmazonKinesisFirehose.class); 54 | when(amazonKinesisFirehoseClient).thenReturn(amazonKinesisFirehoseClient); 55 | 56 | putRecordResult = new PutRecordResult(); 57 | putRecordResult.setRecordId("SUCCESS_RECORD_ID"); 58 | when(any(PutRecordResult.class)).thenReturn(putRecordResult); 59 | } 60 | 61 | @Test 62 | void invokeTest() { 63 | clickLoggerHandler.handleRequest(clickLogRequest, context); 64 | 65 | putRecordRequest.setDeliveryStreamName(deliveryStreamName); 66 | 67 | Gson gson = new Gson(); 68 | String messageJson = gson.toJson(clickLogRequest); 69 | System.out.println("gson - " + messageJson); 70 | record = new Record().withData(ByteBuffer.wrap(messageJson.toString().getBytes())); 71 | putRecordRequest.setRecord(record); 72 | 73 | amazonKinesisFirehoseClient = Mockito.mock(AmazonKinesisFirehose.class); 74 | PutRecordResult result = amazonKinesisFirehoseClient.putRecord( putRecordRequest ); 75 | Assert.assertEquals(result.getRecordId(), "SUCCESS_RECORD_ID"); 76 | 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /source/clicklogger/src/test/java/com/clicklogs/ClickLoggerStreamHanderTest.java: -------------------------------------------------------------------------------- 1 | package com.clicklogs; 2 | 3 | import static org.mockito.Mockito.when; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import com.amazonaws.services.lambda.runtime.Context; 10 | import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; 11 | import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent; 12 | import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent.Record; 13 | //import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse.Record; 14 | import com.clicklogs.Handlers.ClickLoggerStreamHandler; 15 | import com.clicklogs.model.ClickLogRequest; 16 | import com.google.gson.Gson; 17 | 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.mockito.Mockito; 21 | 22 | import org.junit.Assert; 23 | 24 | public class ClickLoggerStreamHanderTest { 25 | Gson gson = new Gson(); 26 | KinesisFirehoseEvent event = new KinesisFirehoseEvent(); 27 | ClickLogRequest clickLogRequest = new ClickLogRequest(); 28 | 29 | Context context = Mockito.mock(Context.class); 30 | ClickLoggerStreamHandler clickLoggerSreamHandler = Mockito.mock(ClickLoggerStreamHandler.class); 31 | 32 | @Before 33 | public void setup() { 34 | List records = new ArrayList<>(); 35 | 36 | Record record = new Record(); 37 | clickLogRequest = new ClickLogRequest(); 38 | clickLogRequest.setAction("ACTION"); 39 | clickLogRequest.setCallerid("CALLERID"); 40 | clickLogRequest.setClientip("CLIENTIP"); 41 | clickLogRequest.setComponent("COMPONENT"); 42 | clickLogRequest.setContextid("CONTEXTID"); 43 | clickLogRequest.setCreatedtime("CREATEDTIME"); 44 | clickLogRequest.setRequestid("REQUESTID"); 45 | clickLogRequest.setType("TYPE"); 46 | clickLogRequest.setUser("USER"); 47 | 48 | when(clickLogRequest).thenReturn(this.clickLogRequest); 49 | record = new Record(); 50 | record.setData(ByteBuffer.wrap(clickLogRequest.toString().getBytes())); 51 | records.add(record); 52 | event.setRecords(records); 53 | when(event).thenReturn(this.event); 54 | 55 | 56 | } 57 | 58 | @Test 59 | void invokeTest() { 60 | KinesisAnalyticsInputPreprocessingResponse response = new KinesisAnalyticsInputPreprocessingResponse(); 61 | response = clickLoggerSreamHandler.handleRequest(event, context); 62 | Assert.assertNotNull(response); 63 | // Assert.assertEquals(response.getRecords()., 1); 64 | 65 | } 66 | } -------------------------------------------------------------------------------- /terraform/templates/apigateway.tf: -------------------------------------------------------------------------------- 1 | 2 | resource "aws_api_gateway_rest_api" "click_logger_api" { 3 | name = "${var.app_prefix}-api" 4 | description = "click logger api" 5 | 6 | } 7 | 8 | #create a resource with name 'resource' in the gateway api , many resources can be created like this 9 | resource "aws_api_gateway_resource" "resource" { 10 | path_part = "clicklogger" 11 | parent_id = "${aws_api_gateway_rest_api.click_logger_api.root_resource_id}" 12 | rest_api_id = "${aws_api_gateway_rest_api.click_logger_api.id}" 13 | depends_on = ["aws_api_gateway_rest_api.click_logger_api"] 14 | } 15 | 16 | resource "aws_api_gateway_account" "click_logger_api_gateway_account" { 17 | cloudwatch_role_arn = "${aws_iam_role.click_logger_api_gateway_cloudwatch_role.arn}" 18 | } 19 | 20 | resource "aws_cloudwatch_log_group" "clicklogger-api-log-group" { 21 | name = "/aws/apigateway/${var.app_prefix}-API-Gateway-Execution-Logs/${var.stage_name}" 22 | retention_in_days = 7 23 | } 24 | 25 | resource "aws_api_gateway_method_settings" "general_settings" { 26 | rest_api_id = "${aws_api_gateway_rest_api.click_logger_api.id}" 27 | stage_name = "${aws_api_gateway_deployment.clicklogger_deployment.stage_name}" 28 | method_path = "*/*" 29 | settings { 30 | # Enable CloudWatch logging and metrics 31 | metrics_enabled = true 32 | data_trace_enabled = true 33 | logging_level = "INFO" 34 | # Limit the rate of calls to prevent abuse and unwanted charges 35 | throttling_rate_limit = 100 36 | throttling_burst_limit = 50 37 | } 38 | } 39 | 40 | resource "aws_api_gateway_authorizer" "clicklogger-authorizer" { 41 | name = "clicklogger-authorizer" 42 | rest_api_id = "${aws_api_gateway_rest_api.click_logger_api.id}" 43 | authorizer_uri = "${aws_lambda_function.lambda_clicklogger_authorizer.invoke_arn}" 44 | authorizer_credentials = "${aws_iam_role.click_logger_invocation_role.arn}" 45 | identity_source = "method.request.header.Authorization" 46 | type = "TOKEN" 47 | } 48 | 49 | 50 | resource "aws_api_gateway_request_validator" "clicklogger_validator" { 51 | name = "${var.app_prefix}-validator" 52 | rest_api_id = "${aws_api_gateway_rest_api.click_logger_api.id}" 53 | validate_request_body = true 54 | validate_request_parameters = true 55 | } 56 | 57 | resource "aws_api_gateway_model" "clicklogger_model" { 58 | rest_api_id = "${aws_api_gateway_rest_api.click_logger_api.id}" 59 | name = "${var.app_prefix}model" 60 | description = "${var.app_prefix}-JSON schema" 61 | content_type = "application/json" 62 | 63 | schema = <