diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 96% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index a8562f7..83fa1f1 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,4 +1,4 @@ -## Contributing: +## Contributing We welcome your involvement, be it fixing bugs or implementing new features that you find relevant to this library. To contribute, you may follow the steps below: diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 86% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md index f0647db..e413ea0 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,8 +12,10 @@ Please delete options that are not relevant. - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update +- [ ] Code enhancement and update (non-breaking change) +- [ ] Security patch -# How Has This Been Tested? +## How Has This Been Tested? Please describe or list the test cases that you ran to verify your changes, and provide instructions so we can reproduce. diff --git a/.travis.yml b/.travis.yml index 6964c86..d460873 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ language: java jdk: - oraclejdk8 +dist: trusty + env: - GRADLE_OPTS="-Dfile.encoding=utf-8" diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b82a3..2af2605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,91 @@ # Change Log -## Added -### V1.0-SNAPSHOT -+ Initial release with HMAC256 and RSA256 signing utility -### V1.0.1-SNAPSHOT -+ Enhancement for Issue #1 - ApiList sorting is not based on key first then value +### V2.1.3 + +- Fixed vulnerability CVE-2021-45105 +- Update to log4j version 2.17.0 + + +### V2.1.2 + +- Fixed vulnerability CVE-2021-45046 +- Update to log4j version 2.16.0 + + +### V2.1.1 + +- Fixed vulnerability CVE-2021-44228 +- Update to log4j version 2.15.0 + +### V2.1 + +- Release for APEX 2 + +### V2.0.0-SNAPSHOT + +- Library to use authParam +- Usage for APEX 2 + +### V1.3.5-SNAPSHOT + +- Fixed vulnerability CVE-2020-25649, CVE-2020-15522, CVE-2020-9488 and CVE-2019-17571 +- Update dependency library for bouncycastle to version 1.69 +- Update dependency library for jackson-databind to version 2.10.5.1 +- Remove dependency library for slf4j, updated to log4j version 2.14.1 for logging + +### V1.3.4-SNAPSHOT + +- Fixed vulnerability CVE-2019-14379 and CVE-2019-14439 + +### V1.3.3-SNAPSHOT + +- Set TravisCI build dist to Trusty +- Fixed vulnerability CVE-2019-12814 + +### V1.3.2-SNAPSHOT + +- Update ApiSigning class to support non-standard http port + +### V1.3.1-SNAPSHOT + +- Update dependency library for jackson-databind to version 2.9.8 +- Update mvn command to pull centralised unit-test cases from github repository + +### V1.3.0-SNAPSHOT + +- Update test-suites framework to cover more use-case scenarios +- Add Utility to read from PKI Key with PEM format +- Update README documentation + +### V1.2.0-SNAPSHOT + +- Bug fixes for null value checking +- Update basestring method to suppport use-case where value of queryparam or form value is empty +- Update nonce method to generate base64 encoded string value of 32 bytes characters + +### V1.1.2-SNAPSHOT + +- Minor refactoring +- Update documentation + +### V1.1.1-SNAPSHOT + +- Minor refactoring +- Update documentation +- Update templates + ### V1.1.0-SNAPSHOT -+ Minor refactoring -+ Update interface name so as be intuitive -+ Update corresponding test cases -+ Include Issue/PR templates -+ Include Contribution template \ No newline at end of file + +- Minor refactoring +- Update interface name so as be intuitive +- Update corresponding test cases +- Include Issue/PR templates +- Include Contribution template + +### V1.0.1-SNAPSHOT + +- Enhancement for Issue #1 - ApiList sorting is not based on key first then value + +### V1.0-SNAPSHOT + +- Initial release with HMAC256 and RSA256 signing utility diff --git a/LICENSE b/LICENSE index 9a74d72..1fa7d98 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2017 GovTech, Government Digital Services, PDD-AI +Copyright (C) 2017-2021 GovTech, Government Digital Services - ENP Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1c260ef..07fefb2 100644 --- a/README.md +++ b/README.md @@ -5,29 +5,48 @@ A java helper utilities that form HTTP security header for authentication and verification +## Table of Contents +- [Getting Started](#getting-started) + * [Using Maven](#maven-guide) + + [Maven Build Option](#maven-build-option) + + [Maven Test](#maven-test) + * [Using Gradle](#gradle-guide) + + [Preparation](#preparation) + + [Gradle Build Option](#gradle-build-option) + + [Gradle Test](#gradle-test) + * [Development](#development) + + [How to create QueryData and FormData](#how-to-create-querydata-and-formdata) + + [Generate L1 Authorization Header](#generate-l1-authorization-header) + + [Generate L2 Authorization Header](#generate-l2-authorization-header) + + [Generate L21 Authorization Header](#generate-l21-authorization-header) + + [Generate L12 Authorization Header](#generate-l12-authorization-header) +- [Release](#release) +- [Contributing](#contributing) +- [License](#license) +- [References](#references) + ## Getting Started Include this helper class in your java project to perform API Security operations This project use Maven or Gradle as its build and management tools + ### Maven Guide + Download and Install Maven (3.5.0 or above) + Java (1.8) -#### Build +#### Maven Build Option **Option 1:** Compile and package into JAR ```bash - mvn package - ``` The compiled _jar_ file will be located in the **target** folder -+ java-apex-api-security--SNAPSHOT.jar -+ java-apex-api-security--SNAPSHOT-jar-with-dependencies.jar (this includes log4j libraries) ++ java-apex-api-security-.jar ++ java-apex-api-security--jar-with-dependencies.jar (this includes log4j libraries) Import this jar file into your java classpath to use the utility class @@ -45,23 +64,38 @@ mvn install com.api.util ApiSecurity - 1.0-SNAPSHOT + 2.1.3 - ``` **Note:** -* This project is leveraging on _slf4j-log4j12_ framework for the logging. If you are using logging implementation other than log4j, you can change to other type of implementation such as nop,simple,jdk14,logback. You could replace the following xml in pom.xml. -* If your are using Log4j _Version2_, please refer to [Log4j2-SLF4J Binding](https://logging.apache.org/log4j/2.x/log4j-slf4j-impl/index.html) +* This project is leveraging on Log4j _Version2_ framework for the logging. If you are using logging implementation other than Log4j _Version2_ , you can change to other type of implementation such as nop,simple,jdk14,logback. You could replace the following xml in pom.xml. ```xml - org.slf4j - slf4j-log4j12 - 1.7.25 + org.apache.logging.log4j + log4j-api + 2.17.0 + + + org.apache.logging.log4j + log4j-core + 2.17.0 - +``` + +#### Maven Test +Pull centralised Unit Test-cases from the following Github url: with Mavem command (in project root folder) + +```bash +mvn scm:checkout -D checkoutDirectory=src/main/resources/test-suites +``` + +To execute unit-test with Maven command (in project root folder) + +```bash +mvn test ``` ### Gradle Guide @@ -76,14 +110,14 @@ As some of the test cases contains UTF-8 characters, you have to set following p export GRADLE_OPTS="-Dfile.encoding=utf-8" ``` -#### Build +#### Gradle Build Option **Option 1:** Compile and package into JAR ```bash gradle clean build ``` -#### Test +#### Gradle Test To test with Jacoco and publish a html report ```bash @@ -91,7 +125,7 @@ gradle test jacocoTestReport ``` The compiled _jar_ file will be located in the **build/libs** folder -+ java-apex-api-security-1.0-SNAPSHOT.jar ++ java-apex-api-security-2.1.3.jar Import this jar into your java classpath to use the utility class @@ -106,158 +140,372 @@ repositories { mavenLocal() } dependencies { - compile group: 'com.api.util', name: 'ApiSecurity', version: '1.0-SNAPSHOT' + compile group: 'com.api.util', name: 'ApiSecurity', version: '2.1.3' } ``` -#### Development +### Development + +#### Preparing HTTP Signature Token + +Append this signature token into the Authorization header of the HTTP +request. + +#### Example Generated Token - + +``` +Apex_l1_eg realm="https://XYZ.api.gov.sg/abc/def", apex_l1_eg_app_id="APP_ID", apex_l1_eg_nonce="SOME_RANDOM_STRING", apex_l1_eg_signature_method="HMACSHA256", apex_l1_eg_timestamp="SOME_TIMESTAMP", apex_l1_eg_version="1.0", apex_l1_eg_signature="SOME_SIGNATURE" +``` + +#### Example Authorization Header - + +``` +Authorization: Apex_l1_eg realm="https://XYZ.api.gov.sg/abc/def", apex_l1_eg_app_id="APP_ID", apex_l1_eg_nonce="SOME_RANDOM_STRING", apex_l1_eg_signature_method="HMACSHA256", apex_l1_eg_timestamp="SOME_TIMESTAMP", apex_l1_eg_version="1.0", apex_l1_eg_signature="SOME_SIGNATURE" +``` + +### Parameters -##### Preparing Signature BaseString : +#### httpMethod -Method: -* getBaseString +The HTTP method, i.e. `GET`, `POST`, etc. -Params: -* authPrefix - Authorization Header scheme prefix , i.e 'prefix_appId' -* signatureMethod -* appId - App ID created in Gateway -* urlPath -* httpMethod -* formList - only needed for Content-Type: application/x-www-form-urlencoded -* nonce - set to null for random generated number -* timestamp - set to null for current timestamp +#### url +The full API endpoint (with query parameters if any). + +#### appId +The APEX App ID. + +#### secret +The APEX App secret. Not required if you want to use L2 +authentication with SHA256WITHRSA. + +#### formData +Data which should be passed in the request (for `POST` requests +usually). For `GET` requests, this value is not necessary. + +#### password +The password of the keystore. Not required for L1. + +#### alias +The alias of the keystore. Not required for L1. + +#### fileName +The p12 file path. Not required for L1. + +#### nonce +The random generated string which to be used to generate the token. If +not set, a new random string will be generated. + +#### timestamp +Timestamp which should be used to generate the token. Not required if +you want to use the current timestamp. + + + +### Example GET Request ```java -String url = "https://<>/api/v1/?param1=first&ab-param2=123"; +try { + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create("https://<>/api/v1"); + authParam.httpMethod = "GET"; + authParam.appName = "<>"; + String certFileName = "certificates/ssc.alpha.example.com.p12"; + String password = "<>"; + String alias = "alpha"; + authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias); + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + // Add this signature value to the authorization header when sending the request. + // authorizationToken.getToken(); + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); +} +``` -ApiList formList = new ApiList(); -formList.add("param1", "data1"); -String baseString; +### Example POST Request +```java try { -baseString = ApiSigning.getBaseString( - "<>", - "HMACSHA256", - "<>", - url, - "post", - formList, - "6584351262900708156", - "1502184161702" -); - -System.out.println(baseString); - -} catch (ApiUtilException e) { - e.printStackTrace(); + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create("https://<>/api/v1"); + authParam.httpMethod = "POST"; + authParam.appName = "<>"; + String certFileName = "certificates/ssc.alpha.example.com.p12"; + String password = "<>"; + String alias = "alpha"; + authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias); + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + // Add this signature value to the authorization header when sending the request. + // authorizationToken.getToken(); + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); } - ``` -##### Preparing HMACSHA256 L1 Security Signature : +### How to create QueryData and FormData -Method: -* getHMACSignature +The ApiSecurity Library provide the utility class ApiList and FormList to construct request Query String and Form Data. -Params: -* baseString -* secret + + +**Generate QueryString** ```java -String baseString = "GET&https://<>/api/v1/&ap=裕廊坊 心邻坊&<>_app_id=<&<>_nonce=2851111144329605674&<>_signature_method=HMACSHA256&<>_timestamp=1502163903712&<>_version=1.0"; -String secret = "<>"; -String L1Sig; - -try { - L1Sig = ApiSigning.getHMACSignature(baseString, secret); - System.out.println(L1Sig); +ApiList queryData = new ApiList(); +queryData.add("clientId", "1256-1231-4598"); +queryData.add("accountStatus", "active"); +queryData.add("txnDate", "2017-09-29"); + +String queryString = queryData.toString(true); +String baseUrl = String.format("https://example.com/resource?%s", queryString); +// https://example.com/resource?accountStatus=active&clientId=1256-1231-4598&txnDate=2017-09-29 +``` -} catch (ApiUtilException e) { - e.printStackTrace(); -} +**Generate FormData** + +```java +FormList formData = new FormList(); +formData.add("phoneNo", "+1 1234 4567 890"); +formData.add("street", "Hellowood Street"); +formData.add("state", "AP"); + +String formDataString = formData.toFormData(); +// phoneNo=%2B1+1234+4567+890&street=Hellowood+Street&state=AP ``` +```java +String signingUrl = "https://<>/api/v1/?param1=first¶m2=123"; -##### Preparing RSA256 L2 Security Signature : +try { + FormList formData = new FormList(); + formData.add("param1", "data1"); + + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(https://<>/api/v1"); + authParam.httpMethod = "POST"; + authParam.appName = "<>"; + authParam.nonce = "<>"; + authParam.timestamp = "<>"; + authParam.formData = formData; + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + assertEquals(expectedBaseString, authorizationToken.getBaseString()); +} +catch (ApiUtilException e) +{ + e.printStackTrace(); +} +``` + +**NOTE** -Method: -* getRSASignature +For **formData** parameter used for Signature generation, the key value parameters **do not** need to be URL encoded, +When your client program is making the actual HTTP POST call, the key value parameters **has** to be URL encoded (refer to **formPostData**) -Params: -* baseString -* privateKey + +### Generate L1 Authorization Header ```java -String baseString = "GET&https://</api/v1/&ap=裕廊坊 心邻坊&<>_app_id=<>&<>_nonce=7231415196459608363&<>_signature_method=SHA256withRSA&<>_timestamp=1502164219425&<>_version=1.0&oq=c# nunit mac&q=c# nunit mac"; -String alias = "alpha"; -String password = "<>"; -String keyStoreFileName = "certificates/ssc.alpha.example.com.p12"; -String publicCertFileName = "certificates/ssc.alpha.example.com.cer"; + +FormList formData = new FormList(); +formData.add("phoneNo", "+1 1234 4567 890"); +formData.add("street", "Hellowood Street"); +formData.add("state", "AP"); try { - - PrivateKey privateKey = ApiSigning.getPrivateKeyFromKeyStore(keyStoreFileName, password, alias); - - String signature = ApiSigning.getRSASignature(baseString, privateKey); - - System.out.println(expectedSignature); - System.out.println(signature); - - PublicKey publicKey = ApiSigning.getPublicKeyFromX509Certificate(publicCertFileName); - -} catch (ApexUtilLibException e) { - e.printStackTrace(); + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create("https://<>/api/v1"); + authParam.httpMethod = "POST"; + authParam.appName = "<>"; + authParam.appSecret = "<>"; + authParam.formData = formData; + + // get the authorization token for L1 + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + + System.out.println("BaseString :: " + authorizationToken.getBaseString()); + System.out.println("Authorization Token :: " + authorizationToken.getToken()); + + // make api call with authorizationToken.getToken() + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); } +``` + +### Generate L2 Authorization Header + +```java +FormList formData = new FormList(); +formData.add("phoneNo", "+1 1234 4567 890"); +formData.add("street", "Hellowood Street"); +formData.add("state", "AP"); + +ApiList queryData = new ApiList(); +queryData.add("clientId", "1256-1231-4598"); +queryData.add("accountStatus", "active"); +queryData.add("txnDate", "2017-09-29"); +String queryString = queryData.toString(true); +String baseUrl = String.format("https://<>/api/v1?%s", queryString); + +try { + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(baseUrl); + authParam.httpMethod = "POST"; + authParam.appName = "<>"; + String certFileName = "certificates/ssc.alpha.example.com.p12"; + String password = "<>"; + String alias = "alpha"; + authParam.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias); + authParam.formData = formData; + + // get the authorization token for L2 + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + + System.out.println("BaseString :: " + authorizationToken.getBaseString()); + System.out.println("Authorization Token :: " + authorizationToken.getToken()); + // make api call with authorizationToken.getToken() + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); +} ``` -##### Preparing HTTP Signature Token : +### Generate L21 Authorization Header +(for cross zone api from internet to intranet) + +```java +FormList formData = new FormList(); +formData.add("phoneNo", "+1 1234 4567 890"); +formData.add("street", "Hellowood Street"); +formData.add("state", "AP"); + +ApiList queryData = new ApiList(); +queryData.add("clientId", "1256-1231-4598"); +queryData.add("accountStatus", "active"); +queryData.add("txnDate", "2017-09-29"); +String queryString = queryData.toString(true); + + +try { + AuthParam authParam_WWW = new AuthParam(); + String baseUrl_WWW = String.format("https://<>/api/v1?%s", queryString); + authParam_WWW.url = URI.create(baseUrl_WWW); + authParam_WWW.httpMethod = "POST"; + authParam_WWW.appName = "<>"; + String certFileName = "certificates/ssc.alpha.example.com.p12"; + String password = "<>"; + String alias = "alpha"; + authParam_WWW.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias); + authParam_WWW.formData = formData; + + AuthParam authParam_WOG = new AuthParam(); + authParam_WOG.httpMethod = "POST"; + authParam_WOG.appName = "<>"; + authParam_WOG.appSecret = "<>"; + String baseUrl_WOG = String.format("https://<>/api/v1?%s", queryString); + authParam_WOG.url = URI.create(baseUrl1); + authParam_WWW.nextHop = authParam_WOG; + -Append this signature token into the Authorization header of the HTTP request + // get the authorization token for L21 + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam_WWW); + + System.out.println("BaseString :: " + authorizationToken.getBaseString()); + System.out.println("Authorization Token :: " + authorizationToken.getToken()); -Params: -* realm -* authPrefix - Authorization Header scheme prefix , i.e 'prefix_appId' -* httpMethod -* urlPath -* appId - App ID created in Gateway -* secret - set to null for REST L2 SHA256WITHRSA -* formList -* password -* alias -* fileName -* nonce - set to null for random generated number -* timestamp - set to null for current timestamp + // make api call with authorizationToken.getToken() + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); +} +``` +### Generate L12 Authorization Header +(for cross zone api from intranet to internet) ```java -String url = "https://<>/api/v1/?ap=裕廊坊%20心邻坊"; -String certFileName = "certificates/ssc.alpha.example.com.p12"; -String password = "<>"; -String alias = "alpha"; -String appId = "<>"; -String secret = null; -ApiList formList = null; -String nonce = null; -String timestamp = null; +FormList formData = new FormList(); +formData.add("phoneNo", "+1 1234 4567 890"); +formData.add("street", "Hellowood Street"); +formData.add("state", "AP"); + +ApiList queryData = new ApiList(); +queryData.add("clientId", "1256-1231-4598"); +queryData.add("accountStatus", "active"); +queryData.add("txnDate", "2017-09-29"); +String queryString = queryData.toString(true); + try { - String signature = ApiSigning.getSignatureToken("http://api.test.io/l2", "<>", "get", url, appId, null, null, password, alias, certFileName, nonce, timestamp); -} catch (ApiUtilException e) { - e.printStackTrace(); + AuthParam authParam_WOG = new AuthParam(); + authParam_WOG.httpMethod = "POST"; + authParam_WOG.appName = "<>"; + authParam_WOG.appSecret = "<>"; + String baseUrl_WOG = String.format("https://<>/api/v1?%s", queryString); + authParam_WOG.url = URI.create(baseUrl1); + + AuthParam authParam_WWW = new AuthParam(); + String baseUrl_WWW = String.format("https://<>/api/v1?%s", queryString); + authParam_WWW.url = URI.create(baseUrl_WWW); + authParam_WWW.httpMethod = "POST"; + authParam_WWW.appName = "<>"; + String certFileName = "certificates/ssc.alpha.example.com.p12"; + String password = "<>"; + String alias = "alpha"; + authParam_WWW.privateKey = ApiSigning.getPrivateKey(certFileName, password, alias); + authParam_WWW.formData = formData; + authParam_WOG.nextHop = authParam_WWW; + + + // get the authorization token for L12 + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam_WOG); + + System.out.println("BaseString :: " + authorizationToken.getBaseString()); + System.out.println("Authorization Token :: " + authorizationToken.getToken()); + + // make api call with authorizationToken.getToken() + +} +catch (ApiUtilException e) +{ + e.printStackTrace(); } ``` + +## Contributing +For more information about contributing PRs and issues, see [CONTRIBUTING.md](.github/CONTRIBUTING.md). -### Contributing -+ For more information about contributing PRs and issues, see [CONTRIBUTING.md](CONTRIBUTING.md). +## Release +See [CHANGELOG.md](CHANGELOG.md). -### Release -+ See [CHANGELOG.md](CHANGELOG.md). +## License +[MIT LICENSE](LICENSE). -### Reference: +## References + [UTF-8 in Gradle](https://stackoverflow.com/questions/21267234/show-utf-8-text-properly-in-gradle) -+ [SLF4J FAQ](https://www.slf4j.org/faq.html) -+ [Akana API Consumer Security](http://docs.akana.com/ag/cm_policies/using_api_consumer_app_sec_policy.htm) -+ [RSA and HMAC Request Signing Standard](http://tools.ietf.org/html/draft-cavage-http-signatures-05) ++ [LOG4J2 FAQ](https://logging.apache.org/log4j/2.x/faq.html) ++ [Akana API Consumer Security](http://docs.akana.com/cm/learnmore/app_security.htm) ++ [RSA and HMAC Request Signing Standard](https://tools.ietf.org/id/draft-cavage-http-signatures-08.html) diff --git a/build.gradle b/build.gradle index 5b7d72f..f83da13 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,9 @@ plugins { id 'com.github.kt3k.coveralls' version '2.6.3' } -version '1.1.0-SNAPSHOT' + + +version '2.1.3' tasks.withType(JavaCompile) { options.encoding = "UTF-8" @@ -15,10 +17,24 @@ repositories { } dependencies { - compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.1' - compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' - testCompile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.25' - testCompile group: 'junit', name: 'junit', version: '4.12' + //compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.1' + + //gradle 4.0 + compile group: 'commons-lang', name: 'commons-lang', version: '2.4' + compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.17.0' + compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.0' + compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.10.5.1' + compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' + compile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.69' + + testCompile group: 'junit', name: 'junit', version: '4.13.1' + + //gradle 6.9 + //implementation group: 'commons-lang', name: 'commons-lang', version: '2.4' + //implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.10.5.1' + //implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' + //implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.69' + //testImplementation group: 'junit', name: 'junit', version: '4.13.1' } jar { diff --git a/pom.xml b/pom.xml index 1888c7e..efb6135 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.api.util ApiSecurity - 1.1.0-SNAPSHOT + 2.1.3 @@ -29,13 +29,39 @@ + + org.jsonschema2pojo + jsonschema2pojo-maven-plugin + 0.5.1 + + src/main/resources/schema + com.api.util.ApiSecurity + + + + + generate + + + + + + org.apache.maven.plugins + maven-scm-plugin + 1.8.1 + + developerConnection + master + branch + + junit junit - 4.12 + 4.13.1 test @@ -52,12 +78,44 @@ test - org.slf4j - slf4j-log4j12 - 1.7.25 - + org.apache.logging.log4j + log4j-api + 2.17.0 + + + org.apache.logging.log4j + log4j-core + 2.17.0 + + + commons-lang + commons-lang + 2.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.10.5.1 + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + org.bouncycastle + bcpkix-jdk15on + 1.69 + UTF-8 + 1.8 + 1.8 + + scm:git:https://github.com/GovTechSG/test-suites-apex-api-security.git + scm:git:https://github.com/GovTechSG/test-suites-apex-api-security.git + https://github.com/GovTechSG + \ No newline at end of file diff --git a/src/main/java/com/api/util/ApiSecurity/ApiList.java b/src/main/java/com/api/util/ApiSecurity/ApiList.java index ff4b641..ddd6023 100644 --- a/src/main/java/com/api/util/ApiSecurity/ApiList.java +++ b/src/main/java/com/api/util/ApiSecurity/ApiList.java @@ -1,7 +1,6 @@ package com.api.util.ApiSecurity; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map.Entry; import java.util.stream.Collectors; @@ -13,7 +12,7 @@ public class ApiList extends ArrayList>{ private static final long serialVersionUID = 1L; - + public void add(String key, String value) { Entry item = new SimpleEntry(key, value); @@ -21,18 +20,16 @@ public void add(String key, String value) this.add(item); } - public String toString() { + public String toString(Boolean isBaseString) { String delimiter = "&"; Boolean sort = true; Boolean quote = false; - - return this.toString(delimiter, sort, quote); + return this.toString(delimiter, sort, quote, isBaseString); } - - public String toString(String delimiter, Boolean sort, Boolean quote) + + public String toString(String delimiter, Boolean sort, Boolean quote, Boolean isBaseString) { List list = new ArrayList(); - final String format = (quote ? "%s=\"%s\"" : "%s=%s"); /* Sort key first then value*/ @@ -43,7 +40,7 @@ public String toString(String delimiter, Boolean sort, Boolean quote) return l1.getKey().equals(l2.getKey()) ? l1.getValue().compareTo(l2.getValue()) : l1.getKey().compareTo(l2.getKey()); }) - .map(e -> String.format(format, e.getKey(), e.getValue())) + .map(e -> (null== e.getValue() || (null!= e.getValue() && e.getValue().isEmpty()) && isBaseString) ? e.getKey() : String.format(format, e.getKey(), e.getValue()) ) .collect(Collectors.toList()); } else{ list = this.stream().map(e -> String.format(format, e.getKey(), e.getValue())) @@ -52,4 +49,19 @@ public String toString(String delimiter, Boolean sort, Boolean quote) return String.join(delimiter, list); } + + + public FormList toFormList() + { + FormList formList = new FormList(); + + for (Entry item : this) + { + formList.add(item.getKey(), item.getValue()); + } + + return formList; + } + + } diff --git a/src/main/java/com/api/util/ApiSecurity/ApiSigning.java b/src/main/java/com/api/util/ApiSecurity/ApiSigning.java index 466b9bd..aee5631 100644 --- a/src/main/java/com/api/util/ApiSecurity/ApiSigning.java +++ b/src/main/java/com/api/util/ApiSecurity/ApiSigning.java @@ -1,14 +1,32 @@ package com.api.util.ApiSecurity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.commons.lang.StringUtils; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.bouncycastle.operator.InputDecryptorProvider; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; + + +import org.bouncycastle.operator.InputDecryptorProvider; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; + import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; + +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -18,7 +36,10 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Base64; +import java.util.LinkedList; +import java.util.List; /** @@ -26,8 +47,7 @@ */ public class ApiSigning { - private static final Logger log = LoggerFactory.getLogger(ApiSigning.class); - private final static String USER_AGENT = "Mozilla/5.0"; + private static final Logger log = LogManager.getLogger(ApiSigning.class); /** * Create HMACRSA256 Signature (L1) with a given basestring @@ -39,7 +59,6 @@ public class ApiSigning { */ public static String getHMACSignature(String baseString, String secret) throws ApiUtilException { log.debug("Enter :: getHMACSignature :: baseString : {} , secret: {} ", baseString, secret); - //Initialization String base64Token = null; SecretKeySpec signingKey = null; @@ -233,6 +252,55 @@ public static boolean verifyRSASignature(String baseString, String signature, Pu return verified; } + + + /** + * Get Private key + * + * @param keystoreFileName Keystore file Path + * @param passphrase Keystore passsword + * @param alias Keystore's alias + * @return private key + * @throws ApiUtilException + */ + public static PrivateKey getPrivateKey(String keystoreFileName, String password, String alias) throws ApiUtilException + { + PrivateKey privateKey = null; + if(null!=keystoreFileName && (keystoreFileName.contains(".key")||keystoreFileName.contains(".pem"))){ + privateKey = getPrivateKeyPEM(keystoreFileName, password); + + }else{ + //For JKS file + privateKey = getPrivateKeyFromKeyStore(keystoreFileName, password, alias); + } + return privateKey; + + } + + /** + * Get Private key + * + * @param keystoreFileName Keystore file Path + * @param passphrase Keystore passsword + * @return private key + * @throws ApiUtilException + */ + public static PrivateKey getPrivateKey(String keystoreFileName, String password) throws ApiUtilException { + return getPrivateKey(keystoreFileName, password, ""); + } + + /** + * Get Private key + * + * @param keystoreFileName Keystore file Path + * @return private key + * @throws ApiUtilException + */ + public static PrivateKey getPrivateKey(String keystoreFileName) throws ApiUtilException { + return getPrivateKey(keystoreFileName, ""); + } + + /** * Get Private key from Keystore @@ -245,7 +313,6 @@ public static boolean verifyRSASignature(String baseString, String signature, Pu */ public static PrivateKey getPrivateKeyFromKeyStore(String keystoreFileName, String password, String alias) throws ApiUtilException { log.debug("Enter :: getPrivateKeyFromKeyStore :: keystoreFileName : {} , password: {} , alias: {} ", keystoreFileName, password, alias); - //Initialization KeyStore ks = null; PrivateKey privateKey = null; @@ -304,7 +371,7 @@ public static PrivateKey getPrivateKeyFromKeyStore(String keystoreFileName, Stri return privateKey; } - + /** * Get Public Key from Certificate * @@ -356,10 +423,63 @@ public static PublicKey getPublicKeyFromX509Certificate(String publicCertificate log.debug("Exit :: getPublicKeyFromX509Certificate"); return pk; } - + + /** + * Get Public Key from PEM format file + * + * @param publicCertificateFileName PEM file path + * @return Public Key + * @throws IOException + * @throws GeneralSecurityException + * @throws ApiUtilException + */ + public static PublicKey getPublicKeyPEM(String publicCertificateFileName) throws IOException, GeneralSecurityException, ApiUtilException { + log.debug("Enter :: getPublicKeyPEM :: publicCertificateFileName : {} ", publicCertificateFileName); + PublicKey key = null; + PEMParser pemParser = null; + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + try{ + File publicCertificateFile = new File(publicCertificateFileName); // private key file in PEM format + pemParser = new PEMParser(new FileReader(publicCertificateFile)); + Object object = pemParser.readObject(); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + SubjectPublicKeyInfo keyInfo; + + KeyPair kp = null; + if(object instanceof SubjectPublicKeyInfo){ + keyInfo = (SubjectPublicKeyInfo) object; + key = converter.getPublicKey(keyInfo); + }else{ + kp = converter.getKeyPair(((PEMKeyPair) object)); + key = kp.getPublic(); + } + + }catch(Exception e){ + log.error(e.getMessage(),e); + throw new ApiUtilException(e.getMessage(), e); + }finally{ + if(null!=pemParser){ + pemParser.close(); + } + } + log.debug("Exit :: getPublicKeyPEM"); + + return key; + } + + /** * Formulate Signature BaseString * + * @deprecated use {@link getBaseString(String authPrefix + , SignatureMethod signatureMethod + , String appId + , URI urlPath + , String httpMethod + , FormList formList + , String nonce + , String timestamp + , String version)} instead. * @param authPrefix Authorization Header scheme prefix , i.e 'prefix_appId' * @param signatureMethod Signature signing method * @param appId App ID @@ -372,6 +492,7 @@ public static PublicKey getPublicKeyFromX509Certificate(String publicCertificate * @return Base String for signing * @throws ApiUtilException */ + @Deprecated public static String getBaseString(String authPrefix , String signatureMethod , String appId @@ -402,25 +523,35 @@ public static String getBaseString(String authPrefix if (!siteUri.getScheme().equals("http") && !siteUri.getScheme().equals("https")) { throw new ApiUtilException("Support http and https protocol only."); } - + + String url = null; // make sure that the port no and querystring are remove from url - String url = String.format("%s://%s%s", siteUri.getScheme(), siteUri.getHost(), siteUri.getPath()); + if(siteUri.getPort()==-1 || siteUri.getPort()==80 || siteUri.getPort()==443){ + url = String.format("%s://%s%s", siteUri.getScheme(), siteUri.getHost(), siteUri.getPath()); + }else{ + url = String.format("%s://%s:%s%s", siteUri.getScheme(), siteUri.getHost(), siteUri.getPort(), siteUri.getPath()); + } + log.debug("url:: {}", url); // helper calss that handle parameters and form fields ApiList paramList = new ApiList(); // process QueryString from url by transfering it to paramList - if (siteUri.getQuery().length() > 1) { + if (null != siteUri.getQuery()) { String queryString = siteUri.getRawQuery(); log.debug("queryString:: {}", queryString); String[] paramArr = queryString.split("&"); for (String item : paramArr) { - log.debug("item:: {}", item); + log.debug("queryItem:: {}", item); String[] itemArr = item.split("="); try { - paramList.add(itemArr[0], java.net.URLDecoder.decode(itemArr[1], StandardCharsets.UTF_8.toString())); + if(itemArr.length == 1) { + paramList.add(itemArr[0], ""); + }else { + paramList.add(itemArr[0], java.net.URLDecoder.decode(itemArr[1], StandardCharsets.UTF_8.toString())); + } } catch (UnsupportedEncodingException e) { throw e; } @@ -432,19 +563,20 @@ public static String getBaseString(String authPrefix if (formList != null && formList.size() > 0) { paramList.addAll(formList); } - paramList.add(authPrefix + "_timestamp", timestamp); paramList.add(authPrefix + "_nonce", nonce); paramList.add(authPrefix + "_app_id", appId); paramList.add(authPrefix + "_signature_method", signatureMethod); paramList.add(authPrefix + "_version", "1.0"); - baseString = httpMethod.toUpperCase() + "&" + url + "&" + paramList.toString(); + baseString = httpMethod.toUpperCase() + "&" + url + "&" + paramList.toString(true); } catch (ApiUtilException ae) { - log.error("Error :: getBaseString :: " + ae.getMessage()); + ae.printStackTrace(); + log.error("Error :: getBaseString :: " + ae.getMessage() ,ae); throw ae; } catch (Exception e) { + e.printStackTrace(); log.error("Error :: getBaseString :: " + e.getMessage()); throw new ApiUtilException("Error while getting Base String", e); } @@ -453,7 +585,138 @@ public static String getBaseString(String authPrefix return baseString; } + + + /** + * Formulate Signature BaseString + * @param authPrefix Authorization Header scheme prefix , i.e 'prefix_appId' + * @param signatureMethod Signature signing method + * @param appId App ID + * @param urlPath API Service URL + * @param httpMethod HTTP Operation + * @param formList form data + * @param nonce Random Nonce + * @param timestamp Timestamp + * @param version api version + * @return Base String for signing + * @throws ApiUtilException + */ + public static String getBaseString(String authPrefix + , SignatureMethod signatureMethod + , String appId + , URI urlPath + , String httpMethod + , FormList formList + , String nonce + , String timestamp + , String version) throws ApiUtilException { + log.debug("Enter :: getBaseString :: authPrefix : {} , signatureMethod : {} , appId : {} , " + + "urlPath : {} , httpMethod : {} , nonce : {} , timestamp : {} , version : {}", + authPrefix, signatureMethod, appId, urlPath, httpMethod, nonce, timestamp, version); + + String baseString = null; + + + try { + authPrefix = authPrefix.toLowerCase(); + + log.debug("raw url:: {}", urlPath.toString()); + log.debug("siteUri.getScheme():: {}", urlPath.getScheme()); + + if (!urlPath.getScheme().equals("http") && !urlPath.getScheme().equals("https")) { + throw new ApiUtilException("Support http and https protocol only."); + } + + String url = null; + // make sure that the port no and querystring are remove from url + if(urlPath.getPort()==-1 || urlPath.getPort()==80 || urlPath.getPort()==443){ + url = String.format("%s://%s%s", urlPath.getScheme(), urlPath.getHost(), urlPath.getPath()); + }else{ + url = String.format("%s://%s:%s%s", urlPath.getScheme(), urlPath.getHost(), urlPath.getPort(), urlPath.getPath()); + } + + log.debug("url:: {}", url); + + // helper class that handle parameters and form fields + ApiList paramList = new ApiList(); + + //for formData array require sorting as query String object exist + boolean sortFormData = false; + //for query key compare to check for query String object + String prevKey = ""; + + // process QueryString from url by transfering it to paramList + if (null != urlPath.getQuery()) { + String queryString = urlPath.getRawQuery(); + log.debug("queryString:: {}", queryString); + + + + String[] paramArr = queryString.split("&"); + for (String item : paramArr) { + log.debug("queryItem:: {}", item); + + String[] itemArr = item.split("="); + String key = itemArr[0]; + + + + String val = null; + try { + + if(itemArr.length > 1) { + val = itemArr[1]; + } + + if(itemArr.length == 1) { + paramList.add(java.net.URLDecoder.decode(key, StandardCharsets.UTF_8.toString()), null); + + }else { + paramList.add(java.net.URLDecoder.decode(key, StandardCharsets.UTF_8.toString()), java.net.URLDecoder.decode(val, StandardCharsets.UTF_8.toString())); + } + } catch (UnsupportedEncodingException e) { + throw e; + } + + + if (key.equals(prevKey)) { + sortFormData = true; + } + prevKey = key; + } + + } + + // add the form fields to paramList + if (formList != null && formList.size() > 0) { + log.debug("formData:: {}", formList.toString()); + paramList.addAll(formList.getFormList(sortFormData)); + } + + paramList.add(authPrefix + "_timestamp", timestamp); + paramList.add(authPrefix + "_nonce", nonce); + paramList.add(authPrefix + "_app_id", appId); + paramList.add(authPrefix + "_signature_method", signatureMethod.name()); + paramList.add(authPrefix + "_version", version); + baseString = httpMethod.toUpperCase() + "&" + url + "&" + paramList.toString(true); + + } catch (ApiUtilException ae) { + ae.printStackTrace(); + log.error("Error :: getBaseString :: " + ae.getMessage() ,ae); + throw ae; + } catch (Exception e) { + e.printStackTrace(); + log.error("Error :: getBaseString :: " + e.getMessage()); + throw new ApiUtilException("Error while getting Base String", e); + } + + log.debug("Exit :: getBaseString :: baseString : {} ", baseString); + + return baseString; + } + + /** * Get Signature Token for HTTP Authorization Header * @@ -499,27 +762,36 @@ public static String getSignatureToken( // Generate the nonce value try { - nonce = nonce != null ? nonce : Long.toString(getNewNonce()); + nonce = (nonce != null && !nonce.isEmpty()) ? nonce : getNewNonce(); } catch (NoSuchAlgorithmException nsae) { throw nsae; } timestamp = timestamp != null ? timestamp : Long.toString(getNewTimestamp()); - - if (secret != null) { - signatureMethod = "HMACSHA256"; - } else { - signatureMethod = "SHA256withRSA"; - } + if(authPrefix.toLowerCase().contains("l1")){ + signatureMethod = "HMACSHA256"; + }else if(authPrefix.toLowerCase().contains("l2")){ + signatureMethod = "SHA256withRSA"; + }else{ + throw new ApiUtilException("Invalid Authorization Prefix."); + } String baseString = getBaseString(authPrefix, signatureMethod , appId, urlPath, httpMethod , formList, nonce, timestamp); - if (secret != null) { + if ("HMACSHA256".equals(signatureMethod)) { base64Token = getHMACSignature(baseString, secret); - } else { - PrivateKey privateKey = getPrivateKeyFromKeyStore(fileName, password, alias); + } else if("SHA256withRSA".equals(signatureMethod)){ + PrivateKey privateKey = null; + if(null!=fileName && (fileName.contains(".key")||fileName.contains(".pem"))){ + privateKey = ApiSigning.getPrivateKeyPEM(fileName, password); + }else{ + //For JKS file + privateKey = ApiSigning.getPrivateKeyFromKeyStore(fileName, password, alias); + } + + //PrivateKey privateKey = getPrivateKeyFromKeyStore(fileName, password, alias); base64Token = getRSASignature(baseString, privateKey); } @@ -527,14 +799,15 @@ public static String getSignatureToken( ApiList tokenList = new ApiList(); tokenList.add("realm", realm); - tokenList.add(authPrefix + "_timestamp", timestamp); - tokenList.add(authPrefix + "_nonce", nonce); tokenList.add(authPrefix + "_app_id", appId); + tokenList.add(authPrefix + "_nonce", nonce); tokenList.add(authPrefix + "_signature_method", signatureMethod); - tokenList.add(authPrefix + "_signature", base64Token); + tokenList.add(authPrefix + "_timestamp", timestamp); tokenList.add(authPrefix + "_version", "1.0"); + tokenList.add(authPrefix + "_signature", base64Token); + - authorizationToken = String.format("%s %s", authPrefix.substring(0, 1).toUpperCase() + authPrefix.substring(1), tokenList.toString(", ", false, true)); + authorizationToken = String.format("%s %s", authPrefix.substring(0, 1).toUpperCase() + authPrefix.substring(1), tokenList.toString(", ", false, true, false)); } catch (ApiUtilException ae) { log.error("Error :: getToken :: " + ae.getMessage()); @@ -548,38 +821,267 @@ public static String getSignatureToken( return authorizationToken; } + + private final static String APEX1_DOMAIN = "api.gov.sg"; + + public URI url; +// String url; + public String httpMethod; + + public String appName; + public String appSecret; + public FormList formData; +// String password; +// String alias; +// String fileName; + //to update fileName type to PrivateKey + public PrivateKey privateKey; + public String nonce; + public String timestamp; + public SignatureMethod signatureMethod; + public String version = "1.0"; + + + public static AuthToken getSignatureTokenV2(AuthParam authParam) throws ApiUtilException { + log.debug("Enter :: getSignatureTokenV2 :: url : {} , httpMethod : {} , appName : {} ", + authParam.url.toString(), authParam.httpMethod, authParam.appName); + + String authorizationToken = null; + String base64Token = ""; + List baseStringList = new LinkedList(); + + try { + + if (authParam.url == null || authParam.url.getHost().split("\\.")[0] == null) { + + throw new ApiUtilException("URL does not exist"); + } + + + + // split url into hostname and domain + String gatewayName = authParam.url.getHost().split("\\.")[0]; + String originalDomain = String.join(".", Arrays.copyOfRange(authParam.url.getHost().split("\\."),1,authParam.url.getHost().split("\\.").length)); + + // default api domain to external zone + String apiDomain = String.format(".%s", originalDomain); + String authSuffix = "eg"; + + // for backward compatible with apex1 + if (authParam.url.getHost().endsWith(APEX1_DOMAIN)) + { + apiDomain = String.format(".e.%s", originalDomain); + } + + // switch to internal zone based on hostname suffix + if (gatewayName.endsWith("-pvt")) + { + authSuffix = "ig"; + + // for backward compatible with apex1 + if (authParam.url.getHost().endsWith(APEX1_DOMAIN)) + { + apiDomain = String.format(".i.%s", originalDomain); + } + } + + // default auth level to l2, switch to l1 if appSecret is not null or "" + String authLevel = "l2"; + if (StringUtils.isNotEmpty(authParam.appSecret)) + { + authLevel = "l1"; + } + + + // for backward compatible with apex1, update the signature url + String signatureUrl = authParam.url.toString().replace(String.format(".%s", originalDomain), apiDomain); + + + // Generate the nonce value + try { + authParam.nonce = (authParam.nonce != null && !authParam.nonce.isEmpty()) ? authParam.nonce : getNewNonce(); + } catch (NoSuchAlgorithmException nsae) { + throw nsae; + } + authParam.timestamp = authParam.timestamp != null ? authParam.timestamp : Long.toString(getNewTimestamp()); + + if(authLevel == "l1"){ + authParam.signatureMethod = SignatureMethod.HMACSHA256; + }else if(authLevel == "l2"){ + authParam.signatureMethod = SignatureMethod.SHA256withRSA; + }else{ + throw new ApiUtilException("Invalid Authorization Level."); + } + + + if (authParam.appName != null) { + String realm = String.format("https://%s", authParam.url.getHost()); + String authPrefix = String.format("apex_%s_%s", authLevel, authSuffix); + if (authParam.version == null) + { + authParam.version = "1.0"; + } + + + String baseString = getBaseString( + authPrefix, + authParam.signatureMethod, + authParam.appName, + new URI (signatureUrl), + authParam.httpMethod, + authParam.formData, + authParam.nonce, + authParam.timestamp, + authParam.version + ); + + baseStringList.add(baseString); + if (authLevel == "l1") { + base64Token = getHMACSignature(baseString, authParam.appSecret); + } else if(authLevel == "l2"){ + base64Token = getRSASignature(baseString, authParam.privateKey); + + } + + ApiList tokenList = new ApiList(); + + tokenList.add("realm", realm); + tokenList.add(authPrefix + "_app_id", authParam.appName); + tokenList.add(authPrefix + "_nonce", authParam.nonce); + tokenList.add(authPrefix + "_signature_method", authParam.signatureMethod.name()); + tokenList.add(authPrefix + "_timestamp", authParam.timestamp); + tokenList.add(authPrefix + "_version", authParam.version); + tokenList.add(authPrefix + "_signature", base64Token); + + + authorizationToken = String.format("%s %s", authPrefix.substring(0, 1).toUpperCase() + authPrefix.substring(1), tokenList.toString(", ", false, true, false)); + } + + } catch (ApiUtilException ae) { + log.error("Error :: getToken :: " + ae.getMessage()); + throw ae; + } catch (NullPointerException npe) { + log.error("Error :: getToken :: " + npe.getMessage()); + // throw "Value cannot be null."; + throw new ApiUtilException("Value cannot be null.", npe); + } catch (Exception e) { + log.error("Error :: getToken :: " + e.getMessage()); + throw new ApiUtilException("Error while getting Token", e); + } + + + + + String authToken = String.format("%s", authorizationToken); + + if (authParam.nextHop != null) { + // propagate the information from root param to nextHop + authParam.nextHop.httpMethod = authParam.httpMethod; + //authParam.nextHop.queryString = authParam.queryString; + authParam.nextHop.formData = authParam.formData; + + // get the apexToken for nextHop + AuthToken nextHopResult = getSignatureTokenV2(authParam.nextHop); + + // save the baseString + baseStringList.add(nextHopResult.getBaseString()); + + String netxHopToken = nextHopResult.getToken(); + + // combine the apexToken if required + if (authToken == "") { + authToken = String.format("%s", netxHopToken); + } else { + authToken += String.format(", %s", netxHopToken); + } + } + + log.debug("Exit :: getToken :: authorizationToken : {} ", authToken); + + return new AuthToken(authToken,baseStringList); + } private static long getNewTimestamp() { return System.currentTimeMillis(); } - private static long getNewNonce() throws NoSuchAlgorithmException { - long nonce = 0; - - nonce = SecureRandom.getInstance("SHA1PRNG").nextLong(); - + /** + * Get new Nonce value used for signature generation + * + * @return nonce value + * @throws NoSuchAlgorithmException + */ + private static String getNewNonce() throws NoSuchAlgorithmException { + String nonce = null; + byte[] b = new byte[32]; + SecureRandom.getInstance("SHA1PRNG").nextBytes(b); + nonce = Base64.getEncoder().encodeToString(b); + return nonce; } - private static TrustManager[] getTrustManager() { - // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - public void checkClientTrusted( - java.security.cert.X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted( - java.security.cert.X509Certificate[] certs, String authType) { - } - } - }; - - return trustAllCerts; - } + /** + * Get Private Key from PEM format file + * + * @param privateKeyFileName PEM file path + * @param password + * @return Private Key + * @throws ApiUtilException + */ + +// * @throws IOException +// * @throws GeneralSecurityException + public static PrivateKey getPrivateKeyPEM(String privateKeyFileName, String password) throws ApiUtilException{ +// public static PrivateKey getPrivateKeyPEM(String privateKeyFileName, String password) throws IOException, GeneralSecurityException, ApiUtilException{ + log.debug("Enter :: getPrivateKeyPEM :: privateKeyFileName : {} ", privateKeyFileName); + PrivateKey key = null; + PEMParser pemParser = null; + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + try{ + File privateKeyFile = new File(privateKeyFileName); // private key file in PEM format + pemParser = new PEMParser(new FileReader(privateKeyFile)); + Object object = pemParser.readObject(); + + PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray()); + InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray()); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + KeyPair kp = null; + if (object instanceof PEMEncryptedKeyPair) { + kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)); + key = kp.getPrivate(); + }else if (object instanceof PEMKeyPair) { + kp = converter.getKeyPair(((PEMKeyPair) object)); + key = kp.getPrivate(); + }else if(object instanceof KeyPair) { + kp = (KeyPair) object; + key = kp.getPrivate(); + }else if(object instanceof PrivateKeyInfo) { + PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(object); + key = (PrivateKey) converter.getPrivateKey(privateKeyInfo); + }else if(object instanceof PKCS8EncryptedPrivateKeyInfo) { + PrivateKeyInfo privateKeyInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(pkcs8Prov); + key = converter.getPrivateKey(privateKeyInfo); + }else { + throw new ApiUtilException("Error while getting Private Key from PEM"); + } + + }catch(Exception e){ + throw new ApiUtilException(e.getMessage(), e); + }finally{ + if(null!=pemParser){ + try { + pemParser.close(); + } catch (IOException ioe) { +// throw ioe; + // TODO Auto-generated catch block + throw new ApiUtilException(ioe.getMessage(), ioe); + } + } + } + log.debug("Exit :: getPrivateKeyPEM"); + return key; + + } } diff --git a/src/main/java/com/api/util/ApiSecurity/AuthParam.java b/src/main/java/com/api/util/ApiSecurity/AuthParam.java new file mode 100644 index 0000000..570e832 --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/AuthParam.java @@ -0,0 +1,31 @@ +package com.api.util.ApiSecurity; + +import java.net.URI; +import java.security.PrivateKey; +/** + * @author nsearch-sohboonkeong + * + */ +public class AuthParam { + + public URI url; + public String httpMethod; + + public String appName; + public String appSecret; + public FormList formData; + public PrivateKey privateKey; + public String nonce; + public String timestamp; + public SignatureMethod signatureMethod; + public String version = "1.0"; + public AuthParam nextHop; + + public AuthParam() { + + } + +} + + + diff --git a/src/main/java/com/api/util/ApiSecurity/AuthToken.java b/src/main/java/com/api/util/ApiSecurity/AuthToken.java new file mode 100644 index 0000000..63bbb38 --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/AuthToken.java @@ -0,0 +1,27 @@ +package com.api.util.ApiSecurity; + +import java.util.List; + +public class AuthToken { + + private String token; + private List baseStringList; + + public AuthToken(String t, List bSL) { + token = t; + baseStringList = bSL; + } + + public String getToken() { + return token; + } + + public List getBaseStringList() { + return baseStringList; + } + + public String getBaseString() { + return String.join(", ", baseStringList); + } + +} diff --git a/src/main/java/com/api/util/ApiSecurity/Form.java b/src/main/java/com/api/util/ApiSecurity/Form.java new file mode 100644 index 0000000..d9d7fce --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/Form.java @@ -0,0 +1,31 @@ +package com.api.util.ApiSecurity; + +import java.util.Map.Entry; + +public class Form implements Entry{ + private String key; + private FormField formfield; + + public Form(String k, FormField v) { + this.key = k; + this.formfield = v; + } + + + public String getKey() { + + return key; + } + + public FormField getValue() { + return formfield; + } + + + public FormField setValue(FormField value) { + this.formfield = value; + return this.formfield; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/api/util/ApiSecurity/FormField.java b/src/main/java/com/api/util/ApiSecurity/FormField.java new file mode 100644 index 0000000..8f4a1c9 --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/FormField.java @@ -0,0 +1,148 @@ +package com.api.util.ApiSecurity; + +import java.util.*; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +public class FormField { + + String _key = null; + + String _value = null; + String[] _arrayValue = null; + + public String setKey(String key) { + return _key = key; + } + + + public FormField(String value) { + _value = value; + } + + public FormField(String[] value) { + _arrayValue = value; + } + + + public FormField() { + } + + public void add(String[] newValue) { + List tempList = new ArrayList(); + + if (_arrayValue != null) + { + for (String item : _arrayValue) + { + tempList.add(item); + } + } + else + { + tempList.add(_value); + _value = null; + } + tempList.addAll(Arrays.asList(newValue)); + + // Can convert it back to an array if you would like to + _arrayValue = tempList.toArray(new String[0]); + + + + } + + + public String[] getRawValue() { + + if (_value!=null) { + return new String[]{_value}; + }else { + return _arrayValue; + } + + } + + + + public String getFormValue() + { + String delimiter = "&"; + String value = ""; + + if (_arrayValue == null) + { + try { + value = URLEncoder.encode(_value, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + int index = 0; + + for (String item : _arrayValue) + { + if (index == 0) + { + try { + value = URLEncoder.encode(item, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + try { + value += String.format("%s%s=%s", delimiter, URLEncoder.encode(_key, StandardCharsets.UTF_8.toString()), URLEncoder.encode(item, StandardCharsets.UTF_8.toString())); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + index++; + } + } + + return value; + + } + + public String getValue() + { + + String delimiter = "&"; + String value = ""; + + if (_arrayValue == null) + { + value = _value; + } + else + { + int index = 0; + + for (String item : _arrayValue) + { + if (index == 0) + { + value = item; + } + else + { + value += String.format("%s%s=%s", delimiter, _key, item); + } + index++; + } + } + + return value; + + } +} + + diff --git a/src/main/java/com/api/util/ApiSecurity/FormList.java b/src/main/java/com/api/util/ApiSecurity/FormList.java new file mode 100644 index 0000000..e695f0f --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/FormList.java @@ -0,0 +1,111 @@ +package com.api.util.ApiSecurity; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + + + + +public class FormList extends ArrayList>{ + + private static final long serialVersionUID = 1L; + + public FormList() { + + } + + public void add(String key, String[] value) { + FormField newValue = new FormField(value); + this.add(key, newValue); + + } + + public void add(String key, String value) { + FormField newValue = new FormField(value); + this.add(key, newValue); + } + + private void add(String key, FormField value) { + Boolean exist = false; + for (Entry entry : this) { + if (entry.getKey()== key) { + entry.getValue().add(value.getRawValue()); + exist = true; + } + } + if (!exist) { + value.setKey(key); + this.add(new AbstractMap.SimpleEntry(key,value)); + + } + + } + + /** + * getFormList (default no sort) + * @return FormList Array + */ + public ArrayList> getFormList(){ + return getFormList(false); + } + + + /** + * getFormList + * @param formDataArraySplit, set to true if query data also exist + * @return FormList Array + */ + public ArrayList> getFormList(boolean formDataArraySplit){ + ArrayList> list = new ArrayList>(); + for (Entry item : this) { + + if (!formDataArraySplit) { + Entry newItem = new AbstractMap.SimpleEntry(item.getKey(),item.getValue().getValue()); + list.add(newItem); + } else { + for (String val : item.getValue().getRawValue()) { + Entry newItem = new AbstractMap.SimpleEntry(item.getKey(),val); + list.add(newItem); + } + } + } + + return list; + } + + + + public String toFormData() + { + String delimiter = "&"; + + List list = new LinkedList(); + + String format = "%s=%s"; + + for (Entry item: this) + { + try { + list.add(String.format(format, URLEncoder.encode(item.getKey(), StandardCharsets.UTF_8.toString()), item.getValue().getFormValue())); + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + return String.join(delimiter, list); + } + + + public String toQueryString() + { + return "?" + toFormData(); + } + +} diff --git a/src/main/java/com/api/util/ApiSecurity/QueryData.java b/src/main/java/com/api/util/ApiSecurity/QueryData.java new file mode 100644 index 0000000..06de389 --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/QueryData.java @@ -0,0 +1,9 @@ +package com.api.util.ApiSecurity; + +public class QueryData extends FormList { + + + + + +} \ No newline at end of file diff --git a/src/main/java/com/api/util/ApiSecurity/SignatureMethod.java b/src/main/java/com/api/util/ApiSecurity/SignatureMethod.java new file mode 100644 index 0000000..15085d7 --- /dev/null +++ b/src/main/java/com/api/util/ApiSecurity/SignatureMethod.java @@ -0,0 +1,6 @@ +package com.api.util.ApiSecurity; + +public enum SignatureMethod { + HMACSHA256, + SHA256withRSA +} diff --git a/src/main/resources/data/testparam.json b/src/main/resources/data/testparam.json new file mode 100644 index 0000000..aabed5c --- /dev/null +++ b/src/main/resources/data/testparam.json @@ -0,0 +1,25 @@ +{ + "id": "7", + "description": "HTTPCall - XX QueryString/formData with array, json object, null value and empty param", + "apiName": "[FT] Helloworld REST L2 External-External", + "message": "message", + "publicCertFileName":"cert/alpha/training.alpha.apex.lab.public.pem", + "skipTest": ["java","nodejs"], + "errorTest":"error", + "apiParam": { + "realm": "apex.l2.test", + "authPrefix": "Apex_l2_eg", + "appId": "training-3O9HWXNyVT0701KyJIZte787", + "invokeUrl": "https://training.api.lab:443/ai/training/rest/v1/level2/ex-ex/1?nullValue1=a&nullValue1=d", + "signatureUrl": "https://training.api.lab:443/ai/training/rest/v1/level2/ex-ex/1?nullValue1=a&nullValue1=d", + "httpMethod": "POST", + "queryString": {}, + "formData": {}, + "privateCertFileName": "cert/alpha/training.alpha.apex.lab.nopass.pem", + "nonce": "1536044563255", + "timestamp": "HuEN209VggqJ9RQQ0fM", + "secret": "secret", + "signature":"signature" + }, + "expectedResult": {} +} \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties deleted file mode 100644 index 2c7c575..0000000 --- a/src/main/resources/log4j.properties +++ /dev/null @@ -1,16 +0,0 @@ -# Root logger -log4j.rootLogger=INFO, stdout - -# log to console -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p:: %m%n - -# log to file -#log4j.appender.file=org.apache.log4j.RollingFileAppender -#log4j.appender.file.File= -#log4j.appender.file.MaxFileSize=10000KB -#log4j.appender.file.MaxBackupIndex=10 -#log4j.appender.file.layout=org.apache.log4j.PatternLayout -#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p:: %m%n diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties new file mode 100644 index 0000000..51fb853 --- /dev/null +++ b/src/main/resources/log4j2.properties @@ -0,0 +1,38 @@ + +status = error +dest = err +name = PropertiesConfig + + +# log to console +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} [%t] %-5p:: %m%n + + +# log to file +# property.filename = target/rolling/rollingtest.log +# appender.rolling.type = RollingFile +# appender.rolling.name = RollingFile +# appender.rolling.fileName = ${filename} +# appender.rolling.filePattern = target/rolling2/test1-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz +# appender.rolling.layout.type = PatternLayout +# appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} [%t] %-5p:: %m%n +# appender.rolling.policies.type = Policies +# appender.rolling.policies.time.type = TimeBasedTriggeringPolicy +# appender.rolling.policies.time.interval = 2 +# appender.rolling.policies.time.modulate = true +# appender.rolling.policies.size.type = SizeBasedTriggeringPolicy +# appender.rolling.policies.size.size=10000KB +# appender.rolling.strategy.type = DefaultRolloverStrategy +# appender.rolling.strategy.max = 10 +# logger.rolling.name = com.example.my.app +# logger.rolling.level = debug +# logger.rolling.additivity = false +# logger.rolling.appenderRef.rolling.ref = RollingFile + + + #Root logger +rootLogger.level = debug +rootLogger.appenderRef.stdout.ref = STDOUT \ No newline at end of file diff --git a/src/main/resources/schema/TestDatum.json b/src/main/resources/schema/TestDatum.json new file mode 100644 index 0000000..d8cafaa --- /dev/null +++ b/src/main/resources/schema/TestDatum.json @@ -0,0 +1,253 @@ +{ + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/root.json", + "type": "array", + "title": "The Root Schema", + "items": { + "$id": "#/items", + "type": "object", + "title": "The Items Schema", + "required": [ + "id", + "description", + "apiName", + "apiParam", + "expectedResult", + "message", + "publicCertFileName", + "skipTest", + "errorTest" + ], + "properties": { + "id": { + "$id": "#/items/properties/id", + "type": "string", + "title": "The Id Schema", + "default": "", + "examples": [ + "7" + ], + "pattern": "^(.*)$" + }, + "description": { + "$id": "#/items/properties/description", + "type": "string", + "title": "The Description Schema", + "default": "", + "examples": [ + "HTTPCall - XX QueryString/formData with array, json object, null value and empty param" + ], + "pattern": "^(.*)$" + }, + "apiName": { + "$id": "#/items/properties/apiName", + "type": "string", + "title": "The Apiname Schema", + "default": "", + "examples": [ + "[FT] Helloworld REST L2 External-External" + ], + "pattern": "^(.*)$" + }, + "apiParam": { + "$id": "#/items/properties/apiParam", + "type": "object", + "title": "The Apiparam Schema", + "required": [ + "realm", + "authPrefix", + "appId", + "invokeUrl", + "signatureUrl", + "httpMethod", + "queryString", + "formData", + "privateCertFileName", + "nonce", + "timestamp", + "secret", + "passphrase", + "signature" + ], + "properties": { + "realm": { + "$id": "#/items/properties/apiParam/properties/realm", + "type": "string", + "title": "The Realm Schema", + "default": "", + "examples": [ + "apex.l2.test" + ], + "pattern": "^(.*)$" + }, + "authPrefix": { + "$id": "#/items/properties/apiParam/properties/authPrefix", + "type": "string", + "title": "The Authprefix Schema", + "default": "", + "examples": [ + "Apex_l2_eg" + ], + "pattern": "^(.*)$" + }, + "appId": { + "$id": "#/items/properties/apiParam/properties/appId", + "type": "string", + "title": "The Appid Schema", + "default": "", + "examples": [ + "training-3O9HWXNyVT0701KyJIZte787" + ], + "pattern": "^(.*)$" + }, + "invokeUrl": { + "$id": "#/items/properties/apiParam/properties/invokeUrl", + "type": "string", + "title": "The Invokeurl Schema", + "default": "", + "examples": [ + "https://training.api.lab:443/ai/training/rest/v1/level2/ex-ex/1?nullValue1=a&nullValue1=d" + ], + "pattern": "^(.*)$" + }, + "signatureUrl": { + "$id": "#/items/properties/apiParam/properties/signatureUrl", + "type": "string", + "title": "The Signatureurl Schema", + "default": "", + "examples": [ + "https://training.api.lab:443/ai/training/rest/v1/level2/ex-ex/1?nullValue1=a&nullValue1=d" + ], + "pattern": "^(.*)$" + }, + "httpMethod": { + "$id": "#/items/properties/apiParam/properties/httpMethod", + "type": "string", + "title": "The Httpmethod Schema", + "default": "", + "examples": [ + "POST" + ], + "pattern": "^(.*)$" + }, + "queryString": { + "$id": "#/items/properties/apiParam/properties/queryString", + "type": "object", + "title": "The Querystring Schema" + }, + "formData": { + "$id": "#/items/properties/apiParam/properties/formData", + "type": "object", + "title": "The Formdata Schema" + }, + "privateCertFileName": { + "$id": "#/items/properties/apiParam/properties/privateCertFileName", + "type": "string", + "title": "The Privatecertfilename Schema", + "default": "", + "examples": [ + "cert/alpha/training.alpha.apex.lab.nopass.pem" + ], + "pattern": "^(.*)$" + }, + "nonce": { + "$id": "#/items/properties/apiParam/properties/nonce", + "type": "string", + "title": "The Nonce Schema", + "default": "", + "examples": [ + "1536044563255" + ], + "pattern": "^(.*)$" + }, + "timestamp": { + "$id": "#/items/properties/apiParam/properties/timestamp", + "type": "string", + "title": "The Timestamp Schema", + "default": "", + "examples": [ + "HuEN209VggqJ9RQQ0fM" + ], + "pattern": "^(.*)$" + }, + "secret": { + "$id": "#/items/properties/apiParam/properties/secret", + "type": "string", + "title": "The Secret Schema", + "default": "", + "examples": [ + "secret" + ], + "pattern": "^(.*)$" + }, + "signature": { + "$id": "#/items/properties/apiParam/properties/signature", + "type": "string", + "title": "The Signature Schema", + "default": "", + "examples": [ + "signature" + ], + "pattern": "^(.*)$" + }, + "passphrase": { + "$id": "#/items/properties/apiParam/properties/passphrase", + "type": "string", + "title": "The Passphrase Schema", + "default": "", + "examples": [ + "passphrase" + ], + "pattern": "^(.*)$" + } + } + }, + "expectedResult": { + "$id": "#/items/properties/expectedResult", + "type": "object", + "title": "The Expectedresult Schema" + }, + "message": { + "$id": "#/items/properties/message", + "type": "string", + "title": "The Message Schema", + "default": "", + "examples": [ + "message" + ], + "pattern": "^(.*)$" + }, + "publicCertFileName": { + "$id": "#/items/properties/publicCertFileName", + "type": "string", + "title": "The PublicCertFileName Schema", + "default": "", + "examples": [ + "cert/alpha/training.alpha.apex.lab.public.pem" + ], + "pattern": "^(.*)$" + }, + "skipTest": { + "$id": "#/items/properties/skipTest", + "type": "array", + "title": "The SkipTest Schema", + "default": "", + "examples": [ + ["java","nodejs"] + ], + "pattern": "^(.*)$" + }, + "errorTest": { + "$id": "#/items/properties/errorTest", + "type": "boolean", + "title": "The ErrorTest Schema", + "default": "", + "examples": [ + "true" + ], + "pattern": "^(.*)$" + } + } + } +} \ No newline at end of file diff --git a/src/test/java/AuthorizationTokenTest.java b/src/test/java/AuthorizationTokenTest.java deleted file mode 100644 index cd33877..0000000 --- a/src/test/java/AuthorizationTokenTest.java +++ /dev/null @@ -1,187 +0,0 @@ -import com.api.util.ApiSecurity.ApiSigning; -import com.api.util.ApiSecurity.ApiUtilException; -import org.junit.Test; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.junit.Assert.*; - -/** - * @author GDS-PDD - * - */ -public class AuthorizationTokenTest { - - private final static String privateCertNameP12 = getLocalPath("certificates/ssc.alpha.example.com.p12"); - - private final static String alias = "alpha"; - - private final static boolean liveTest = false; - - private final static String realm = "http://example.api.test/token"; - private final static String authPrefixL1 = "Apex_l1_ig"; - private final static String authPrefixL2 = "Apex_l2_ig"; - private final static String httpMethod = "get"; - private final static String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - private final static String appId = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; - private final static String secret = "ffef0c5087f8dc24a3f122e1e2040cdeb5f72c73"; - private final static String nonce = "-5816789581922453013"; - private final static String timestamp = "1502199514462"; - private final static String passphrase = "passwordp12"; - - private static String getLocalPath(String relativeFileName) - { - Path currentRelativePath = Paths.get(""); - String s = combine(currentRelativePath.toAbsolutePath().toString(), relativeFileName.replaceAll("/", File.separator)); - - return s; - } - - public static String combine(String... paths) - { - File file = new File(File.separator); - - for (int i = 0; i < paths.length ; i++) { - file = new File(file, paths[i]); - } - - return file.getPath(); - } - - @Test - public void Test_L1_Basic_Test() throws ApiUtilException - { - String expectedToken = "Apex_l1_ig realm=\"http://example.api.test/token\", apex_l1_ig_timestamp=\"1502199514462\", apex_l1_ig_nonce=\"-5816789581922453013\", apex_l1_ig_app_id=\"example-4Swyn7qwKeO32EXdH1dKTeIQ\", apex_l1_ig_signature_method=\"HMACSHA256\", apex_l1_ig_signature=\"DoARux+dvq/A2ioQfRybInAQ4Lt4DTAI6DrDJRx7zcs=\", apex_l1_ig_version=\"1.0\""; - - String authorizationToken = ApiSigning.getSignatureToken( - realm - , authPrefixL1 - , httpMethod - , url - , appId - , secret - , null - , null - , null - , null - , nonce - , timestamp - ); - System.out.println("expectedToken:"+expectedToken); - System.out.println("authorizationToken:"+authorizationToken); - assertEquals(expectedToken, authorizationToken); - } - - @Test - public void Test_L2_Basic_Test() throws ApiUtilException - { - String expectedToken = "Apex_l2_ig realm=\"http://example.api.test/token\", apex_l2_ig_timestamp=\"1502199514462\", apex_l2_ig_nonce=\"-5816789581922453013\", apex_l2_ig_app_id=\"example-4Swyn7qwKeO32EXdH1dKTeIQ\", apex_l2_ig_signature_method=\"SHA256withRSA\", apex_l2_ig_signature=\"Za7B8MaOlGZjc8DTEh9HwhcL+5DiiuTMy+s0bQ8/lajy1Ug64gPCyNEbcYkD/XBEHFyg6vlY9/J85Y+Ui6DeYbXmUFnQjDWdOKf13xJvpsnAQgOqWi+LSc0+gy3pvsQ50nyES3E04vb3RvGwd7UC6SyBhmQ5P8Mz0UUgWBX6L6N3n+xergTg3DKWEPyQih+dqN3DkOmNE8fstAp+HOqiVq2OBxNeg9x5Kp0tq2vka7cC86zdYSNhsQR+D7hC+S1NPninWvdxUF1EwrPrEZYSYXka0Md1XFVjaL6b0htcFo6LxwJ8X6wsOqS4g4qmrAadwm7fITZLxcI0Zdaz7dRw9UFUsGWEVPG8MQztVXleimDxYvorLKTD5bhWGHe+XNwyL+IdR7ErooOHP9pTslJ7yBEmsePTRIAL//h0AEXaBN4pCmBPJnVtYtUWdQsUq/iv/4FLtWvOK77EReAtq3uqndJfGInXUMESqS4PzGDajTZj+oDP7xektLh7umELQBnSKNuv3BR9H63sf+Z9mZQ1531LYEmQWR8p3LCP8E0DcROo0OP1gcE76N9Z1HKLtJjLYDRyQRUQMM2FlJRkb3sy2g60yNThkPprzohBvHowCRFs02tlkyBbOuKC2cV9hwSz8eMqhUTzNn/WMi2Dr2V7iTJtyJHT9kdebVY2Cvnlt5I=\", apex_l2_ig_version=\"1.0\""; - String authorizationToken = ApiSigning.getSignatureToken( - realm - , authPrefixL2 - , httpMethod - , url - , appId - , null - , null - , passphrase - , alias - , privateCertNameP12 - , nonce - , timestamp - ); - - System.out.println("Expected Token:" + authorizationToken); - assertEquals(expectedToken, authorizationToken); - } - - @Test - public void Test_L2_Wrong_Password_Test() throws ApiUtilException - { - String expectedMessage = "keystore password was incorrect"; - - try { - ApiSigning.getSignatureToken( - realm - , authPrefixL2 - , httpMethod - , url - , appId - , null - , null - , passphrase + "x" - , alias - , privateCertNameP12 - , null - , null - ); - } - catch (ApiUtilException expected) - { - assertEquals(expectedMessage, expected.getCause().getMessage()); - } - } - - @Test - public void Test_L2_Not_Supported_Cert_Test() throws ApiUtilException - { - String fileName = getLocalPath("certificates/ssc.alpha.example.com.pem"); - - String expectedMessage = "Invalid keystore format"; - - try { - - ApiSigning.getSignatureToken( - realm - , authPrefixL2 - , httpMethod - , url - , appId - , null - , null - , passphrase - , alias - , fileName - , null - , null - ); - } - catch (ApiUtilException expected) - { - assertEquals(expectedMessage, expected.getCause().getMessage()); - } - } - - @Test - public void Test_L2_Invalid_FileName_Test() throws ApiUtilException - { - String fileName = getLocalPath("Xalpha.test.p12"); - - String expectedMessage = "No such file or directory"; - - try { - - ApiSigning.getSignatureToken( - realm - , authPrefixL2 - , httpMethod - , url - , appId - , null - , null - , passphrase - , alias - , fileName - , null - , null - ); - } - catch (ApiUtilException expected) - { - assertTrue(expected.getCause().getMessage().contains(expectedMessage)); - } - } - -} diff --git a/src/test/java/BaseStringTest.java b/src/test/java/BaseStringTest.java deleted file mode 100644 index b4e2478..0000000 --- a/src/test/java/BaseStringTest.java +++ /dev/null @@ -1,109 +0,0 @@ -import static org.junit.Assert.*; -import java.net.URISyntaxException; -import org.junit.Test; - -import com.api.util.ApiSecurity.ApiSigning; -import com.api.util.ApiSecurity.ApiList; -import com.api.util.ApiSecurity.ApiUtilException; - -/** - * @author GDS-PDD - * - */ -public class BaseStringTest { - - @Test - public void BaseString_Basic_Test() throws ApiUtilException - { - String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - String expectedBaseString = "GET&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=1355584618267440511&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502175057654&apex_l1_ig_version=1.0"; - - String baseString = ApiSigning.getBaseString( - "Apex_L1_IG", - "HMACSHA256", - "example-4Swyn7qwKeO32EXdH1dKTeIQ", - url, - "get", - null, - "1355584618267440511", - "1502175057654" - ); - - assertEquals(expectedBaseString, baseString); - } - - @Test - public void BaseString_FormData_Test() throws ApiUtilException - { - String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - String expectedBaseString = "POST&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=6584351262900708156&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502184161702&apex_l1_ig_version=1.0¶m1=data1"; - - ApiList formList = new ApiList(); - formList.add("param1", "data1"); - - String baseString = ApiSigning.getBaseString( - "Apex_L1_IG", - "HMACSHA256", - "example-4Swyn7qwKeO32EXdH1dKTeIQ", - url, - "post", - formList, - "6584351262900708156", - "1502184161702" - ); - - assertEquals(expectedBaseString, baseString); - } - - @Test - public void BaseString_Invalid_Url_01_Test() - { - String url = "ftp://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - - String expectedMessage = "Support http and https protocol only."; - - try { - ApiSigning.getBaseString( - "Apex_L1_IG", - "HMACSHA256", - "example-4Swyn7qwKeO32EXdH1dKTeIQ", - url, - "post", - null, - "6584351262900708156", - "1502184161702" - ); - } - catch (ApiUtilException expected) - { - assertEquals(expectedMessage, expected.getMessage()); - } - } - - @Test - public void BaseString_Invalid_Url_02_Test() - { - String url = "://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; - - try { - ApiSigning.getBaseString( - "Apex_L1_IG", - "HMACSHA256", - "example-4Swyn7qwKeO32EXdH1dKTeIQ", - url, - "post", - null, - "6584351262900708156", - "1502184161702" - ); - } - catch (ApiUtilException expected) - { - if (!(expected.getCause() instanceof URISyntaxException)) - { - fail("Expecting URISyntaxException error."); - } - } - } - -} diff --git a/src/test/java/com/api/util/ApiSecurity/ApiSecurityTest.java b/src/test/java/com/api/util/ApiSecurity/ApiSecurityTest.java new file mode 100644 index 0000000..2890a68 --- /dev/null +++ b/src/test/java/com/api/util/ApiSecurity/ApiSecurityTest.java @@ -0,0 +1,138 @@ +package com.api.util.ApiSecurity; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.runner.RunWith; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import com.api.util.testframework.JUnitFactoryRunner; +import com.api.util.testframework.JUnitTestFactory; +import com.api.util.testframework.RuntimeTestCase; +import com.api.util.testframework.dto.TestDatum; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * @author GDS-PDD + * + * This class is a JUnit TC, which we need to run to test the given file, + * which may have n number of test-cases. + */ +@RunWith(JUnitFactoryRunner.class) +public class ApiSecurityTest { + + private static final Logger log = LogManager.getLogger(ApiSecurityTest.class); + + private static final String testDataPath = getLocalPath("src/main/resources/test-suites/testData/"); + + + /** Here's where to set up test case inputs, and their expected result will be + * read from a text-file, and will be populated as an map + * (Map) and need to compare values with + * their corresponding expected result fetched from map. + * This basically reads the text input file and prepare the List. + * and provides this List as an input to RuntimeTestCase + * @return + * @throws IOException + */ + @JUnitTestFactory + public static Collection tests() throws IOException { + + ObjectMapper objectMapper = new ObjectMapper(); + TypeFactory typeFactory = objectMapper.getTypeFactory(); + File testDataFolder = new File(testDataPath); + File[] testDataFile = testDataFolder.listFiles(); + Map testNameAndDatumMap = new LinkedHashMap(); + for (int i = 0; i < testDataFile.length; i++) { + List testDataSingle = new ArrayList(); + if (testDataFile[i].isFile()) { + System.out.println("File: " + testDataFile[i].getName()); + try{ + String jsonString = getJSON(testDataFile[i].getCanonicalPath()); + testDataSingle = objectMapper.readValue(jsonString, typeFactory.constructCollectionType(List.class, TestDatum.class)); + for(TestDatum datum : testDataSingle){ + if(testDataFile[i].getName().contains("defaultParams") || testDataFile[i].getName().contains("httpCall") ){ + continue; + } + testNameAndDatumMap.put(datum.getId() + "_" + testDataFile[i].getName(), datum); + } + }catch(IOException ioe){ + ioe.printStackTrace(); + throw ioe; + } + } + } + Set> testNameAndDatumMapSet = testNameAndDatumMap.entrySet(); + ArrayList tests = new ArrayList(testNameAndDatumMap.size()); + for(Entry entry : testNameAndDatumMapSet){ + String testName = entry.getKey(); + TestDatum testDatum = entry.getValue(); + if(null != testDatum.getSkipTest() && testDatum.getSkipTest().contains("java")){ + log.debug("Skip test: " + testName); + continue; + } + tests.add(new RuntimeTestCase(testName, testDatum)); + } + return tests; + + } + + private static String getLocalPath(String relativeFileName) { + Path currentRelativePath = Paths.get(""); + String s = combine(currentRelativePath.toAbsolutePath().toString(), relativeFileName.replaceAll("/", File.separator)); + + return s; + } + private static String combine(String... paths) { + File file = new File(File.separator); + + for (int i = 0; i < paths.length; i++) { + file = new File(file, paths[i]); + } + + return file.getPath(); + } + private static String getJSON(String path) throws IOException{ + java.io.FileInputStream fis = null; + String json; + try { + fis = new java.io.FileInputStream(path); + try( BufferedReader br = + new BufferedReader( new InputStreamReader(fis, StandardCharsets.UTF_8 ))) + { + StringBuilder sb = new StringBuilder(); + String line; + while(( line = br.readLine()) != null ) { + sb.append( line ); + sb.append( '\n' ); + } + json = sb.toString(); + } + } catch (IOException ioe) { + throw ioe; + } finally { + if (fis != null) { + try { + fis.close(); + } catch (IOException ioe) { + throw ioe; + } + } + } + return json; + } +} \ No newline at end of file diff --git a/src/test/java/com/api/util/ApiSecurity/AuthorizationTokenTest.java b/src/test/java/com/api/util/ApiSecurity/AuthorizationTokenTest.java new file mode 100644 index 0000000..8802691 --- /dev/null +++ b/src/test/java/com/api/util/ApiSecurity/AuthorizationTokenTest.java @@ -0,0 +1,236 @@ +package com.api.util.ApiSecurity; +import org.junit.Test; +import java.net.URI; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import static org.junit.Assert.*; + +/** + * @author GDS-ENP + * + */ +public class AuthorizationTokenTest { + + private final static String privateCertNameP12 = getLocalPath("certificates/ssc.alpha.example.com.p12"); + + private final static String alias = "alpha"; + + private final static boolean liveTest = false; + +// private final static String realm = "http://example.api.test/token"; +// private final static String authPrefixL1 = "Apex_l1_ig"; +// private final static String authPrefixL2 = "Apex_l2_ig"; + private final static String httpMethod = "get"; + private final static String urlString = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + private final static URI url = URI.create(urlString); + private final static String appId = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; + private final static String secret = "ffef0c5087f8dc24a3f122e1e2040cdeb5f72c73"; + private final static String nonce = "-5816789581922453013"; + private final static String timestamp = "1502199514462"; + private final static String passphrase = "passwordp12"; + + + + private static String getLocalPath(String relativeFileName) + { + Path currentRelativePath = Paths.get(""); + String s = combine(currentRelativePath.toAbsolutePath().toString(), relativeFileName.replaceAll("/", File.separator)); + + return s; + } + + public static String combine(String... paths) + { + File file = new File(File.separator); + + for (int i = 0; i < paths.length ; i++) { + file = new File(file, paths[i]); + } + + return file.getPath(); + } + + @Test + public void Test_L1_Basic_Test() throws ApiUtilException + { + String expectedToken = "Apex_l1_eg realm=\"https://example.lab\", apex_l1_eg_app_id=\"example-4Swyn7qwKeO32EXdH1dKTeIQ\", apex_l1_eg_nonce=\"-5816789581922453013\", apex_l1_eg_signature_method=\"HMACSHA256\", apex_l1_eg_timestamp=\"1502199514462\", apex_l1_eg_version=\"1.0\", apex_l1_eg_signature=\"Ili+E6mPb96dkzZM7yJaKPiGA6JjbR/B25oVmkp/Rxs=\""; + + AuthParam authParam = new AuthParam(); + + authParam.url = url; + authParam.httpMethod = httpMethod; + authParam.appName = appId; + authParam.appSecret = secret; + authParam.nonce = nonce; + authParam.timestamp = timestamp; + +// String authorizationToken = ApiSigning.getSignatureToken( +// realm +// , authPrefixL1 +// , httpMethod +// , url +// , appId +// , secret +// , null +// , null +// , null +// , null +// , nonce +// , timestamp +// ); + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); +// System.out.println("expectedToken:"+expectedToken); +// System.out.println("authorizationToken:"+authorizationToken); + assertEquals(expectedToken, authorizationToken.getToken()); + } + + @Test + public void Test_L2_Basic_Test() throws ApiUtilException, IOException, GeneralSecurityException + { + String expectedToken = "Apex_l2_eg realm=\"https://example.lab\", apex_l2_eg_app_id=\"example-4Swyn7qwKeO32EXdH1dKTeIQ\", apex_l2_eg_nonce=\"-5816789581922453013\", apex_l2_eg_signature_method=\"SHA256withRSA\", apex_l2_eg_timestamp=\"1502199514462\", apex_l2_eg_version=\"1.0\", apex_l2_eg_signature=\"aFbjNdDoSRPNcy6amYoS+gThv8q+rJhbxBWs7oyU+BN7bhkn1C+BSfQUUGb+oh1R+gKfoBwpdNptSvkCBpopzAmCRc6m79Haki4CFeWIDvqlyzbIp1MQBj36giOOxBrfijEtL5LdXG+lOb5D+WVJaOx6xstnumy1HsMdjG0mp0qbTzG2wTKNPN09/UG3wyChg7eGMtrif93gIXA7gIZmXNrm38qXJsz5A99b/PoROMS1koxK3YFkVSJ7o8cC/XDu1FJWS2Fz/U4rBQPjz1GrzUr1G0xrfeKiUX/UREVkoEvb9403LZjUP/t1uDCdFWA1ugspnHMVyePCierRtdEZ29dEBAB9jUGNJjp03xwyfG/ZLph3kknuLlLjMQoqG6RZ4USVQgIb1kENLnNxlWcR7NdMM9ebqiTNrpWCTxiSvkILN1ybX6DLvPHxXJdPP5cFHgwW4WsZV2HHzrrGLLXzis/marwsC/90cemCMO5vic71RPlqmv/xD2gMZvJ0sEjRDQ4HiCNp9CWhSy4tdu7EpZ7qeh4ecu2o1DUQdauLNFQwNUgh3K2uByOZ8TPCGjRnSqYply98BUIxHECviI77YK8Tr45GsalT+8Q5e2HMWJV/mcNHbSlz1WxT+hhc8DdBKqf23TUOgi2N/EuzU5X0DffUHAJBx9MHSpI4pMGW+TQ=\""; +// String authorizationToken = ApiSigning.getSignatureToken( +// realm +// , authPrefixL2 +// , httpMethod +// , url +// , appId +// , null +// , null +// , passphrase +// , alias +// , privateCertNameP12 +// , nonce +// , timestamp +// ); + + AuthParam authParam = new AuthParam(); + + authParam.url = url; + authParam.httpMethod = httpMethod; + authParam.appName = appId; + // authParam.privateKey; + authParam.nonce = nonce; + authParam.timestamp = timestamp; + + authParam.privateKey = ApiSigning.getPrivateKey(privateCertNameP12, passphrase, alias); + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + assertEquals(expectedToken, authorizationToken.getToken()); + } + + @Test + public void Test_L2_Wrong_Password_Test() throws ApiUtilException + { + String expectedMessage = "keystore password was incorrect"; + + try { +// ApiSigning.getSignatureToken( +// realm +// , authPrefixL2 +// , httpMethod +// , url +// , appId +// , null +// , null +// , passphrase + "x" +// , alias +// , privateCertNameP12 +// , null +// , null +// ); + AuthParam authParam = new AuthParam(); + + authParam.url = url; + authParam.httpMethod = httpMethod; + authParam.appName = appId; + String wrongPassphrase = passphrase + "x"; + authParam.privateKey = ApiSigning.getPrivateKey(privateCertNameP12, wrongPassphrase, alias); + ApiSigning.getSignatureTokenV2(authParam); + } + catch (ApiUtilException expected) + { + assertEquals(expectedMessage, expected.getCause().getMessage()); + } + } + + @Test + public void Test_L2_Not_Supported_Cert_Test() throws ApiUtilException +// , IOException, GeneralSecurityException + { + String fileName = getLocalPath("certificates/ssc.alpha.example.com.pem"); + String expectedMessage = "unable to read encrypted data: Error finalising cipher"; + //String fileName = getLocalPath("certificates/team20.pem"); + //String expectedMessage = "org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo cannot be cast to org.bouncycastle.openssl.PEMKeyPair"; + + try { + +// ApiSigning.getSignatureToken( +// realm +// , authPrefixL2 +// , httpMethod +// , url +// , appId +// , null +// , null +// , passphrase +// , alias +// , fileName +// , null +// , null +// ); + AuthParam authParam = new AuthParam(); + + authParam.url = url; + authParam.httpMethod = httpMethod; + authParam.appName = appId; + authParam.privateKey = ApiSigning.getPrivateKey(fileName, passphrase, alias); + ApiSigning.getSignatureTokenV2(authParam); + + } + catch (ApiUtilException expected) + { + assertEquals(expectedMessage, expected.getCause().getMessage()); + + } + } + + @Test + public void Test_L2_Invalid_FileName_Test() throws ApiUtilException, IOException, GeneralSecurityException + { + String fileName = getLocalPath("Xalpha.test.p12"); + + String expectedMessage = "No such file or directory"; + + try { + +// ApiSigning.getSignatureToken( +// realm +// , authPrefixL2 +// , httpMethod +// , url +// , appId +// , null +// , null +// , passphrase +// , alias +// , fileName +// , null +// , null +// ); + AuthParam authParam = new AuthParam(); + + authParam.url = url; + authParam.httpMethod = httpMethod; + authParam.appName = appId; + authParam.privateKey = ApiSigning.getPrivateKey(fileName, passphrase, alias); + ApiSigning.getSignatureTokenV2(authParam); + } + catch (ApiUtilException expected) + { + assertTrue(expected.getCause().getMessage().contains(expectedMessage)); + } + } + +} diff --git a/src/test/java/com/api/util/ApiSecurity/BaseStringTest.java b/src/test/java/com/api/util/ApiSecurity/BaseStringTest.java new file mode 100644 index 0000000..984a771 --- /dev/null +++ b/src/test/java/com/api/util/ApiSecurity/BaseStringTest.java @@ -0,0 +1,221 @@ +package com.api.util.ApiSecurity; +import static org.junit.Assert.*; + +import java.net.URI; +import java.net.URISyntaxException; +import org.junit.Test; + +import com.api.util.ApiSecurity.ApiSigning; +import com.api.util.ApiSecurity.ApiList; +import com.api.util.ApiSecurity.ApiUtilException; + +/** + * @author GDS-PDD + * + */ +public class BaseStringTest { + + @Test + public void BaseString_Basic_Test() throws ApiUtilException + { + String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + String expectedBaseString = "GET&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=1355584618267440511&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502175057654&apex_l1_ig_version=1.0"; + + String baseString = ApiSigning.getBaseString( + "Apex_L1_IG", + "HMACSHA256", + "example-4Swyn7qwKeO32EXdH1dKTeIQ", + url, + "get", + null, + "1355584618267440511", + "1502175057654" + ); + + + + assertEquals(expectedBaseString, baseString); + } + + @Test + public void BaseString_New_Basic_Test() throws ApiUtilException + { + String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + String expectedBaseString = "GET&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=1355584618267440511&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502175057654&apex_l1_ig_version=1.0"; + + try { + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(url); + authParam.httpMethod = "GET"; + authParam.appName = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; + authParam.nonce = "1355584618267440511"; + authParam.timestamp = "1502175057654"; + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + assertEquals(expectedBaseString, authorizationToken.getBaseString()); + } + catch (ApiUtilException e) + { + e.printStackTrace(); + } + + } + + @Test + public void BaseString_FormData_Test() throws ApiUtilException + { + String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + String expectedBaseString = "POST&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=6584351262900708156&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502184161702&apex_l1_ig_version=1.0¶m1=data1"; + + ApiList formList = new ApiList(); + formList.add("param1", "data1"); + + String baseString = ApiSigning.getBaseString( + "Apex_L1_IG", + "HMACSHA256", + "example-4Swyn7qwKeO32EXdH1dKTeIQ", + url, + "post", + formList, + "6584351262900708156", + "1502184161702" + ); + + assertEquals(expectedBaseString, baseString); + } + + @Test + public void BaseString_New_FormData_Test() throws ApiUtilException + { + String url = "https://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + String expectedBaseString = "POST&https://example.lab/api/v1/rest/level1/in-in/&ap=裕廊坊 心邻坊&apex_l1_ig_app_id=example-4Swyn7qwKeO32EXdH1dKTeIQ&apex_l1_ig_nonce=6584351262900708156&apex_l1_ig_signature_method=HMACSHA256&apex_l1_ig_timestamp=1502184161702&apex_l1_ig_version=1.0¶m1=data1"; + + try { + FormList formData = new FormList(); + formData.add("param1", "data1"); + + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(url); + authParam.httpMethod = "POST"; + authParam.appName = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; + authParam.nonce = "6584351262900708156"; + authParam.timestamp = "1502184161702"; + authParam.formData = formData; + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + assertEquals(expectedBaseString, authorizationToken.getBaseString()); + } + catch (ApiUtilException e) + { + e.printStackTrace(); + } + } + + @Test + public void BaseString_Invalid_Url_01_Test() + { + String url = "ftp://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + + String expectedMessage = "Support http and https protocol only."; + + try { + ApiSigning.getBaseString( + "Apex_L1_IG", + "HMACSHA256", + "example-4Swyn7qwKeO32EXdH1dKTeIQ", + url, + "post", + null, + "6584351262900708156", + "1502184161702" + ); + } + catch (ApiUtilException expected) + { + assertEquals(expectedMessage, expected.getMessage()); + } + } + + @Test + public void BaseString_New_Invalid_Url_01_Test() throws ApiUtilException + { + String url = "ftp://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + String expectedMessage = "Support http and https protocol only."; + + try { + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(url); + authParam.httpMethod = "POST"; + authParam.appName = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; + authParam.nonce = "6584351262900708156"; + authParam.timestamp = "1502184161702"; + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + } + catch (ApiUtilException e) + { + e.printStackTrace(); + assertEquals(expectedMessage, e.getMessage()); + } + } + + @Test + public void BaseString_Invalid_Url_02_Test() + { + String url = "://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + + try { + ApiSigning.getBaseString( + "Apex_L1_IG", + "HMACSHA256", + "example-4Swyn7qwKeO32EXdH1dKTeIQ", + url, + "post", + null, + "6584351262900708156", + "1502184161702" + ); + } + catch (ApiUtilException expected) + { + if (!(expected.getCause() instanceof URISyntaxException)) + { + fail("Expecting URISyntaxException error."); + } + } + } + + @Test + public void BaseString_New_Invalid_Url_02_Test() throws ApiUtilException + { + + String url = "://example.lab:443/api/v1/rest/level1/in-in/?ap=裕廊坊%20心邻坊"; + + try { + AuthParam authParam = new AuthParam(); + + authParam.url = URI.create(url); + authParam.httpMethod = "POST"; + authParam.appName = "example-4Swyn7qwKeO32EXdH1dKTeIQ"; + authParam.nonce = "6584351262900708156"; + authParam.timestamp = "1502184161702"; + + AuthToken authorizationToken = ApiSigning.getSignatureTokenV2(authParam); + } + catch (Exception e) + { + e.printStackTrace(); + if (!(e.getCause() instanceof URISyntaxException)) + { + fail("Expecting URISyntaxException error."); + } + + } + } + + + +} diff --git a/src/test/java/HMACSignatureTest.java b/src/test/java/com/api/util/ApiSecurity/HMACSignatureTest.java similarity index 99% rename from src/test/java/HMACSignatureTest.java rename to src/test/java/com/api/util/ApiSecurity/HMACSignatureTest.java index 3a5e8be..65330f9 100644 --- a/src/test/java/HMACSignatureTest.java +++ b/src/test/java/com/api/util/ApiSecurity/HMACSignatureTest.java @@ -1,3 +1,4 @@ +package com.api.util.ApiSecurity; import static org.junit.Assert.*; import java.util.ArrayList; diff --git a/src/test/java/RSASignatureTest.java b/src/test/java/com/api/util/ApiSecurity/RSASignatureTest.java similarity index 98% rename from src/test/java/RSASignatureTest.java rename to src/test/java/com/api/util/ApiSecurity/RSASignatureTest.java index 79757bc..ca5f69a 100644 --- a/src/test/java/RSASignatureTest.java +++ b/src/test/java/com/api/util/ApiSecurity/RSASignatureTest.java @@ -1,3 +1,4 @@ +package com.api.util.ApiSecurity; import static org.junit.Assert.*; import java.io.File; @@ -53,7 +54,6 @@ private static String getLocalPath(String relativeFileName) { Path currentRelativePath = Paths.get(""); String s = combine(currentRelativePath.toAbsolutePath().toString(), relativeFileName.replaceAll("/", File.separator)); - //log.error("Current relative path is: " + s); return s; } @@ -170,7 +170,6 @@ public void L2_Basic_Test() { }); for (String[] item : dataList) { - System.out.println("Test Type:" + item[2]); L2Test(item[0], item[1], item[2]); } @@ -185,8 +184,6 @@ public static void L2Test(String baseString, String expectedSignature, String me fail("Should not throw any exception during test execution"); } - System.out.println("Expected Signature :" + signature); - assertEquals(message, expectedSignature, signature); } } diff --git a/src/test/java/com/api/util/testframework/JUnitFactoryRunner.java b/src/test/java/com/api/util/testframework/JUnitFactoryRunner.java new file mode 100644 index 0000000..40ae170 --- /dev/null +++ b/src/test/java/com/api/util/testframework/JUnitFactoryRunner.java @@ -0,0 +1,93 @@ +package com.api.util.testframework; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.TestClass; + +/** + * @author GDS-PDD + * + * Custom JUnit Runner + * This is the base class to create the runtime JUnit TC. + */ +public class JUnitFactoryRunner extends BlockJUnit4ClassRunner { + + protected LinkedList tests = new LinkedList(); + /** + * Creates a customized BlockJUnit4ClassRunner to run cls. + * It throws the InitializationError if the test class is malformed. + **/ + public JUnitFactoryRunner(Class cls) throws InitializationError { + super(cls); + try { + computeTests(); + } catch (Exception e) { + } + } + /** + * To ensure the test class constructor is called at least + * once during testing. This is adding all the tests + * which will be entertained as new fixture for running a test. + * + * @throws Exception + */ + protected void computeTests() throws Exception { + tests.addAll(super.computeTestMethods()); + tests.addAll(computeFactoryTests()); + } + /** + * Find all methods in our test class marked with @JUnitTestFactory, + * and for each @JUnitTestFactory, find any methods marked with @JUnitFactoryTest, + * and add them to the List. + * + * @return + * @throws Exception + */ + protected Collection computeFactoryTests() throws Exception { + List tests = new LinkedList(); + for (FrameworkMethod method: getTestClass().getAnnotatedMethods(JUnitTestFactory.class)) { + if (! Modifier.isStatic(method.getMethod().getModifiers())) { + throw new InitializationError("Exception during initialization as method must be static."); + } + Object instances = method.getMethod().invoke(getTestClass().getJavaClass()); + if (instances.getClass().isArray()) { + instances = Arrays.asList((Object[]) instances); + } + if (! (instances instanceof Iterable)) { + instances = Collections.singletonList(instances); + } + for (Object instance: (Iterable) instances) { + RuntimeTestCase tc = (RuntimeTestCase) instance; + for (FrameworkMethod m: new TestClass(instance.getClass()).getAnnotatedMethods(JUnitFactoryTest.class)){ + if(tc.getTestName().contains(m.getMethod().getName())){ + tests.add(new JUnitFrameworkFactory(m.getMethod(), instance, method.getName())); + } + } + + } + } + return tests; + } + /**Returns the methods that run tests.**/ + @Override + protected List computeTestMethods() { + return tests; + } + @Override + protected void validateInstanceMethods(List errors) { + validatePublicVoidNoArgMethods(After.class, false, errors); + validatePublicVoidNoArgMethods(Before.class, false, errors); + validatePublicVoidNoArgMethods(BeforeClass.class, true, errors); + validateTestMethods(errors); + } +} \ No newline at end of file diff --git a/src/test/java/com/api/util/testframework/JUnitFactoryTest.java b/src/test/java/com/api/util/testframework/JUnitFactoryTest.java new file mode 100644 index 0000000..3b3ddcb --- /dev/null +++ b/src/test/java/com/api/util/testframework/JUnitFactoryTest.java @@ -0,0 +1,15 @@ +package com.api.util.testframework; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +/** + * @author GDS-PDD + * + * Here RetentionPolicy.RUNTIME annotations are to be recorded + * in the class file by the compiler and retained by the VM at + * runtime, so they may be read reflectively. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface JUnitFactoryTest {} \ No newline at end of file diff --git a/src/test/java/com/api/util/testframework/JUnitFrameworkFactory.java b/src/test/java/com/api/util/testframework/JUnitFrameworkFactory.java new file mode 100644 index 0000000..dbb5096 --- /dev/null +++ b/src/test/java/com/api/util/testframework/JUnitFrameworkFactory.java @@ -0,0 +1,38 @@ +package com.api.util.testframework; + + +import java.lang.reflect.Method; +import org.junit.runners.model.FrameworkMethod; +/** +* Represents a method on a test class to be invoked at the appropriate point in test +* execution. These methods are usually marked with an annotation (such as @Test, @Before, +* @After, @BeforeClass, @AfterClass, etc.) +*/ +public class JUnitFrameworkFactory extends FrameworkMethod { + private Object target; + private String name; + /** Returns a new FrameworkMethod for method **/ + public JUnitFrameworkFactory(Method method, Object target, String name) { + super(method); + this.target = target; + this.name = name; + } + + /** + * Returns the result of invoking this method on target with parameters params. + * Executes the test method on the supplied target (returned by the JUnitTestFactory) + * and not the instance generated by FrameworkMethod. + */ + @Override + public Object invokeExplosively(Object target, Object... params) throws Throwable { + return super.invokeExplosively(this.target, params); + } + /** + * Returns the method's name. + */ + @Override + public String getName() { + return String.format("%s=%s.%s[%s]", name, target.getClass().getSimpleName(), + getMethod().getName(), target.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/api/util/testframework/JUnitTestFactory.java b/src/test/java/com/api/util/testframework/JUnitTestFactory.java new file mode 100644 index 0000000..a0a4957 --- /dev/null +++ b/src/test/java/com/api/util/testframework/JUnitTestFactory.java @@ -0,0 +1,14 @@ +package com.api.util.testframework; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +/** +* Here RetentionPolicy.RUNTIME annotations are to be recorded +* in the class file by the compiler and retained by the VM at +* run time, so they may be read reflectively. +*/ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface JUnitTestFactory { +} diff --git a/src/test/java/com/api/util/testframework/RuntimeTestCase.java b/src/test/java/com/api/util/testframework/RuntimeTestCase.java new file mode 100644 index 0000000..05b53ee --- /dev/null +++ b/src/test/java/com/api/util/testframework/RuntimeTestCase.java @@ -0,0 +1,509 @@ +package com.api.util.testframework; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import com.api.util.ApiSecurity.ApiList; +import com.api.util.ApiSecurity.ApiSigning; +import com.api.util.ApiSecurity.ApiUtilException; +import com.api.util.ApiSecurity.AuthParam; +import com.api.util.ApiSecurity.FormList; +import com.api.util.ApiSecurity.SignatureMethod; +import com.api.util.testframework.dto.ExpectedResult; +import com.api.util.testframework.dto.FormData; +import com.api.util.testframework.dto.QueryString; +import com.api.util.testframework.dto.TestDatum; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.text.ParseException; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.*; + +public class RuntimeTestCase{ + + private static final Logger log = LogManager.getLogger(RuntimeTestCase.class); + + //private ApiList apiList; + private String testName; + private TestDatum testDatum; + + public RuntimeTestCase(String testName,TestDatum testDatum) { + this.testName = testName; + this.testDatum = testDatum; + } + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } + + /** + * This is the actual TC, which we are expecting to test. Number of same + * TCs with different input data will be placed in air. + * @throws ApiUtilException + */ + @JUnitFactoryTest + public void getSignatureBaseString() throws IOException, InterruptedException, ParseException { + log.trace("Entering teset test application."); + log.info("====================> Start :: RuntimeTestCase :: getSignatureBaseString :: testName : {} ", testName); + + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedBaseString = null; + try { + expectedBaseString = RuntimeTestUtility.getExpectedResultMap(expectedResult); + } catch (ApiUtilException e1) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: getSignatureBaseString :: testName : {} ", testName); + } + + String baseString = null; + try { + baseString = getBaseString(testDatum); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: getSignatureBaseString :: testName : {} ", testName); + } + log.info("Run :: RuntimeTestCase :: getSignatureBaseString :: expectedBaseString : {} ", expectedBaseString); + log.info("Run :: RuntimeTestCase :: getSignatureBaseString :: baseString : {} ", baseString); + assertEquals(expectedBaseString, baseString); + + log.info("End :: RuntimeTestCase :: getSignatureBaseString :: testName : {} ", testName + "<===================="); + + } + + @JUnitFactoryTest + public void verifyL1Signature() throws IOException, InterruptedException, ParseException { + log.info("==========> Start :: RuntimeTestCase :: verifyL1Signature :: testName : {} ", testName); + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedResultVerify = null; + try { + expectedResultVerify = RuntimeTestUtility.getExpectedResultMap(expectedResult); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: verifyL1Signature :: testName : {} ", testName); + } + + try { + assertEquals(Boolean.valueOf(expectedResultVerify), ApiSigning.verifyHMACSignature(testDatum.getApiParam().getSignature(), testDatum.getApiParam().getSecret(), testDatum.getMessage())); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: verifyL1Signature :: testName : {} ", testName); + } + + log.info("End :: RuntimeTestCase :: verifyL1Signature :: testName : {} ", testName + "<===================="); + } + + @JUnitFactoryTest + public void verifyL2Signature() throws IOException, InterruptedException, ParseException { + log.info("==========> Start :: RuntimeTestCase :: verifyL2Signature :: testName : {} ", testName); + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedResultVerify = null; + try { + expectedResultVerify = RuntimeTestUtility.getExpectedResultMap(expectedResult); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: verifyL2Signature :: testName : {} ", testName); + } + + try { + assertEquals(Boolean.valueOf(expectedResultVerify), ApiSigning.verifyRSASignature(testDatum.getMessage(), testDatum.getApiParam().getSignature(), getPublicKeyLocal("src/main/resources/test-suites/" + testDatum.getPublicKeyFileName()))); + } catch (Exception e) { + //no errorTest parameter was used, default errorTest is true. for verifyL2Signature use expectedResult false = errorTest + if(expectedResultVerify.equals("false")){ + log.error("ErrorTest :: RuntimeTestCase :: verifyL2Signature :: testName : {} ", testName); +// assertEquals(expectedResultVerify, failTest); + }else{ + log.error("Exception :: RuntimeTestCase :: verifyL2Signature :: testName : {} ", testName, e); + fail("Not Expecting ApiUtilException error."); + } + + } + + log.info("End :: RuntimeTestCase :: verifyL2Signature :: testName : {} ", testName + "<===================="); + } + + @JUnitFactoryTest + public void getL1Signature() throws IOException, InterruptedException, ParseException { + log.info("==========> Start :: RuntimeTestCase :: getL1Signature :: testName : {} ", testName); + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedL1Signature = null; + try { + expectedL1Signature = RuntimeTestUtility.getExpectedResultMap(expectedResult); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: getL1Signature :: testName : {} ", testName); + } + + try { + assertEquals(expectedL1Signature, ApiSigning.getHMACSignature(testDatum.getMessage(), testDatum.getApiParam().getSecret())); + } catch (Exception e) { + if(testDatum.getErrorTest()){ + log.error("ErrorTest :: RuntimeTestCase :: getL1Signature :: testName : {} ", testName); + log.error("Error message: " + e.getMessage()); + assertEquals(expectedL1Signature, e.getMessage()); + }else{ + log.error("Exception :: RuntimeTestCase :: getL1Signature :: testName : {} ", testName, e); + fail("Not Expecting ApiUtilException error."); + } + + } + + log.info("End :: RuntimeTestCase :: getL1Signature :: testName : {} ", testName + "<===================="); + } + + @JUnitFactoryTest + public void getL2Signature() throws IOException, InterruptedException, ParseException { + log.info("==========> Start :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName); + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedL2Signature = null; + try { + expectedL2Signature = RuntimeTestUtility.getExpectedResultMap(expectedResult); + log.info("expectedL2Signature: " + expectedL2Signature); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName); + } + + try { + assertEquals(expectedL2Signature, ApiSigning.getRSASignature(testDatum.getMessage(), ApiSigning.getPrivateKey("src/main/resources/test-suites/" + testDatum.getApiParam().getPrivateKeyFileName()))); + } catch (Exception e) { + if(testDatum.getErrorTest()){ + log.error("ErrorTest :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName); + log.error("Error message: " + e.getMessage()); + assertEquals(expectedL2Signature, e.getMessage()); + }else{ + log.error("Exception :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName, e); + fail("Not Expecting ApiUtilException error."); + } + + } + + log.info("End :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName + "<===================="); + } + + @JUnitFactoryTest + public void verifySupportedKeyFileType() throws IOException, InterruptedException, ParseException { + log.info("==========> Start :: RuntimeTestCase :: verifySupportedKeyFileType :: testName : {} ", testName); + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedResultVerify = null; + try { + expectedResultVerify = RuntimeTestUtility.getExpectedResultMap(expectedResult); + log.info("expectedL2Signature: " + expectedResultVerify); + } catch (ApiUtilException e) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: verifySupportedKeyFileType :: testName : {} ", testName); + } + + try { + if (testDatum.getApiParam().getPrivateKeyFileName() != "") { + PrivateKey pk = ApiSigning.getPrivateKey("src/main/resources/test-suites/" + testDatum.getApiParam().getPrivateKeyFileName(), testDatum.getApiParam().getPassPhrase(), testDatum.getApiParam().getAlias()); + assertEquals(Boolean.valueOf(expectedResultVerify), Objects.equals(ApiSigning.getRSASignature(testDatum.getMessage(), pk), testDatum.getApiParam().getSignature()) ); + }else { + PublicKey pubk = getPublicKeyLocal("src/main/resources/test-suites/" + testDatum.getPublicKeyFileName()); + assertEquals(Boolean.valueOf(expectedResultVerify), ApiSigning.verifyRSASignature(testDatum.getMessage(), testDatum.getApiParam().getSignature(), pubk)); + } + + } catch (Exception e) { + if(testDatum.getErrorTest()){ + log.error("ErrorTest :: RuntimeTestCase :: verifySupportedKeyFileType :: testName : {} ", testName); + log.error("Error message: " + e.getMessage()); + assertEquals(expectedResultVerify, e.getMessage()); + }else{ + log.error("Exception :: RuntimeTestCase :: verifySupportedKeyFileType :: testName : {} ", testName, e); + fail("Not Expecting ApiUtilException error."); + } + + } + + log.info("End :: RuntimeTestCase :: getL2Signature :: testName : {} ", testName + "<===================="); + } + + @JUnitFactoryTest + public void getSignatureToken() throws IOException, InterruptedException, ParseException { + log.info("====================> Start :: RuntimeTestCase :: getSignatureToken :: testName : {} ", testName); + + ExpectedResult expectedResult = testDatum.getExpectedResult(); + String expectedBaseString = null; + try { + expectedBaseString = RuntimeTestUtility.getExpectedResultMap(expectedResult); + } catch (ApiUtilException e1) { + fail("Not Expecting ApiUtilException error."); + log.error("Exception :: RuntimeTestCase :: getSignatureToken :: testName : {} ", testName); + } + + String signatureToken = null; + try { + signatureToken = getSignatureToken(testDatum); + log.info("Run :: RuntimeTestCase :: getSignatureToken :: expectedBaseString : {} ", expectedBaseString); + log.info("Run :: RuntimeTestCase :: getSignatureToken :: signatureToken : {} ", signatureToken); + + assertEquals(expectedBaseString, signatureToken); + } catch (Exception e) { + if(testDatum.getErrorTest()){ + log.error("ErrorTest :: RuntimeTestCase :: getSignatureToken :: testName : {} ", testName); + log.error("Error message: " + e.getMessage()); + assertEquals(expectedBaseString, e.getMessage()); + }else{ + log.error("Exception :: RuntimeTestCase :: getSignatureToken :: testName : {} ", testName, e); + fail("Not Expecting ApiUtilException error."); + } + + } + + log.info("End :: RuntimeTestCase :: getSignatureToken :: testName : {} ", testName + "<===================="); + + } + + + + protected String getBaseString(TestDatum testDatum) throws ApiUtilException{ + QueryString queryString = testDatum.getApiParam().getQueryString(); + ApiList apiList = null; + ApiList queryList = null; + String qString = ""; + if(null!=queryString){ + queryList = new ApiList(); + queryList.addAll(RuntimeTestUtility.getURLEncodedApiList(queryString.getAdditionalProperties(),true)); + } + + if(!testDatum.getApiParam().getSignatureUrl().isEmpty() && null!=queryString) { + // query start with ?, replace ? with & when url already contain queryString + if (testDatum.getApiParam().getSignatureUrl().indexOf('?') > -1) { + qString = String.format("%s%s", "&", queryList.toString(true)); + } + else { + qString = String.format("%s%s", "?", queryList.toString(true)); + } + + } + + + FormData formData = testDatum.getApiParam().getFormData(); + if(null!=formData){ + if(null==apiList){ + apiList = new ApiList(); + } + apiList.addAll(RuntimeTestUtility.getApiList(formData.getAdditionalProperties(),true)); + } + + + String authPrefix = testDatum.getApiParam().getAuthPrefix(); + String signatureMethod = null; + if(authPrefix.toLowerCase().contains("l1")){ + signatureMethod = "HMACSHA256"; + }else if(authPrefix.toLowerCase().contains("l2")){ + signatureMethod = "SHA256withRSA"; + } + String baseString = null; + try { +// baseString = ApiSigning.getBaseString( +// authPrefix, +// signatureMethod, +// testDatum.getApiParam().getAppId(), +// testDatum.getApiParam().getSignatureUrl(), +// testDatum.getApiParam().getHttpMethod(), +// apiList, +// testDatum.getApiParam().getNonce(), +// testDatum.getApiParam().getTimestamp() +// ); + FormList formList = new FormList(); + if (null!=apiList) { + formList = apiList.toFormList(); + } + baseString = ApiSigning.getBaseString( + authPrefix, + SignatureMethod.valueOf(signatureMethod), + testDatum.getApiParam().getAppId(), + URI.create(String.format("%s%s",testDatum.getApiParam().getSignatureUrl(), qString)), + testDatum.getApiParam().getHttpMethod(), + formList, + testDatum.getApiParam().getNonce(), + testDatum.getApiParam().getTimestamp(), + "1.0" + ); + +// baseString = ApiSigning.getBaseString( +// +// ); + } catch (ApiUtilException e) { + e.printStackTrace(); + throw e; + } + return baseString; + } + + + protected String getSignatureToken(TestDatum testDatum) throws ApiUtilException{ + QueryString queryString = testDatum.getApiParam().getQueryString(); + ApiList formList = null; + ApiList queryList = null; + String qString = ""; + if(null!=queryString){ + queryList = new ApiList(); + + queryList.addAll(RuntimeTestUtility.getURLEncodedApiList(queryString.getAdditionalProperties(),true)); + } + + FormData formData = testDatum.getApiParam().getFormData(); + if(null!=formData){ + if(null==formList){ + formList = new ApiList(); + } + formList.addAll(RuntimeTestUtility.getApiList(formData.getAdditionalProperties(),true)); + } + + String authPrefix = testDatum.getApiParam().getAuthPrefix(); + String signatureToken = null; + try { + + if(null==testDatum.getApiParam().getNonce() || testDatum.getApiParam().getNonce().isEmpty()){ + testDatum.getApiParam().setNonce("%s"); + } + + if(null==testDatum.getApiParam().getTimestamp() || testDatum.getApiParam().getTimestamp().isEmpty()){ + testDatum.getApiParam().setTimestamp("%s"); + } + + if(!testDatum.getApiParam().getSignatureUrl().isEmpty() && null!=queryString) { + // query start with ?, replace ? with & when url already contain queryString + if (testDatum.getApiParam().getSignatureUrl().indexOf('?') > -1) { + qString = String.format("%s%s", "&", queryList.toString(true)); + } + else { + qString = String.format("%s%s", "?", queryList.toString(true)); + } + + } + + +// signatureToken = ApiSigning.getSignatureToken( +// testDatum.getApiParam().getRealm(), +// authPrefix, +// testDatum.getApiParam().getHttpMethod(), +// testDatum.getApiParam().getSignatureUrl(), +// testDatum.getApiParam().getAppId(), +// testDatum.getApiParam().getSecret(), +// apiList, +// testDatum.getApiParam().getPassphrase(), +// null, +// "src/main/resources/test-suites/" + testDatum.getApiParam().getPrivateCertFileName(), +// testDatum.getApiParam().getNonce(), +// testDatum.getApiParam().getTimestamp() +// ); + AuthParam authParam = new AuthParam(); + + try { + authParam.url = new URI(String.format("%s%s", testDatum.getApiParam().getSignatureUrl(), qString)); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + FormList apiListformData = null; + if (formList !=null) { + apiListformData = formList.toFormList(); + } + + + + authParam.httpMethod = testDatum.getApiParam().getHttpMethod(); + authParam.appName = testDatum.getApiParam().getAppId(); + authParam.appSecret = testDatum.getApiParam().getSecret(); + authParam.nonce = testDatum.getApiParam().getNonce(); + authParam.timestamp = testDatum.getApiParam().getTimestamp(); + authParam.formData = apiListformData; + String filePathString = "src/main/resources/test-suites/" + testDatum.getApiParam().getPrivateKeyFileName(); + File f = new File(filePathString); + if(f.exists() && !f.isDirectory()) { + // do something + authParam.privateKey = ApiSigning.getPrivateKey(filePathString); + } +// try { + + +// } catch (IOException e1) { +// // TODO Auto-generated catch block +// e1.printStackTrace(); +// } catch (GeneralSecurityException e1) { +// // TODO Auto-generated catch block +// e1.printStackTrace(); +// } + signatureToken = ApiSigning.getSignatureTokenV2(authParam).getToken(); + + if(testDatum.getApiParam().getNonce().equals("%s") || testDatum.getApiParam().getTimestamp().equals("%s")){ + + try{ + Pattern p = Pattern.compile("apex_l2_eg_signature=(\\S+)"); + Matcher m = p.matcher(signatureToken); + m.find(); + String signature_value = m.group(1); + signatureToken = signatureToken.replace(signature_value, "\"%s\""); + }catch(Exception e){ + e.printStackTrace(); + throw e; + } + } + + + } catch (ApiUtilException e) { + e.printStackTrace(); + throw e; + } + return signatureToken; + } + + private static PublicKey getPublicKeyLocal(String publicCertificateFileName) throws ApiUtilException, IOException, GeneralSecurityException { + try { + if(null!=publicCertificateFileName && (publicCertificateFileName.contains(".key")||publicCertificateFileName.endsWith(".pem"))){ + return ApiSigning.getPublicKeyPEM(publicCertificateFileName); + }else{ + return ApiSigning.getPublicKeyFromX509Certificate(publicCertificateFileName); + } + + } catch (Exception e) { + throw e; + } + } + + private static PrivateKey getPrivateKeyLocal(String privateKeyFileName, String password, String alias) throws IOException, GeneralSecurityException, ApiUtilException { + try { + + if(null!=privateKeyFileName && (privateKeyFileName.contains(".key")||privateKeyFileName.contains(".pem"))){ + return ApiSigning.getPrivateKeyPEM(privateKeyFileName, password); + }else{ + //For JKS file + return ApiSigning.getPrivateKeyFromKeyStore(privateKeyFileName, password, alias); + } + } catch (ApiUtilException e) { + throw e; + } + } + + + private static PrivateKey getPrivateKeyLocal(String privateKeyFileName) throws IOException, GeneralSecurityException, ApiUtilException { + return ApiSigning.getPrivateKeyPEM(privateKeyFileName,""); + } + + public static String combine(String... paths) { + File file = new File(File.separator); + + for (int i = 0; i < paths.length; i++) { + file = new File(file, paths[i]); + } + + return file.getPath(); + } +} + diff --git a/src/test/java/com/api/util/testframework/RuntimeTestUtility.java b/src/test/java/com/api/util/testframework/RuntimeTestUtility.java new file mode 100644 index 0000000..d055505 --- /dev/null +++ b/src/test/java/com/api/util/testframework/RuntimeTestUtility.java @@ -0,0 +1,140 @@ +package com.api.util.testframework; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import com.api.util.ApiSecurity.ApiList; +import com.api.util.ApiSecurity.ApiUtilException; +import com.api.util.testframework.dto.ExpectedResult; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; +import java.util.Map.Entry; +import java.util.AbstractMap.SimpleEntry; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +public class RuntimeTestUtility { + + private static final Logger log = LogManager.getLogger(RuntimeTestUtility.class); + private static ApiList apiList; + + public static String getExpectedResultMap(ExpectedResult expectedResult) throws ApiUtilException { + try{ + JSONParser parser = new JSONParser(); + ObjectMapper mapper = new ObjectMapper(); + Object obj = parser.parse(mapper.writeValueAsString(expectedResult)); + JSONObject jsonObject = (JSONObject) obj; + String java_val = (String) jsonObject.get("java"); + String default_val = (String) jsonObject.get("default"); + if(null!=java_val && !java_val.isEmpty()){ + return java_val; + }else{ + if(null!=default_val && !default_val.isEmpty()){ + return default_val; + }else{ + throw new ApiUtilException("JSON parameter for ExpectedResult is invalid or empty"); + } + } + }catch(Exception e){ + log.error("General exception in RuntimeTestUtility",e); + throw new ApiUtilException("General exception in RuntimeTestUtility",e); + } + } + + public static ApiList getApiList(ArrayList> entryList,boolean newEntry){ + //log.debug("getApiList(Is new Entry? )" + newEntry); + if(newEntry){ + apiList = new ApiList(); + } + //log.debug("EntryList Size: " + entryList.size()); + for( Entry Map : entryList){ + String MapValue = ""; + if(Map.getValue() instanceof String){ + MapValue=(String) Map.getValue(); + apiList.add(Map.getKey(),MapValue); + }else if(Map.getValue() instanceof Integer){ + MapValue=(String) String.valueOf((Integer) Map.getValue()); + apiList.add(Map.getKey(),MapValue); + }else if(Map.getValue() instanceof Double){ + //log.debug("Instance of Integer"); + MapValue=(String) Double.toString((Double) Map.getValue()); + apiList.add(Map.getKey(),MapValue); + }else if(Map.getValue() instanceof Boolean){ + MapValue=(String) String.valueOf((Boolean) Map.getValue()); + apiList.add(Map.getKey(),MapValue); + }else if(Map.getValue() instanceof ArrayList){ + ArrayList> arrayList = new ArrayList>(); + for(Object o : (ArrayList) Map.getValue()){ + Entry entry = new SimpleEntry(Map.getKey(),o); + arrayList.add(entry); + } + getApiList(arrayList,false); + } + else{ + MapValue=""; + apiList.add(Map.getKey(),MapValue); + + } + } + return apiList; + + } + + public static ApiList getURLEncodedApiList(ArrayList> entryList,boolean newEntry){ + //log.debug("getApiList(Is new Entry? )" + newEntry); + if(newEntry){ + apiList = new ApiList(); + } + + try { + + + //log.debug("EntryList Size: " + entryList.size()); + for( Entry Map : entryList){ + String MapValue = ""; + String encodedKey = java.net.URLEncoder.encode(Map.getKey(), StandardCharsets.UTF_8.toString()); + if(Map.getValue() instanceof String){ + MapValue=(String) Map.getValue(); + apiList.add(encodedKey,java.net.URLEncoder.encode(MapValue, StandardCharsets.UTF_8.toString())); + }else if(Map.getValue() instanceof Integer){ + MapValue=(String) String.valueOf((Integer) Map.getValue()); + apiList.add(encodedKey,MapValue); + }else if(Map.getValue() instanceof Double){ + //log.debug("Instance of Integer"); + MapValue=(String) Double.toString((Double) Map.getValue()); + apiList.add(encodedKey,MapValue); + }else if(Map.getValue() instanceof Boolean){ + MapValue=(String) String.valueOf((Boolean) Map.getValue()); + apiList.add(encodedKey,MapValue); + }else if(Map.getValue() instanceof ArrayList){ + ArrayList> arrayList = new ArrayList>(); + for(Object o : (ArrayList) Map.getValue()){ + if (o instanceof String) { + Entry entry = new SimpleEntry(encodedKey,java.net.URLEncoder.encode((String) o, StandardCharsets.UTF_8.toString())); + arrayList.add(entry); + } else { + Entry entry = new SimpleEntry(encodedKey,o); + arrayList.add(entry); + } + + } + getApiList(arrayList,false); + } + else{ + MapValue=""; + apiList.add(java.net.URLEncoder.encode(encodedKey, StandardCharsets.UTF_8.toString()),MapValue); + + } + } + }catch (UnsupportedEncodingException uee) { + uee.printStackTrace(); + } + return apiList; + + } + +} diff --git a/src/test/java/com/api/util/testframework/dto/ApiParam.java b/src/test/java/com/api/util/testframework/dto/ApiParam.java new file mode 100644 index 0000000..42372f6 --- /dev/null +++ b/src/test/java/com/api/util/testframework/dto/ApiParam.java @@ -0,0 +1,573 @@ + +package com.api.util.testframework.dto; + +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + + +/** + * The Apiparam Schema + *

+ * + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "realm", + "authPrefix", + "appId", + "invokeUrl", + "signatureUrl", + "httpMethod", + "queryString", + "formData", + "privateKeyFileName", + "alias", + "passphrase", + "nonce", + "timestamp", + "secret", + "signature" +}) +public class ApiParam { + + /** + * The Realm Schema + *

+ * + * (Required) + * + */ + @JsonProperty("realm") + private String realm = ""; + /** + * The Authprefix Schema + *

+ * + * (Required) + * + */ + @JsonProperty("authPrefix") + private String authPrefix = ""; + /** + * The Appid Schema + *

+ * + * (Required) + * + */ + @JsonProperty("appId") + private String appId = ""; + /** + * The Invokeurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("invokeUrl") + private String invokeUrl = ""; + /** + * The Signatureurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signatureUrl") + private String signatureUrl = ""; + /** + * The Httpmethod Schema + *

+ * + * (Required) + * + */ + @JsonProperty("httpMethod") + private String httpMethod = ""; + /** + * The Querystring Schema + *

+ * + * (Required) + * + */ + @JsonProperty("queryString") + private QueryString queryString; + /** + * The Formdata Schema + *

+ * + * (Required) + * + */ + @JsonProperty("formData") + private FormData formData; + /** + * The Privatekeyfilename Schema + *

+ * + * (Required) + * + */ + @JsonProperty("privateKeyFileName") + private String privateKeyFileName = ""; + /** + * The Alias Schema + *

+ * + * (Required) + * + */ + @JsonProperty("alias") + private String alias = ""; + /** + * The Passphrase Schema + *

+ * + * (Required) + * + */ + @JsonProperty("passphrase") + private String passPhrase = ""; + /** + * The Nonce Schema + *

+ * + * (Required) + * + */ + @JsonProperty("nonce") + private String nonce = ""; + /** + * The Timestamp Schema + *

+ * + * (Required) + * + */ + @JsonProperty("timestamp") + private String timestamp = ""; + /** + * The Secret Schema + *

+ * + * (Required) + * + */ + @JsonProperty("secret") + private String secret = ""; + /** + * The Signature Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signature") + private String signature = ""; + @JsonIgnore + private Map additionalProperties = new HashMap(); + + /** + * The Realm Schema + *

+ * + * (Required) + * + */ + @JsonProperty("realm") + public String getRealm() { + return realm; + } + + /** + * The Realm Schema + *

+ * + * (Required) + * + */ + @JsonProperty("realm") + public void setRealm(String realm) { + this.realm = realm; + } + + /** + * The Authprefix Schema + *

+ * + * (Required) + * + */ + @JsonProperty("authPrefix") + public String getAuthPrefix() { + return authPrefix; + } + + /** + * The Authprefix Schema + *

+ * + * (Required) + * + */ + @JsonProperty("authPrefix") + public void setAuthPrefix(String authPrefix) { + this.authPrefix = authPrefix; + } + + /** + * The Appid Schema + *

+ * + * (Required) + * + */ + @JsonProperty("appId") + public String getAppId() { + return appId; + } + + /** + * The Appid Schema + *

+ * + * (Required) + * + */ + @JsonProperty("appId") + public void setAppId(String appId) { + this.appId = appId; + } + + /** + * The Invokeurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("invokeUrl") + public String getInvokeUrl() { + return invokeUrl; + } + + /** + * The Invokeurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("invokeUrl") + public void setInvokeUrl(String invokeUrl) { + this.invokeUrl = invokeUrl; + } + + /** + * The Signatureurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signatureUrl") + public String getSignatureUrl() { + return signatureUrl; + } + + /** + * The Signatureurl Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signatureUrl") + public void setSignatureUrl(String signatureUrl) { + this.signatureUrl = signatureUrl; + } + + /** + * The Httpmethod Schema + *

+ * + * (Required) + * + */ + @JsonProperty("httpMethod") + public String getHttpMethod() { + return httpMethod; + } + + /** + * The Httpmethod Schema + *

+ * + * (Required) + * + */ + @JsonProperty("httpMethod") + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + /** + * The Querystring Schema + *

+ * + * (Required) + * + */ + @JsonProperty("queryString") + public QueryString getQueryString() { + return queryString; + } + + /** + * The Querystring Schema + *

+ * + * (Required) + * + */ + @JsonProperty("queryString") + public void setQueryString(QueryString queryString) { + this.queryString = queryString; + } + + /** + * The Formdata Schema + *

+ * + * (Required) + * + */ + @JsonProperty("formData") + public FormData getFormData() { + return formData; + } + + /** + * The Formdata Schema + *

+ * + * (Required) + * + */ + @JsonProperty("formData") + public void setFormData(FormData formData) { + this.formData = formData; + } + + /** + * The Privatekeyfilename Schema + *

+ * + * (Required) + * + */ + @JsonProperty("privateKeyFileName") + public String getPrivateKeyFileName() { + return privateKeyFileName; + } + + /** + * The Privatekeyfilename Schema + *

+ * + * (Required) + * + */ + @JsonProperty("privateKeyFileName") + public void setPrivateKeyFileName(String privateKeyFileName) { + this.privateKeyFileName = privateKeyFileName; + } + + /** + * The Alias Schema + *

+ * + * (Required) + * + */ + @JsonProperty("alias") + public String getAlias() { + return alias; + } + + /** + * The Alias Schema + *

+ * + * (Required) + * + */ + @JsonProperty("alias") + public void setAlias(String alias) { + this.alias = alias; + } + + /** + * The Passphrase Schema + *

+ * + * (Required) + * + */ + @JsonProperty("passPhrase") + public String getPassPhrase() { + return passPhrase; + } + + /** + * The Passphrase Schema + *

+ * + * (Required) + * + */ + @JsonProperty("passPhrase") + public void setPassPhrase(String passPhrase) { + this.passPhrase = passPhrase; + } + + /** + * The Nonce Schema + *

+ * + * (Required) + * + */ + @JsonProperty("nonce") + public String getNonce() { + return nonce; + } + + /** + * The Nonce Schema + *

+ * + * (Required) + * + */ + @JsonProperty("nonce") + public void setNonce(String nonce) { + this.nonce = nonce; + } + + /** + * The Timestamp Schema + *

+ * + * (Required) + * + */ + @JsonProperty("timestamp") + public String getTimestamp() { + return timestamp; + } + + /** + * The Timestamp Schema + *

+ * + * (Required) + * + */ + @JsonProperty("timestamp") + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + /** + * The Secret Schema + *

+ * + * (Required) + * + */ + @JsonProperty("secret") + public String getSecret() { + return secret; + } + + /** + * The Secret Schema + *

+ * + * (Required) + * + */ + @JsonProperty("secret") + public void setSecret(String secret) { + this.secret = secret; + } + + /** + * The Signature Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signature") + public String getSignature() { + return signature; + } + + /** + * The Signature Schema + *

+ * + * (Required) + * + */ + @JsonProperty("signature") + public void setSignature(String signature) { + this.signature = signature; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("realm", realm).append("authPrefix", authPrefix).append("appId", appId).append("invokeUrl", invokeUrl).append("signatureUrl", signatureUrl).append("httpMethod", httpMethod).append("queryString", queryString).append("formData", formData).append("privateKeyFileName", privateKeyFileName).append("nonce", nonce).append("timestamp", timestamp).append("secret", secret).append("signature", signature).append("additionalProperties", additionalProperties).toString(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(privateKeyFileName).append(signature).append(signatureUrl).append(secret).append(httpMethod).append(queryString).append(nonce).append(appId).append(invokeUrl).append(realm).append(formData).append(additionalProperties).append(authPrefix).append(timestamp).toHashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof ApiParam) == false) { + return false; + } + ApiParam rhs = ((ApiParam) other); + return new EqualsBuilder().append(privateKeyFileName, rhs.privateKeyFileName).append(signature, rhs.signature).append(signatureUrl, rhs.signatureUrl).append(secret, rhs.secret).append(httpMethod, rhs.httpMethod).append(queryString, rhs.queryString).append(nonce, rhs.nonce).append(appId, rhs.appId).append(invokeUrl, rhs.invokeUrl).append(realm, rhs.realm).append(formData, rhs.formData).append(additionalProperties, rhs.additionalProperties).append(authPrefix, rhs.authPrefix).append(timestamp, rhs.timestamp).isEquals(); + } + +} diff --git a/src/test/java/com/api/util/testframework/dto/ExpectedResult.java b/src/test/java/com/api/util/testframework/dto/ExpectedResult.java new file mode 100644 index 0000000..0f21a34 --- /dev/null +++ b/src/test/java/com/api/util/testframework/dto/ExpectedResult.java @@ -0,0 +1,63 @@ + +package com.api.util.testframework.dto; + +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + + +/** + * The Expectedresult Schema + *

+ * + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + +}) +public class ExpectedResult { + + @JsonIgnore + private Map additionalProperties = new HashMap(); + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("additionalProperties", additionalProperties).toString(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(additionalProperties).toHashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof ExpectedResult) == false) { + return false; + } + ExpectedResult rhs = ((ExpectedResult) other); + return new EqualsBuilder().append(additionalProperties, rhs.additionalProperties).isEquals(); + } + +} diff --git a/src/test/java/com/api/util/testframework/dto/FormData.java b/src/test/java/com/api/util/testframework/dto/FormData.java new file mode 100644 index 0000000..6b73f3c --- /dev/null +++ b/src/test/java/com/api/util/testframework/dto/FormData.java @@ -0,0 +1,66 @@ + +package com.api.util.testframework.dto; + +import java.util.ArrayList; +import java.util.Map.Entry; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import java.util.AbstractMap.SimpleEntry; + + +/** + * The Formdata Schema + *

+ * + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + +}) +public class FormData { + + @JsonIgnore + private ArrayList> additionalProperties = new ArrayList>(); + + @JsonAnyGetter + public ArrayList> getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + Entry e = new SimpleEntry(name,value); + this.additionalProperties.add(e); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("additionalProperties", additionalProperties).toString(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(additionalProperties).toHashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof FormData) == false) { + return false; + } + FormData rhs = ((FormData) other); + return new EqualsBuilder().append(additionalProperties, rhs.additionalProperties).isEquals(); + } + +} diff --git a/src/test/java/com/api/util/testframework/dto/QueryString.java b/src/test/java/com/api/util/testframework/dto/QueryString.java new file mode 100644 index 0000000..629f232 --- /dev/null +++ b/src/test/java/com/api/util/testframework/dto/QueryString.java @@ -0,0 +1,65 @@ + +package com.api.util.testframework.dto; + +import java.util.ArrayList; +import java.util.Map.Entry; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import java.util.AbstractMap.SimpleEntry; + +/** + * The Querystring Schema + *

+ * + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + +}) +public class QueryString { + + @JsonIgnore + private ArrayList> additionalProperties = new ArrayList>(); + + @JsonAnyGetter + public ArrayList> getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + Entry e = new SimpleEntry(name,value); + this.additionalProperties.add(e); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("additionalProperties", additionalProperties).toString(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(additionalProperties).toHashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof QueryString) == false) { + return false; + } + QueryString rhs = ((QueryString) other); + return new EqualsBuilder().append(additionalProperties, rhs.additionalProperties).isEquals(); + } + +} diff --git a/src/test/java/com/api/util/testframework/dto/TestDatum.java b/src/test/java/com/api/util/testframework/dto/TestDatum.java new file mode 100644 index 0000000..f348764 --- /dev/null +++ b/src/test/java/com/api/util/testframework/dto/TestDatum.java @@ -0,0 +1,405 @@ + +package com.api.util.testframework.dto; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.api.util.testframework.RuntimeTestCase; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + + +/** + * The Items Schema + *

+ * + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "id", + "description", + "apiName", + "apiParam", + "expectedResult", + "message", + "publicKeyFileName", + "skipTest", + "passphrase", + "errorTest" +}) +public class TestDatum { + + /** + * The Id Schema + *

+ * + * (Required) + * + */ + @JsonProperty("id") + private String id = ""; + /** + * The Description Schema + *

+ * + * (Required) + * + */ + @JsonProperty("description") + private String description = ""; + /** + * The Apiname Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiName") + private String apiName = ""; + /** + * The Apiparam Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiParam") + private ApiParam apiParam = new ApiParam(); + /** + * The Expectedresult Schema + *

+ * + * (Required) + * + */ + @JsonProperty("expectedResult") + private ExpectedResult expectedResult; + /** + * The Message Schema + *

+ * + * (Required) + * + */ + @JsonProperty("message") + private String message = ""; + /** + * The PublicCertFileName Schema + *

+ * + * (Required) + * + */ + @JsonProperty("publicKeyFileName") + private String publicKeyFileName = ""; + /** + * The SkipTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("skipTest") + private List skipTest = new ArrayList(); + /** + * The Passphrase Schema + *

+ * + * + */ + @JsonProperty("passphrase") + private String passphrase = ""; + /** + * The ErrorTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("errorTest") + private Boolean errorTest = false; + @JsonIgnore + private Map additionalProperties = new HashMap(); + + /** + * The Id Schema + *

+ * + * (Required) + * + */ + @JsonProperty("id") + public String getId() { + return id; + } + + /** + * The Id Schema + *

+ * + * (Required) + * + */ + @JsonProperty("id") + public void setId(String id) { + this.id = id; + } + + /** + * The Description Schema + *

+ * + * (Required) + * + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + /** + * The Description Schema + *

+ * + * (Required) + * + */ + @JsonProperty("description") + public void setDescription(String description) { + this.description = description; + } + + /** + * The Apiname Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiName") + public String getApiName() { + return apiName; + } + + /** + * The Apiname Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiName") + public void setApiName(String apiName) { + this.apiName = apiName; + } + + /** + * The Apiparam Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiParam") + public ApiParam getApiParam() { + return apiParam; + } + + /** + * The Apiparam Schema + *

+ * + * (Required) + * + */ + @JsonProperty("apiParam") + public void setApiParam(ApiParam apiParam) { + this.apiParam = apiParam; + } + + /** + * The Expectedresult Schema + *

+ * + * (Required) + * + */ + @JsonProperty("expectedResult") + public ExpectedResult getExpectedResult() { + return expectedResult; + } + + /** + * The Expectedresult Schema + *

+ * + * (Required) + * + */ + @JsonProperty("expectedResult") + public void setExpectedResult(ExpectedResult expectedResult) { + this.expectedResult = expectedResult; + } + + /** + * The Message Schema + *

+ * + * (Required) + * + */ + @JsonProperty("message") + public String getMessage() { + return message; + } + + /** + * The Message Schema + *

+ * + * (Required) + * + */ + @JsonProperty("message") + public void setMessage(String message) { + this.message = message; + } + + /** + * The PublicKeyFileName Schema + *

+ * + * (Required) + * + */ + @JsonProperty("publicKeyFileName") + public String getPublicKeyFileName() { + return publicKeyFileName; + } + + /** + * The PublicCertFileName Schema + *

+ * + * (Required) + * + */ + @JsonProperty("publicKeyFileName") + public void setPublicKeyFileName(String publicKeyFileName) { + this.publicKeyFileName = publicKeyFileName; + } + + /** + * The SkipTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("skipTest") + public List getSkipTest() { + return skipTest; + } + + /** + * The SkipTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("skipTest") + public void setSkipTest(List skipTest) { + this.skipTest = skipTest; + } + + /** + * The Passphrase Schema + *

+ * + * + */ + @JsonProperty("passphrase") + public String getPassphrase() { + return passphrase; + } + + /** + * The Passphrase Schema + *

+ * + * + */ + @JsonProperty("passphrase") + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + + /** + * The ErrorTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("errorTest") + public Boolean getErrorTest() { + return errorTest; + } + + /** + * The ErrorTest Schema + *

+ * + * (Required) + * + */ + @JsonProperty("errorTest") + public void setErrorTest(Boolean errorTest) { + this.errorTest = errorTest; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + @Override + public String toString() { + return new ToStringBuilder(this).append("id", id).append("description", description).append("apiName", apiName).append("apiParam", apiParam).append("expectedResult", expectedResult).append("message", message).append("publicCertFileName", publicKeyFileName).append("skipTest", skipTest).append("passphrase", passphrase).append("errorTest", errorTest).append("additionalProperties", additionalProperties).toString(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder().append(skipTest).append(apiName).append(publicKeyFileName).append(expectedResult).append(description).append(passphrase).append(id).append(additionalProperties).append(message).append(apiParam).append(errorTest).toHashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if ((other instanceof TestDatum) == false) { + return false; + } + TestDatum rhs = ((TestDatum) other); + return new EqualsBuilder().append(skipTest, rhs.skipTest).append(apiName, rhs.apiName).append(publicKeyFileName, rhs.publicKeyFileName).append(expectedResult, rhs.expectedResult).append(description, rhs.description).append(passphrase, rhs.passphrase).append(id, rhs.id).append(additionalProperties, rhs.additionalProperties).append(message, rhs.message).append(apiParam, rhs.apiParam).append(errorTest, rhs.errorTest).isEquals(); + } + +}