diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..179a245 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM openjdk:8-jdk-slim + +LABEL maintainer="kevin.lee@microfocus.com" + +# Add a volume pointing to /tmp +VOLUME /tmp + +# Make port 8080 available to the world outside this container +EXPOSE 8080 + +# Location of WebInspect RuntTime Agent - comment out if not required +#ARG WI_AGENT_DIR=/installs/Fortify_WebInspect_Runtime_Agent_Java_21.3.1/ + +# The application's jar file +ARG JAR_FILE=target/iwa.jar + +# Copy Fortify WebInspect Runtime Agent directory to the container - comment out if not required +#COPY ${WI_AGENT_DIR} /wirtagent + +# Copy the application's jar to the container +COPY ${JAR_FILE} app.jar + +# JAVA_OPTS to be passed in +ENV JAVA_OPTS="-Xmx512m -Xss256k" + +# Run the jar file +# Uncomment if not using WebInspect Agent +ENTRYPOINT ["java","-jar","/app.jar"] +# Comment out if not using WebInspect Agent +#ENTRYPOINT ["java","-javaagent:/wirtagent/lib/FortifyAgent.jar","-jar","/app.jar"] + diff --git a/Dockerfile.win b/Dockerfile.win new file mode 100644 index 0000000..46ff8c7 --- /dev/null +++ b/Dockerfile.win @@ -0,0 +1,30 @@ +FROM openjdk:8-nanoserver-1809 + +LABEL maintainer="kevin.lee@microfocus.com" + +# Add a volume pointing to C:\Data +VOLUME C:\\data + +# Make port 8080 available to the world outside this container +EXPOSE 8080 + +# Location of WebInspect RuntTime Agent - comment out if not required +#ARG WI_AGENT_DIR=/installs/Fortify_WebInspect_Runtime_Agent_Java_21.3.1/ + +# The application's jar file +ARG JAR_FILE=target/iwa.jar + +# Copy Fortify WebInspect Runtime Agent directory to the container - comment out if not required +#COPY ${WI_AGENT_DIR} /wirtagent + +# Copy the application's jar to the container +COPY ${JAR_FILE} app.jar + +# JAVA_OPTS to be passed in +ENV JAVA_OPTS="-Xmx512m -Xss256k" + +# Run the jar file +# Uncomment if not using WebInspect Agent +ENTRYPOINT ["cmd","/c","java -jar C:\\app.jar"] +# Comment out if using WebInspect Agent +#ENTRYPOINT ["cmd","/c","java -javaagent:C:\\wirtagent\\lib\\FortifyAgent.jar -jar C:\\app.jar"] diff --git a/EXPLOITS.md b/EXPLOITS.md new file mode 100644 index 0000000..740d0f8 --- /dev/null +++ b/EXPLOITS.md @@ -0,0 +1,108 @@ +# fortifydemoapps + +## A01:2021-Broken Access Control +### /user/files/download/unverified - file parameter (below steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter `12345` as the OTP + 4. Click `Download Files` menu from the user home page. + 5. Enter `c:\\windows\\system.ini` and `Submit`. + 6. Browser will prompt to keep or discard the `system.ini` file which is being downloaded + 7. Let us try another method to re-run the same exploit. + 8. Open new browser tab + 9. Enter `http://localhost:8080//user/files/download/unverified?file=../../../../../../windows/system.ini` + 10. Browser will prompt to keep or discard the `system.ini` file which is being downloaded + 11. Any `*secret and sensitive files*` can be downloaded using this exploit. + +## A03:2021 Injection - SQL Injection +### /Products - search textbox ( below Steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter `12345` as the OTP + 3. Hover on the username on the right top of the screen next to Search glass icon. + 4. Note that the drop-down menu contains Home, API Explorer and Logout + 5. Click on the SHOP menu + 6. List of available products will be displayed + 5. In the search textbox, enter **'; INSERT INTO user_authorities (authority_id, user_id) VALUES ('05970e74-c82b-4e21-b100-f8184d6e3454', '32e7db01-86bc-4687-9ecb-d79b265ac14f') -- ** + 6. Click search icon + 7. A message such as `Searching for: '; INSERT INTO user_authorities (authority_id, user_id) VALUES ('05970e74-c82b-4e21-b100-f8184d6e3454', '32e7db01-86bc-4687-9ecb-d79b265ac14f') --` will be shown. + 6. Log Out from existing session + 7. Login again with above user credentials + 8. repeat steps 3. + 9. Now, the username dropdown on the right top of the screen will show: `Site Administration` and `Database Console` apart from the three menu items shown earlier + 10. user1 has become an admin and can do anything - including adding products, canceling orders, removing users etc. + +## A03:2021 Injection - Cross site scripting (Reflected) +### /products/xss - search textbox ( below Steps to validate this vulnerability) + 1. Go to Home Page + 2. Go to SHOP -> Health & Well being -> First Aid menu + 3. In the search textbox, enter **** + 4. Click search + 5. Script in the search-term gets executed and exposes the vulnerability + +## A04:2021-Insecure Design +### /user/log - val parameter (below steps to validate this vulnerability) + 1. Go to Home Page + 2. Click on SHOP + 3. In the search textbox, enter *test'* + 4. Click search + 5. An error page with full stacktrace is shown. Stacktrace shows the entire SQL command used exposing sensitive information like column and table names. + +## A05:2021 Security Misconfiguration - XML External Entity (XXE) +### /user/files/upload-xml - File Content textarea (below Steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter 12345 as the OTP + 4. Click `Upload XML Files` menu from the user home page. + 5. Upload any xml file by clicking on `Choose File` and `Submit`. + 4. Once you file uploaded successfully, you will be able to see the `file` and its `content`. + 5. Replace the content with ` ]>John&example;` + 6. click on the `Save` link below the content box. This should reflect the executed xml with last name as "Doe" + 7. Let's try another payload. + 8. Follow Steps 3-5 using different xml file. + 9. Replace content with **]>&myExternalEntity;** + 10. Now the server hosts file will be shown as the content of updated xml file. + +## A06:2021-Vulnerable and Outdated Components (Composition only) +### /user/command-shell - Command text field (below Steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter 12345 as the OTP + 4. Click `Command Shell` menu from the user home page. + 5. Enter `cmd /c echo "calc.exe" > C:\\a06-vul-java.bat & C:\\a06-vul-java.bat` in the `Windows Command To Execute` text box. + 6. **`a06-vul-java.bat` file will exist, ran successfully and Calc application is opened.** + +## A08:2021 – Software and Data Integrity Failures +### /cart/order - CVE-2020-36518 jackson-databind before 2.13.0 allows a Java StackOverflow exception and denial of service via a large depth of nested objects. + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter `12345` as the OTP + 4. Click `SHOP` + 5. Click on a product with a *Sale* tag + 6. Click on `ADD TO CART` button + 7. Click on `BUY NOW` button in the popup + 8. Click on `PROCEED TO CHECKOUT` button + 9. In the `Order Notes` field enter the following text + *[ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ]* + 10. Click on `PLACE ORDER` button + 11. Order placement will fail with *java.lang.StackOverflowError* error + +## A09:2021-Security Logging and Monitoring Failures (log forging/static) +### /products - keywords parameter (below steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter `12345` as the OTP + 4. Click `Log Entry` menu from the user home page. + 5. Enter `17` as the first line + 6. Enter `2022-08-09 02:46:04.136 INFO 2654 --- [nio-8080-exec-3] c.m.e.w.c.UserController : Order payment reversed successfully for user1` as the second line. + 7. Click `Submit`. + 8. In the web application console (cmd window), `Order payment reversed successfully for user1` would have got logged in a separate line as a valid log entry. + +## A10:2021 Server-Side Request Forgery +### /user/ssrf - url parameter (below steps to validate this vulnerability) + 1. Go to Home Page + 2. Login as `user1@localhost.com`, password as `password` + 3. Enter `12345` as the OTP + 4. Click `Load URL` menu from the user home page. + 5. Enter `file:///windows/system.ini` and `Submit`. + 6. Contents of `C:\windows\system.ini` will be displayed in the `URL Content` area. diff --git a/IWA_API.subset.postman_collection.json b/IWA_API.subset.postman_collection.json new file mode 100644 index 0000000..42c9ccf --- /dev/null +++ b/IWA_API.subset.postman_collection.json @@ -0,0 +1,3340 @@ +{ + "info": { + "_postman_id": "c7236a64-d315-4f3b-b726-49a47dcc149a", + "name": "Insecure Web App (IWA) API", + "description": "This is the REST API for Insecure Web App (IWA) Pharmacy Direct. You can select a development or production server to test the API. Most operations require authentication via a user specific JWT token. To retrieve a JWT token for a user you can use the '/authentication/sign-in' operation below and then copy the value of the 'accessToken' field. This value can then be entered when you click on the 'Authorize' button or lock icons.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "9630190" + }, + "item": [ + { + "name": "api/v3", + "item": [ + { + "name": "reviews", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find review by Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a review by UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update an review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete an review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find reviews by product and keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "UUID of the product to find reviews for." + }, + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for reviews to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Product and keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"372c0b93-c1d9-7776-18f9-5159d049516e\",\n \"code\": \"qui id\",\n \"name\": \"ex enim voluptate\",\n \"summary\": \"Duis ullamco quis aute\",\n \"description\": \"id in in\",\n \"image\": \"ullamco laboris in\",\n \"price\": -27913647.199027598,\n \"onSale\": true,\n \"salePrice\": 7322939.265867442,\n \"inStock\": false,\n \"timeToStock\": -83974172,\n \"rating\": 19487882,\n \"available\": true\n },\n {\n \"id\": \"6f7601d2-c7af-244f-2dd3-4175a1243416\",\n \"code\": \"irure consequat do eu\",\n \"name\": \"dolor\",\n \"summary\": \"Lorem esse\",\n \"description\": \"anim dolore in\",\n \"image\": \"officia enim occaecat reprehenderit cupidatat\",\n \"price\": 84333309.66977769,\n \"onSale\": true,\n \"salePrice\": -66026868.59564058,\n \"inStock\": true,\n \"timeToStock\": 75635255,\n \"rating\": 42086492,\n \"available\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "site", + "item": [ + { + "name": "Check if username is taken", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Check if a user with the specified username already exists in the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"paria\",\n \"firstName\": \"voluptate ut Ut est\",\n \"lastName\": \"cupidatat inc\",\n \"phone\": \"77494218\",\n \"username\": \"nzu00\",\n \"id\": \"459e4949-9495-0287-2a5b-8275f7fa2056\",\n \"address\": \"Duis cillum\",\n \"city\": \"exercitation culpa est\",\n \"state\": \"officia commodo est magna Lorem\",\n \"zip\": \"ut\",\n \"country\": \"sint in\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:69dab483-422a-ef21-a008-64fe6e9eabea\",\n \"name\": \"ROLE_API\"\n },\n {\n \"id\": \"urn:uuid:05e4dcc3-6ce8-593f-2169-8a2009642443\",\n \"name\": \"ROLE_API\"\n }\n ],\n \"enabled\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Check if email exists", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Check if a user with the specified email address already exists in the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Not Found", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Register a new user", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Register a new user with the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"username\": \"nostrud mollit officia non\",\n \"password\": \"commodo voluptate Duis nostrud\",\n \"firstName\": \"do laboris ullamco\",\n \"lastName\": \"ullamco in ea\",\n \"email\": \"elit tem\",\n \"phone\": \"culpa proid\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Already Exists", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Subscribe a new user", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Subscribe a new user to the newsletter" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Already Exists", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Sign in", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Sign in to the system" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Get the site status", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Get the site message of the day" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"health\": \"anim aute eu pariatu\",\n \"motd\": \"voluptate magna\"\n },\n {\n \"health\": \"occaecat Ut c\",\n \"motd\": \"culpa ut aliqua aliquip nostrud\"\n }\n]" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + } + ] + } + ], + "variable": [ + { + "key": "protocol", + "value": "https", + "type": "any", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "iwa.onfortify.com", + "type": "any", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + }, + { + "key": "baseUrl", + "value": "{{protocol}}://{{environment}}", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..09f1866 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# IWA (Insecure Web App) Java Edition + +## Overview + +_IWA (Insecure Web App) Java Edition_ is an example Java/Spring Web Application for use in **DevSecOps** scenarios and demonstrations. +It includes some examples of bad and insecure code - which can be found using static and dynamic application +security testing tools such as those provided by [Micro Focus Fortify](https://www.microfocus.com/en-us/cyberres/application-security). + +The application is intended to provide the functionality of a typical "online pharmacy", including purchasing Products (medication) +and requesting Services (prescriptions, health checks etc). It has a modern-ish HTML front end (with some JavaScript) and a Swagger based API. + +*Please note: the application should not be used in a production environment!* + +## Forking the Repository + +In order to execute example scenarios for yourself it is recommended that you "fork" a copy of this repository into +your own GitHub account. The process of "forking" is described in detail in the [GitHub documentation](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) - you can start the process by clicking on the "Fork" button at the top right. + +## Building the Application + +To build the application, execute the following from the command line: + +``` +mvn clean package +``` + +This will create a JAR file (called `iwa.jar`) in the `target` directory. + +To build a WAR file for deployment to an application server such as [Apache Tomcat](http://tomcat.apache.org/) +execute the following: + +``` +mvn -Pwar clean package +``` + +This will create a WAR file (called `iwa.war`) in the `target` directory. + +## Running the Application + +### Development (IDE/command line) + +To run (and test) locally in development mode, execute the following from the command line: + +``` +mvn spring-boot:run +``` + +### Release (Docker Image) + +The JAR file can be built into a [Docker](https://www.docker.com/) image using the provided `Dockerfile` and the +following commands: + +``` +mvn -Pjar clean package +docker build -t iwa -f Dockerfile . +``` + +or on Windows: + +``` +mvn -Pjar clean package +docker build -t iwa -f Dockerfile.win . +``` + +This image can then be executed using the following commands: + +``` +docker run -d -p 8888:8080 iwa +``` + +## Using the Application + +To use the application navigate to the URL: [http://localhost:8888](http://localhost:8888). You can carry out a number of +actions unauthenticated, but if you want to login you can do so as one of the following users: + +- **user1@localhost.com/password** +- **user2@localhost.com/password** + +There is also an administrative user: + +- **admin@localhost.com/password** + +Upon login, you will be subsequently asked for a Multi-Factor Authentication (MFA) code. This functionality +is not yet enabled and you can enter anything here, e.g. `12345`. + +### REST APIs +To run (and test) locally in development mode, Go to Home Page -> My Account -> API Explorer OR +use the following URL: [http://localhost:8888/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config](http://localhost:8888/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config) + +### API Authentication +every API endpoint is behind authenitcation and thus require to authenticate with JWT Token before pro +Go To "Site" Operations and expand on : +``` +/api/v3/site/sign-in +``` +Click "Try it Out" button, provide administrative username and password mentioned above and hit "Execute" button. + +Copy the "accessToken" value from response and paste into Swagger Authorization (padlock) icon. + +Now, go ahead and try the API methods. + +## Licensing + +This application is made available under the [GNU General Public License V3](LICENSE) diff --git a/appspec.yml b/appspec.yml new file mode 100644 index 0000000..90eb1b6 --- /dev/null +++ b/appspec.yml @@ -0,0 +1,37 @@ +version: 0.0 +os: linux +files: +- source: target/iwa.war + destination: /home/ec2-user/javaapp +- source: /scripts/requirements.txt + destination: /home/ec2-user/scripts +hooks: + AfterInstall: + - location: scripts/install_python3 + timeout: 300 + runas: root + - location: scripts/codestar_remote_access + timeout: 300 + runas: root + - location: scripts/install_java + timeout: 300 + runas: root + - location: scripts/install_tomcat + timeout: 300 + runas: root + - location: scripts/install_httpd + timeout: 300 + runas: root + ApplicationStart: + - location: scripts/start_server + timeout: 300 + runas: root + ValidateService: + - location: devops-integrations/aws/fortify_dast_scancentral.bash + timeout: 500 + runas: root + ApplicationStop: + - location: scripts/stop_server + timeout: 300 + runas: root + diff --git a/build_spec.yaml b/build_spec.yaml new file mode 100644 index 0000000..d784fe8 --- /dev/null +++ b/build_spec.yaml @@ -0,0 +1,77 @@ +version: 0.1 +component: build +timeoutInSeconds: 6000 +runAs: root +shell: bash +env: + # these are local variables to the build config + variables: + "JAVA_HOME" : "/usr/lib64/graalvm/graalvm-java17" + # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service + # you can then access the value of that secret in your build_spec.yaml commands + vaultVariables: + # Use below variables for FORTIFY ON DEMAND integration + FCLI_DEFAULT_FOD_TENANT: ocid1.vaultsecret.oc1.XXXXXXX # TENANT ID + FCLI_DEFAULT_FOD_USER: ocid1.vaultsecret.oc1.XXXXXXX # FOD USER KEY + FCLI_DEFAULT_FOD_PASSWORD: ocid1.vaultsecret.oc1.XXXXXXX # FOD PAT + FCLI_DEFAULT_FOD_URL: ocid.vaultsecret.oc1.XXXXXXX # FOD URL + FOD_RELEASE_ID: ocid1.vaultsecret.oc1.XXXXXXX # FOD APPLICATION BASED RELEASE ID + # Use below variables for FORTIFY SCANCENTRAL integration + FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN: ocid1.vaultsecret.oc1.XXXXXXX # SCANCENTRAL CLIENT AUTH TOKEN FOR HANDSHAKE + FCLI_DEFAULT_SSC_USER: ocid1.vaultsecret.oc1.XXXXXXX # SSC USERNAME + FCLI_DEFAULT_SSC_PASSWORD: ocid1.vaultsecret.XXXXXXX # SSC PASSWORD + FCLI_DEFAULT_SSC_CI_TOKEN: ocid1.vaultsecret.XXXXXXX # SSC CI TOKEN + FCLI_DEFAULT_SSC_URL: ocid1.vaultsecret.oc1.XXXXXXX # SSC URL + SSC_APP_VERSION_ID: ocid1.vaultsecret.oc1.XXXXXXX # SSC APPLICATION VERSION ID + +steps: + - type: Command + timeoutInSeconds: 600 + name: "Install Prereqs" + command: | + java -version + yum -y install graalvm-17-native-image + export PATH=$JAVA_HOME/bin:$PATH + + #yum install -y java-11-openjdk-devel + #alternatives --display java + #alternatives --set java /usr/lib/jvm/java-11-openjdk-11.0.18.0.10-1.el7_9.x86_64/bin/java + java -version + # install Maven + #yum install maven + + mvn --version + + onFailure: + - type: Command + timeoutInSeconds: 40 + command: | + echo "Handling Prereqs Failure" + echo "Successfully handled" + timeoutInSeconds: 400 + runAs: root + + - type: Command + timeoutInSeconds: 600 + name: "Fortify SAST" + command: | + + ############################################################### + # INTEGRATE FORTIFY SAST # + # # + # For FORTIFY ON DEMAND uncomment the next line # + # bash devops-integrations/oci/fortify-sast-fod.bash + # # + # For FORTIFY SCANCENTRAL uncomment the next line # + bash devops-integrations/oci/fortify_sast_scancentral.bash + # # + # # + ############################################################### + + onFailure: + - type: Command + command: | + echo "Handling Failure" + echo "Failure successfully handled" + timeoutInSeconds: 40 + runAs: root \ No newline at end of file diff --git a/buildspec.yml b/buildspec.yml new file mode 100644 index 0000000..44353c3 --- /dev/null +++ b/buildspec.yml @@ -0,0 +1,50 @@ +version: 0.2 +env: + parameter-store: + ############################################################### + # INTEGRATE FORTIFY ON DEMAND # + # FOD_RELEASE_ID_LOCAL: "/fod/releaseid" + # FCLI_DEFAULT_FOD_TENANT_LOCAL: "/fod/tenant" + # FCLI_DEFAULT_FOD_URL_LOCAL: "/fod/url" + # FCLI_DEFAULT_FOD_CLIENT_ID_LOCAL: "/fod/client_id" + # FCLI_DEFAULT_FOD_CLIENT_SECRET_LOCAL: "/fod/client_secret" + ############################################################### + # INTEGRATE FORTIFY SCANCENTRAL # + FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN: "/fortify/client_auth_token" + FCLI_DEFAULT_SSC_USER: "/fortify/ssc_user" + FCLI_DEFAULT_SSC_PASSWORD: "/fortify/ssc_password" + FCLI_DEFAULT_SSC_CI_TOKEN: "/fortify/ci_token" + FCLI_DEFAULT_SSC_URL: "/fortify/ssc_url" + SSC_APP_VERSION_ID: "/fortify/ssc_app_versionid" + SSC_IP_LOCAL: "/fortify/ssc_ip" +phases: + install: + runtime-versions: + java: corretto17 + commands: + # Upgrade AWS CLI to the latest version + #- pip install --upgrade awscli + pre_build: + commands: + - mvn clean + build: + commands: + - mvn -Pwar clean package + post_build: + commands: + ############################################################### + # INTEGRATE FORTIFY SAST # + # # + # For FORTIFY ON DEMAND uncomment the next line # + #- bash devops-integrations/aws/fortify-sast-fod.bash + #- bash devops-integrations/aws/fortify_sast_local_java_template.bash + # # + # For FORTIFY SCANCENTRAL uncomment the next line # + - bash devops-integrations/aws/fortify_sast_scancentral.bash + # # + # # + ############################################################### +artifacts: + files: + - 'devops-integrations/aws/*' + - 'target/iwa.war' diff --git a/cloudbuild.yaml b/cloudbuild.yaml new file mode 100644 index 0000000..5c5425e --- /dev/null +++ b/cloudbuild.yaml @@ -0,0 +1,98 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Google Cloud Build pipeline +# Please refer to \devops-integrations\gcp\cloudbuild_fortify_sast_fod.yaml to integrate this build with Fortify On Demand +# The following Google Cloud Build Secrets must be defined before using this job +# - $$FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $$FCLI_DEFAULT_SSC_USER +# - $$FCLI_DEFAULT_SSC_PASSWORD +# - $$FCLI_DEFAULT_SSC_CI_TOKEN +# - $$FCLI_DEFAULT_SSC_URL +# - $$SSC_APP_VERSION_ID + +steps: +- name: maven:3.9.7 + entrypoint: 'mvn' + args: ['-q', 'clean', 'package', '-DskipTests'] + +- name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', 'gcr.io/$PROJECT_ID/iwa_java:latest', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$BUILD_ID', '.'] + id: 'build-image-IWAJava' + +- name: 'fortifydocker/fortify-ci-tools:5.4.1-jdk-17' + entrypoint: bash + args: + - -c + - | + echo Setting connection with Fortify Platform + fcli ssc session login + fcli sc-sast session login + + scancentral package -bt mvn -o package.zip + fcli sc-sast scan start --publish-to=$$SSC_APP_VERSION_ID --sensor-version=$$SC_SAST_SENSOR_VERSION --package-file=package.zip --store=Id + + fcli sc-sast scan wait-for ::Id:: --interval=30s + fcli ssc issue count --appversion=$$SSC_APP_VERSION_ID + + echo Terminating connection with Fortify Platform + fcli sc-sast session logout + fcli ssc session logout + secretEnv: ['FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN', 'FCLI_DEFAULT_SSC_USER', 'FCLI_DEFAULT_SSC_PASSWORD', 'FCLI_DEFAULT_SSC_CI_TOKEN', 'FCLI_DEFAULT_SSC_URL'] + env: + - 'FORTIFY_IP=${_PUBLIC_IP}' + - 'SSC_APP_VERSION_ID=${_SSC_APP_VERSION_ID}' + - 'SC_SAST_SENSOR_VERSION=24.2' + id: 'fortify-static-scan' + waitFor: ['build-image-IWAJava'] + +- name: 'gcr.io/cloud-builders/docker' + args: ['push', 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA'] + id: 'push-image-to-container-registry' + +- name: 'gcr.io/cloud-builders/gcloud' + args: + - 'run' + - 'deploy' + - 'iwajava' + - '--image' + - 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA' + - '--region' + - 'us-central1' + - '--platform' + - 'managed' + - '--allow-unauthenticated' + id: 'deploy-to-cloud-run' + +- name: 'fortifydocker/fortify-ci-tools:5.4.1-jdk-17' + entrypoint: "bash" + args: + - "-c" + - | + echo Setting connection with Fortify Platform + fcli ssc session login + fcli sc-dast session login + + fcli sc-dast scan start --name=$$SC_DAST_SCAN_NAME --settings=$$SC_DAST_CICD_IDENTIFIER + + echo Terminating connection with Fortify Platform + fcli sc-dast session logout + fcli ssc session logout + secretEnv: ['FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN', 'FCLI_DEFAULT_SSC_USER', 'FCLI_DEFAULT_SSC_PASSWORD', 'FCLI_DEFAULT_SSC_CI_TOKEN', 'FCLI_DEFAULT_SSC_URL'] + env: + - 'FORTIFY_IP=${_PUBLIC_IP}' + - 'SC_DAST_CICD_IDENTIFIER=${_SC_DAST_CICD_IDENTIFIER}' + - 'SC_DAST_SCAN_NAME=IWA_DAST_GCP' +availableSecrets: + secretManager: + - versionName: projects/$PROJECT_ID/secrets/fcli_default_sc_sast_client_auth_token/versions/latest + env: 'FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_user/versions/latest + env: 'FCLI_DEFAULT_SSC_USER' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_password/versions/latest + env: 'FCLI_DEFAULT_SSC_PASSWORD' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_ci_token/versions/latest + env: 'FCLI_DEFAULT_SSC_CI_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_url/versions/latest + env: 'FCLI_DEFAULT_SSC_URL' +images: +- 'gcr.io/$PROJECT_ID/iwa_java:latest' +- 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA' +- 'gcr.io/$PROJECT_ID/iwa_java:$BUILD_ID' \ No newline at end of file diff --git a/devops-integrations/.circleci/config-fortify-dast-scancentral.yml b/devops-integrations/.circleci/config-fortify-dast-scancentral.yml new file mode 100644 index 0000000..afffdb2 --- /dev/null +++ b/devops-integrations/.circleci/config-fortify-dast-scancentral.yml @@ -0,0 +1,54 @@ +# Integrate Fortify ScanCentral Dynamic AppSec Testing (DAST) into your Circle CI pipeline +# Renaming this file to config.yml for leveraging the file directly otherwise copy dast job content +# The following environment variables must be defined in CircleCI organization context before using this job +# - $FCLI_DEFAULT_SSC_USER +# - $FCLI_DEFAULT_SSC_PASSWORD +# - $FCLI_DEFAULT_SSC_CI_TOKEN +# - $FCLI_DEFAULT_SSC_URL +# - $SC_DAST_CICD_IDENTIFIER +version: '2.1' +jobs: + deploy: + docker: + - image: 'tuffacton/jfrog-cli:latest' + steps: + - checkout + - run: + command: | + echo Deploying artifacts + jf config add --url=$ARTIFACTORY_URL --user=$ARTIFACTORY_USER --password=$ARTIFACTORY_API_KEY --interactive=false + jf rt u "(*).jar" example-repo-local/circleci/ --recursive=false + + dast: + environment: + SC_DAST_SCAN_NAME: "Circle_CI_Scan" + SC_DAST_CICD_IDENTIFIER: "<>" + working_directory: ~/circleci-iwajava-scancentral + docker: + - image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + + steps: + - checkout + + - run: + command: | + echo Setting connection with Fortify Platform + #Use --insecure switch when SSL certificates are self-generated + fcli ssc session login + fcli sc-dast session login + + fcli sc-dast scan start --name=$SC_DAST_SCAN_NAME --settings=$SC_DAST_CICD_IDENTIFIER + + echo Terminating connection with Fortify Platform + fcli sc-dast session logout + fcli ssc session logout + +# Orchestrate job run sequence +workflows: + CI_and_CD: + jobs: + - deploy: + - dast: + context: ScanCentral + requires: + - deploy \ No newline at end of file diff --git a/devops-integrations/.circleci/config-fortify-sast-fod.yml b/devops-integrations/.circleci/config-fortify-sast-fod.yml new file mode 100644 index 0000000..75371fa --- /dev/null +++ b/devops-integrations/.circleci/config-fortify-sast-fod.yml @@ -0,0 +1,67 @@ +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your Circle CI pipeline +# Renaming this file to config.yml for leveraging the file directly otherwise copy scan job content +# The following environment variables must be defined in CircleCI context before using this job +# - $FCLI_DEFAULT_FOD_TENANT +# - $FCLI_DEFAULT_FOD_CLIENT_ID +# - $FCLI_DEFAULT_FOD_CLIENT_SECRET +# - $FCLI_DEFAULT_FOD_URL +version: '2.1' +jobs: + build: + working_directory: ~/circleci-iwajava + docker: + - image: maven:3.8.7-openjdk-18 + + steps: + - checkout + + - restore_cache: + key: circleci-iwajava-{{ checksum "pom.xml" }} + + - run: mvn package + + - save_cache: + paths: + - ~/.m2 + key: circleci-iwajava-{{ checksum "pom.xml" }} + + - store_test_results: + path: target/surefire-reports + + - store_artifacts: + path: target/iwa.jar + + scan: + environment: + FOD_NOTES: "Triggered by CircleCI Pipeline" + FOD_RELEASE_ID: + working_directory: ~/circleci-iwajava + docker: + - image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + + steps: + - checkout + + - run: + command: | + echo Setting connection with Fortify Platform + #Use --insecure switch if the SSL certificate is self generated. + fcli fod session login + + scancentral package -bt mvn -oss -o package.zip + fcli fod sast start --release=$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes=$FOD_NOTES --store=Id + + fcli fod sast wait-for ::Id:: --interval=30s + fcli fod issue list --release=$FOD_RELEASE_ID + + fcli fod session logout + +# Orchestrate job run sequence +workflows: + build_and_scan: + jobs: + - build + - scan: + context: FOD + requires: + - build \ No newline at end of file diff --git a/devops-integrations/.circleci/config-fortify-sast-scancentral.yml b/devops-integrations/.circleci/config-fortify-sast-scancentral.yml new file mode 100644 index 0000000..2b6e794 --- /dev/null +++ b/devops-integrations/.circleci/config-fortify-sast-scancentral.yml @@ -0,0 +1,72 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Circle CI pipeline +# Rename this file to config.yml for leveraging the file directly otherwise copy sast job content +# The following environment variables must be defined in CircleCI organization context before using this job +# - $FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $FCLI_DEFAULT_SSC_USER +# - $FCLI_DEFAULT_SSC_PASSWORD +# - $FCLI_DEFAULT_SSC_CI_TOKEN +# - $FCLI_DEFAULT_SSC_URL +# - $SSC_APP_VERSION_ID +version: '2.1' +jobs: + build: + working_directory: ~/circleci-iwajava-scancentral + docker: + - image: maven:3.8.7-openjdk-18 + + steps: + - checkout + + - restore_cache: + key: circleci-iwajava-sc-{{ checksum "pom.xml" }} + + - run: mvn package + + - save_cache: + paths: + - ~/.m2 + key: circleci-iwajava-sc-{{ checksum "pom.xml" }} + + - store_test_results: + path: target/surefire-reports + + - store_artifacts: + path: target/iwa.jar + + sast: + environment: + SSC_APP_VERSION_ID: "<<$$$$>>" + SC_SAST_SENSOR_VERSION: "24.2" + working_directory: ~/circleci-iwajava-scancentral + docker: + - image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + + steps: + - checkout + + - run: + command: | + echo Setting connection with Fortify Platform + #Use --insecure switch if the SSL certificate is self generated. + fcli ssc session login + fcli sc-sast session login + + scancentral package -bt mvn -o package.zip + fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SC_SAST_SENSOR_VERSION --package-file=package.zip --store=Id + + fcli sc-sast scan wait-for ::Id:: --interval=30s + fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + + echo Terminating connection with Fortify Platform + fcli sc-sast session logout + fcli ssc session logout + +# Orchestrate job run sequence +workflows: + build_and_scan: + jobs: + - build + - sast: + context: ScanCentral + requires: + - build \ No newline at end of file diff --git a/devops-integrations/.teamcity/fortify-sast-fod-settings.kts b/devops-integrations/.teamcity/fortify-sast-fod-settings.kts new file mode 100644 index 0000000..a5a9d8b --- /dev/null +++ b/devops-integrations/.teamcity/fortify-sast-fod-settings.kts @@ -0,0 +1,75 @@ +import jetbrains.buildServer.configs.kotlin.* +import jetbrains.buildServer.configs.kotlin.buildFeatures.perfmon +import jetbrains.buildServer.configs.kotlin.buildSteps.dockerCommand +import jetbrains.buildServer.configs.kotlin.buildSteps.maven +import jetbrains.buildServer.configs.kotlin.buildSteps.script +import jetbrains.buildServer.configs.kotlin.triggers.vcs + +/* +The settings script is an entry point for defining a single +TeamCity project. TeamCity looks for the 'settings.kts' file in a +project directory and runs it if it's found, so the script name +shouldn't be changed and its package should be the same as the +project's external id. + +Integrate Fortify on Demand Static AppSec Testing (SAST) into your TeamCity build pipeline + Rename this file to "settings.kts" before use + The following environment variables must be defined in Project/Agent settings before using this job + - env.FOD_RELEASE_ID + - env.FOD_USER + - env.FOD_PAT + - env.FOD_TENANT +*/ + +version = "2022.10" + +project { + + buildType(Build) +} + +object Build : BuildType({ + name = "Build" + + vcs { + root(DslContext.settingsRoot) + } + + steps { + maven { + goals = "clean package" + runnerArgs = "-Dmaven.test.failure.ignore=true" + } + dockerCommand { + commandType = build { + source = file { + path = "Dockerfile" + } + } + } + script { + name = "Fortify Scan" + scriptContent = """ + export FOD_API_URL=https://api.ams.fortify.com + export FOD_URL=https://ams.fortify.com + export FOD_UPLOADER_OPTS='-ep 2 -pp 0 -I 1 -apf' + export FOD_NOTES='Triggered by TeamCity Build Pipeline' + + scancentral package -bt mvn -oss -o package.zip + + FoDUpload -z package.zip -aurl ${'$'}FOD_API_URL -purl ${'$'}FOD_URL -rid %env.FOD_RELEASE_ID% -tc %env.FOD_TENANT% -uc %env.FOD_USER% %env.FOD_PAT% ${'$'}FOD_UPLOADER_OPTS -n "${'$'}FOD_NOTES" + """.trimIndent() + dockerImage = "fortifydocker/fortify-ci-tools:latest-jdk-11" + } + } + + triggers { + vcs { + } + } + + features { + perfmon { + } + } +}) diff --git a/devops-integrations/.teamcity/fortify_sast_scancentral-settings.kts b/devops-integrations/.teamcity/fortify_sast_scancentral-settings.kts new file mode 100644 index 0000000..f8e4727 --- /dev/null +++ b/devops-integrations/.teamcity/fortify_sast_scancentral-settings.kts @@ -0,0 +1,80 @@ +package _Self.buildTypes + +import jetbrains.buildServer.configs.kotlin.* +import jetbrains.buildServer.configs.kotlin.buildSteps.dockerCommand +import jetbrains.buildServer.configs.kotlin.buildSteps.maven +import jetbrains.buildServer.configs.kotlin.buildSteps.script +import jetbrains.buildServer.configs.kotlin.triggers.vcs + +/* +The settings script is an entry point for defining a single +TeamCity project. TeamCity looks for the 'settings.kts' file in a +project directory and runs it if it's found, so the script name +shouldn't be changed and its package should be the same as the +project's external id. + +Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your TeamCity build pipeline. + Rename this file to "settings.kts" before use + The following environment variables must be defined in Project/Agent settings before using this job + - env._FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN + - env._FCLI_DEFAULT_SSC_CI_TOKEN + - env._FCLI_DEFAULT_SSC_URL + - env._FCLI_DEFAULT_SSC_USER + - env._FCLI_DEFAULT_SSC_PASSWORD + - env.SSC_AV_ID +*/ + +object DevBuild : BuildType({ + name = "dev_build" + + vcs { + root(HttpsGitlabComMforgIwaJavaTravisGit) + } + + steps { + maven { + name = "build" + goals = "clean package" + } + dockerCommand { + commandType = build { + source = file { + path = "Dockerfile" + } + } + } + script { + name = "Fortify Scan" + scriptContent = """ + export FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN=%env._FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN% # SCANCENTRAL CLIENT AUTH TOKEN + export FCLI_DEFAULT_SSC_USER=%env._FCLI_DEFAULT_SSC_USER% # SSC USER NAME + export FCLI_DEFAULT_SSC_PASSWORD=%env._FCLI_DEFAULT_SSC_PASSWORD% # SSC PASSWORD + export FCLI_DEFAULT_SSC_CI_TOKEN=%env._FCLI_DEFAULT_SSC_CI_TOKEN% # SSC CI TOKEN + export FCLI_DEFAULT_SSC_URL=%env._FCLI_DEFAULT_SSC_URL% # SSC URL + export SC_SAST_SENSOR_VERSION='22.2.0' + + # USE --INSECURE WHEN YOUR SSL CERTIFICATES ARE SELF GENERATED/UNTRUSTED + fcli ssc session login + fcli sc-sast session login + + scancentral package -bt mvn -o package.zip + + fcli sc-sast scan start --package-file=package.zip --upload --sensor-version=${'$'}SC_SAST_SENSOR_VERSION --appversion=%env.SSC_AV_ID% --store '?' + + fcli sc-sast scan wait-for '?' --interval=30s + + fcli ssc appversion-vuln count --appversion=%env.SSC_AV_ID% + + fcli sc-sast session logout + fcli ssc session logout + """.trimIndent() + dockerImage = "fortifydocker/fortify-ci-tools:latest-jdk-11" + } + } + + triggers { + vcs { + branchFilter = "" + } + } +}) diff --git a/devops-integrations/.travisci/fortify-sast-fod.sh b/devops-integrations/.travisci/fortify-sast-fod.sh new file mode 100644 index 0000000..97c01a3 --- /dev/null +++ b/devops-integrations/.travisci/fortify-sast-fod.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your Travis CI pipeline + +# *** Configuration *** + +# The following environment variables must be defined in Repository settings +export FCLI_DEFAULT_FOD_URL=$FCLI_DEFAULT_FOD_URL_LOCAL +export FCLI_DEFAULT_FOD_TENANT=$FCLI_DEFAULT_FOD_TENANT_LOCAL +export FCLI_DEFAULT_FOD_CLIENT_ID=$FCLI_DEFAULT_FOD_CLIENT_ID_LOCAL +export FCLI_DEFAULT_FOD_CLIENT_SECRET=$FCLI_DEFAULT_FOD_CLIENT_SECRET_LOCAL +FOD_RELEASE_ID=$FOD_RELEASE_ID_LOCAL # FOD APPLICATION BASED RELEASE ID + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +FODUPLOAD_VERSION=5.4.1 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/opt/fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +FODUPLOAD_HOME=$FORTIFY_TOOLS_DIR/FodUpload +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral +fod_notes="Triggered by Travis CI" + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** + +echo "Installing FCLI" +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +export PATH=$FCLI_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + +fcli tool definitions update +fcli tool fod-uploader install -v ${FODUPLOAD_VERSION} -d ${FODUPLOAD_HOME} +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + +echo Setting connection with Fortify Platform +#Use --insecure switch if the SSL certificate is self generated. +fcli fod session login + +echo "Scan starting.." +scancentral package -bt mvn -oss -o package.zip +fcli fod sast start --release=$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes=$FOD_NOTES --store=Id + +fcli fod sast wait-for ::Id:: --interval=30s +fcli fod issue list --release=$FOD_RELEASE_ID + +fcli fod session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/.travisci/fortify_sast_scancentral.sh b/devops-integrations/.travisci/fortify_sast_scancentral.sh new file mode 100644 index 0000000..c2a4f10 --- /dev/null +++ b/devops-integrations/.travisci/fortify_sast_scancentral.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Travis CI pipeline + +# *** Configuration *** +# The following environment variables must be defined in Repository settings +export FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN=$FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN # SCANCENTRAL CLIENT AUTH TOKEN +export FCLI_DEFAULT_SSC_USER=$FCLI_DEFAULT_SSC_USER # SSC USER NAME +export FCLI_DEFAULT_SSC_PASSWORD=$FCLI_DEFAULT_SSC_PASSWORD # SSC PASSWORD +export FCLI_DEFAULT_SSC_CI_TOKEN=$FCLI_DEFAULT_SSC_CI_TOKEN # SSC CI TOKEN +export FCLI_DEFAULT_SSC_URL=$FCLI_DEFAULT_SSC_URL # SSC URL +ssc_app_version_id=$SSC_APP_VERSION_ID # SSC APPLICATION VERSION ID +ssc_ip=$SSC_IP_LOCAL + + +# Local variables +FCLI_VERSION=v2.4.0 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/opt/fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** + +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +# Use when domain name not available in the public registry +#echo "setting domain..." +#echo ${ssc_ip} + " fortify.cyberxdemo.com" | tee -a /etc/hosts +#cat /etc/hosts + +#mkdir -p $fcli_home/bin +#tar -xvzf "$fcli_install" -C $fcli_home/bin +export PATH=$FCLI_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + + +fcli tool definitions update +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + +echo Setting connection with Fortify Platform +#Use --insecure switch if the SSL certificate is self generated. +fcli ssc session login +fcli sc-sast session login + +scancentral package -bt mvn -o package.zip + +fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SCANCENTRAL_VERSION --package-file=package.zip --store=Id + +fcli sc-sast scan wait-for ::Id:: --interval=30s +fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + +echo Terminating connection with Fortify Platform +fcli sc-sast session logout +fcli ssc session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/aws/fortify-sast-fod.bash b/devops-integrations/aws/fortify-sast-fod.bash new file mode 100644 index 0000000..f8a03a4 --- /dev/null +++ b/devops-integrations/aws/fortify-sast-fod.bash @@ -0,0 +1,81 @@ +#!/bin/bash +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your AWS Codebuild pipeline + +# The following environment variables must be defined +export FCLI_DEFAULT_FOD_URL=$FCLI_DEFAULT_FOD_URL_LOCAL +export FCLI_DEFAULT_FOD_TENANT=$FCLI_DEFAULT_FOD_TENANT_LOCAL +export FCLI_DEFAULT_FOD_CLIENT_ID=$FCLI_DEFAULT_FOD_CLIENT_ID_LOCAL +export FCLI_DEFAULT_FOD_CLIENT_SECRET=$FCLI_DEFAULT_FOD_CLIENT_SECRET_LOCAL +FOD_RELEASE_ID=$FOD_RELEASE_ID_LOCAL # FOD APPLICATION BASED RELEASE ID + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +FODUPLOAD_VERSION=5.4.1 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/opt/fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +FODUPLOAD_HOME=$FORTIFY_TOOLS_DIR/FodUpload +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral +fod_notes="Triggered by AWS CodeBuild" + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** +echo "Installing FCLI" +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +export PATH=$FCLI_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + +fcli tool definitions update +fcli tool fod-uploader install -v ${FODUPLOAD_VERSION} -d ${FODUPLOAD_HOME} +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + +echo Setting connection with Fortify Platform +#Use --insecure switch if the SSL certificate is self generated. +fcli fod session login + +echo "Scan starting.." +scancentral package -bt mvn -oss -o package.zip +fcli fod sast start --release=$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes=$FOD_NOTES --store=Id + +fcli fod sast wait-for ::Id:: --interval=30s +fcli fod issue list --release=$FOD_RELEASE_ID + +fcli fod session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/aws/fortify_dast_scancentral.bash b/devops-integrations/aws/fortify_dast_scancentral.bash new file mode 100644 index 0000000..282fa83 --- /dev/null +++ b/devops-integrations/aws/fortify_dast_scancentral.bash @@ -0,0 +1,86 @@ +#!/bin/bash +# Integrate Fortify ScanCentral Dynamic AppSec Testing (DAST) into your AWS Codestar pipeline +# The following environment variables must be defined in AWS Parameter Store before using this script +# - /fortify/ssc_user +# - /fortify/ssc_password +# - /fortify/ci_token +# - /fortify/ssc_url +# - $SC_DAST_CICD_IDENTIFIER +FCLI_DEFAULT_SSC_USER=$(aws ssm get-parameters --region us-east-1 --names /fortify/ssc_user --query Parameters[0].Value) +FCLI_DEFAULT_SSC_PASSWORD=$(aws ssm get-parameters --region us-east-1 --names /fortify/ssc_password --query Parameters[0].Value) +FCLI_DEFAULT_SSC_CI_TOKEN=$(aws ssm get-parameters --region us-east-1 --names /fortify/ci_token --query Parameters[0].Value) +FCLI_DEFAULT_SSC_URL=$(aws ssm get-parameters --region us-east-1 --names /fortify/ssc_url --query Parameters[0].Value) + +export FCLI_DEFAULT_SSC_USER=$FCLI_DEFAULT_SSC_USER +export FCLI_DEFAULT_SSC_PASSWORD=$FCLI_DEFAULT_SSC_PASSWORD +export FCLI_DEFAULT_SSC_CI_TOKEN=$FCLI_DEFAULT_SSC_CI_TOKEN +export FCLI_DEFAULT_SSC_URL=$FCLI_DEFAULT_SSC_URL + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/opt/fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral +SC_DAST_CICD_IDENTIFIER='<<15xxxxx-2xxx-4xxx-xxxx-77xxxxxxx814>>' +SC_DAST_SCAN_NAME='AWS_SCAN' + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +# Use when domain name not available in the public registry +#echo "setting domain..." +#echo ${ssc_ip} + " fortify.cyberxdemo.com" | tee -a /etc/hosts +#cat /etc/hosts + +export PATH=$fcli_home/bin:${PATH} + +echo Setting connection with Fortify Platform +# USE --INSECURE WHEN YOUR SSL CERTIFICATES ARE SELF GENERATED/UNTRUSTED +fcli ssc session login +fcli sc-dast session login + +fcli sc-dast scan start $SC_DAST_SCAN_NAME --settings $SC_DAST_CICD_IDENTIFIER + +echo Terminating connection with Fortify Platform +fcli sc-dast session logout +fcli ssc session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/aws/fortify_sast_scancentral.bash b/devops-integrations/aws/fortify_sast_scancentral.bash new file mode 100644 index 0000000..daa69eb --- /dev/null +++ b/devops-integrations/aws/fortify_sast_scancentral.bash @@ -0,0 +1,91 @@ +#!/bin/bash +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your AWS Codestar pipeline + +# *** Configuration *** + +# The following environment variables must be defined +export FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN=$FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +export FCLI_DEFAULT_SSC_USER=$FCLI_DEFAULT_SSC_USER +export FCLI_DEFAULT_SSC_PASSWORD=$FCLI_DEFAULT_SSC_PASSWORD +export FCLI_DEFAULT_SSC_CI_TOKEN=$FCLI_DEFAULT_SSC_CI_TOKEN +export FCLI_DEFAULT_SSC_URL=$FCLI_DEFAULT_SSC_URL +ssc_app_version_id=$SSC_APP_VERSION_ID +ssc_ip=$SSC_IP_LOCAL + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/opt/fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +# Use when domain name not available in the public registry +#echo "setting domain..." +#echo ${ssc_ip} + " fortify.cyberxdemo.com" | tee -a /etc/hosts +#cat /etc/hosts + +#mkdir -p $fcli_home/bin +#tar -xvzf "$fcli_install" -C $fcli_home/bin +export PATH=$FCLI_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + + +fcli tool definitions update +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + +echo Setting connection with Fortify Platform +#Use --insecure switch if the SSL certificate is self generated. +fcli ssc session login +fcli sc-sast session login + +scancentral package -bt mvn -o package.zip + +fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SCANCENTRAL_VERSION --package-file=package.zip --store=Id + +fcli sc-sast scan wait-for ::Id:: --interval=30s +fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + +echo Terminating connection with Fortify Platform +fcli sc-sast session logout +fcli ssc session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/azure/azure-pipelines-fortify-dast-scancentral.yml b/devops-integrations/azure/azure-pipelines-fortify-dast-scancentral.yml new file mode 100644 index 0000000..dd07355 --- /dev/null +++ b/devops-integrations/azure/azure-pipelines-fortify-dast-scancentral.yml @@ -0,0 +1,69 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (DAST) into your Azure DevOps pipeline +# This pipeline deploys to Azure App Service which requires below to be setup: +# - azureSubscription connection with Azure Pipeline +# - WebApp needs to be created with Azure Subscription +# The following pipeline variables must be defined before using SAST stage +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_CI_TOKEN +# - $_SC_DAST_CICD_IDENTIFIER + +trigger: +- none +stages: +- stage: Build + jobs: + - job: Deploy + displayName: Deploy to Azure App Service + pool: + vmImage: Ubuntu-latest + steps: + - task: DownloadBuildArtifacts@0 + inputs: + buildType: 'current' + downloadType: 'specific' + downloadPath: '$(System.ArtifactsDirectory)' + + - task: AzureRmWebAppDeployment@4 + displayName: 'Deploy War to Azure App Service' + enabled: false + env: + AZURE_SUBSCRIPTION_ID: $(_AZURE_SUBSCRIPTION_ID) + inputs: + ConnectionType: 'AzureRM' + azureSubscription: 'XXXXXXX' + appType: 'webAppLinux' + WebAppName: 'IWAJava' + packageForLinux: '$(System.ArtifactsDirectory)/**/iwa.war' + RuntimeStack: 'TOMCAT|9.0-java11' + - job: DAST + displayName: Fortify DAST + dependsOn: + - Deploy + pool: + vmImage: 'ubuntu-latest' + container: + image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + env: + FCLI_DEFAULT_SSC_USER: $(_FCLI_DEFAULT_SSC_USER) + FCLI_DEFAULT_SSC_PASSWORD: $(_FCLI_DEFAULT_SSC_PASSWORD) + FCLI_DEFAULT_SSC_URL: $(_FCLI_DEFAULT_SSC_URL) + SC_DAST_CICD_IDENTIFIER: $(_SC_DAST_CICD_IDENTIFIER) + SC_DAST_SCAN_NAME: IWA_DAST_AZURE + steps: + - script: | + echo Setting connection with Fortify Platform + + #Use --insecure switch when SSL certificates are self-generated + fcli ssc session login + fcli sc-dast session login + + fcli sc-dast scan start --name=$SC_DAST_SCAN_NAME --settings=$SC_DAST_CICD_IDENTIFIER + + echo Terminating connection with Fortify Platform + fcli sc-dast session logout + fcli ssc session logout + displayName: Scan Central Scan + enabled: true + continueOnError: false \ No newline at end of file diff --git a/devops-integrations/azure/azure-pipelines-fortify-sast-fod.yml b/devops-integrations/azure/azure-pipelines-fortify-sast-fod.yml new file mode 100644 index 0000000..0279969 --- /dev/null +++ b/devops-integrations/azure/azure-pipelines-fortify-sast-fod.yml @@ -0,0 +1,39 @@ +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your Azure DevOps pipeline +# The following service connection must be establish before using this job +# - FoD_AMS +# +# The following task parameter must be defined +# - ReleaseId + +trigger: +- main + +pool: + vmImage: ubuntu-latest + +steps: +- task: Maven@3 + inputs: + mavenPomFile: 'pom.xml' + publishJUnitResults: true + testResultsFiles: '**/surefire-reports/TEST-*.xml' + javaHomeOption: 'JDKVersion' + jdkVersionOption: '1.11' + mavenVersionOption: 'Default' + mavenOptions: '-Xmx3072m' + mavenAuthenticateFeed: false + effectivePomSkip: false + sonarQubeRunAnalysis: false +- task: FortifyOnDemandStatic@8 + inputs: + FortifyProjects: '$(Build.Repository.LocalPath)' + FodConnection: 'FoD_AMS' # create Azure DevOps Service connection with name FoD_AMS + ReleaseOptions: '0' + ReleaseId: 00000 # update FoD RELEASE ID + EntitlementSelection: '1' + EntitlementPreference: '2' + OverrideScanSettings: '2' + InProgressScanActionType: '0' + RemediationScanPreference: '2' + BuildType: 'mvn' + PolicyFailAction: '0' diff --git a/devops-integrations/azure/azure-pipelines-fortify-sast-scancentral.yml b/devops-integrations/azure/azure-pipelines-fortify-sast-scancentral.yml new file mode 100644 index 0000000..5ede654 --- /dev/null +++ b/devops-integrations/azure/azure-pipelines-fortify-sast-scancentral.yml @@ -0,0 +1,65 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Azure DevOps pipeline +# The following pipeline variables must be defined before using SAST stage +# - $_FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $_FCLI_DEFAULT_SSC_CI_TOKEN +# - $_FCLI_DEFAULT_SSC_URL +# - $_SSC_APP_VERSION_ID + +trigger: +- none +stages: +- stage: Build + jobs: + - job: Build + displayName: Building IWA Project + pool: + vmImage: ubuntu-latest + steps: + - task: Maven@3 + inputs: + mavenPomFile: 'pom.xml' + mavenOptions: '-Xmx3072m' + javaHomeOption: 'JDKVersion' + jdkVersionOption: '1.17' + jdkArchitectureOption: 'x64' + publishJUnitResults: true + testResultsFiles: '**/surefire-reports/TEST-*.xml' + goals: 'package' + - job: SAST + displayName: Fortify SAST + dependsOn: + - Build + pool: + vmImage: 'ubuntu-latest' + container: + image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + options: "--add-host=<>:x.x.x.x" + env: + FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN: $(_FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN) + FCLI_DEFAULT_SSC_USER: $(_FCLI_DEFAULT_SSC_USER) + FCLI_DEFAULT_SSC_PASSWORD: $(_FCLI_DEFAULT_SSC_PASSWORD) + FCLI_DEFAULT_SSC_CI_TOKEN: $(_FCLI_DEFAULT_SSC_CI_TOKEN) + FCLI_DEFAULT_SSC_URL: $(_FCLI_DEFAULT_SSC_URL) + SSC_APP_VERSION_ID: $(_SSC_APP_VERSION_ID) + SC_SAST_SENSOR_VERSION: 24.2 + steps: + - script: | + echo Setting connection with Fortify Platform + echo $FORTIFY_SSC_IP fortify.cyberxdemo.com >> /etc/hosts + #Use --insecure switch if the SSL certificate is self generated. + fcli ssc session login + fcli sc-sast session login + + scancentral package -bt mvn -o package.zip + fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SC_SAST_SENSOR_VERSION --package-file=package.zip --store=Id + + fcli sc-sast scan wait-for ::Id:: --interval=30s + fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + + echo Terminating connection with Fortify Platform + fcli sc-sast session logout + fcli ssc session logout + displayName: Scan Central Scan + continueOnError: false diff --git a/devops-integrations/bitbucket/bitbucket-pipelines.yml b/devops-integrations/bitbucket/bitbucket-pipelines.yml new file mode 100644 index 0000000..37204c4 --- /dev/null +++ b/devops-integrations/bitbucket/bitbucket-pipelines.yml @@ -0,0 +1,36 @@ +# Template maven-build + +# This template allows you to test and build IWA Java app. +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your TeamCity build pipeline +# The following environment variables must be defined in Project/Agent settings before using this job +# - $FOD_RELEASE_ID +# - $FOD_USER +# - $FOD_PAT +# - $FOD_TENANT + +image: maven:3.9.7 + +pipelines: + default: + - parallel: + - step: + name: Build and Test + caches: + - maven + script: + - mvn -B verify --file pom.xml + after-script: + # Collect checkstyle results, if any, and convert to Bitbucket Code Insights. + - pipe: atlassian/checkstyle-report:0.3.0 + - step: + name: Fortify On Demand Scan + script: + - pipe: fortifysoftware/fortify-scan:5.4.1-jdk-17 + variables: + PACKAGE_OPTS: -bt mvn + FOD_URL: https://ams.fortify.com + FOD_TENANT: $FOD_TENANT + FOD_USER: $FOD_USER + FOD_PAT: $FOD_PAT + FOD_RELEASE_ID: $FOD_RELEASE_ID + FOD_UPLOAD_OPTS: -ep 2 -pp 0 \ No newline at end of file diff --git a/devops-integrations/gcp/cloudbuild_fortify_dast_scancentral.yaml b/devops-integrations/gcp/cloudbuild_fortify_dast_scancentral.yaml new file mode 100644 index 0000000..880e27e --- /dev/null +++ b/devops-integrations/gcp/cloudbuild_fortify_dast_scancentral.yaml @@ -0,0 +1,64 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Google Cloud Build pipeline +# The following Google Cloud Build Secrets must be defined before using this step +# - $$FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $$FCLI_DEFAULT_SSC_USER +# - $$FCLI_DEFAULT_SSC_PASSWORD +# - $$FCLI_DEFAULT_SSC_CI_TOKEN +# - $$FCLI_DEFAULT_SSC_URL +# - $$SSC_APP_VERSION_ID + + +steps: +- name: 'gcr.io/cloud-builders/docker' + args: ['push', 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA'] + id: 'push-image-to-container-registry' + +- name: 'gcr.io/cloud-builders/gcloud' + args: + - 'run' + - 'deploy' + - 'iwajava' + - '--image' + - 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA' + - '--region' + - 'us-central1' + - '--platform' + - 'managed' + - '--allow-unauthenticated' + id: 'deploy-to-cloud-run' + +- name: 'fortifydocker/fortify-ci-tools:5.4.1-jdk-17' + entrypoint: "bash" + args: + - "-c" + - | + echo Setting connection with Fortify Platform + fcli ssc session login + fcli sc-dast session login + + fcli sc-dast scan start --name=$$SC_DAST_SCAN_NAME --settings=$$SC_DAST_CICD_IDENTIFIER + + echo Terminating connection with Fortify Platform + fcli sc-dast session logout + fcli ssc session logout + secretEnv: ['FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN', 'FCLI_DEFAULT_SSC_USER', 'FCLI_DEFAULT_SSC_PASSWORD', 'FCLI_DEFAULT_SSC_CI_TOKEN', 'FCLI_DEFAULT_SSC_URL'] + env: + - 'FORTIFY_IP=${_PUBLIC_IP}' + - 'SC_DAST_CICD_IDENTIFIER=${_SC_DAST_CICD_IDENTIFIER}' + - 'SC_DAST_SCAN_NAME=IWA_DAST_GCP' +availableSecrets: + secretManager: + - versionName: projects/$PROJECT_ID/secrets/fcli_default_sc_sast_client_auth_token/versions/latest + env: 'FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_user/versions/latest + env: 'FCLI_DEFAULT_SSC_USER' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_password/versions/latest + env: 'FCLI_DEFAULT_SSC_PASSWORD' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_ci_token/versions/latest + env: 'FCLI_DEFAULT_SSC_CI_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_url/versions/latest + env: 'FCLI_DEFAULT_SSC_URL' +images: +- 'gcr.io/$PROJECT_ID/iwa_java:latest' +- 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA' +- 'gcr.io/$PROJECT_ID/iwa_java:$BUILD_ID' \ No newline at end of file diff --git a/devops-integrations/gcp/cloudbuild_fortify_sast_fod.yaml b/devops-integrations/gcp/cloudbuild_fortify_sast_fod.yaml new file mode 100644 index 0000000..834493b --- /dev/null +++ b/devops-integrations/gcp/cloudbuild_fortify_sast_fod.yaml @@ -0,0 +1,47 @@ +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your Google Cloud Build pipeline +# The following Google Cloud Build Secrets must be defined before using this job +# - $$FCLI_DEFAULT_FOD_URL +# - $$FCLI_DEFAULT_FOD_USER +# - $$FCLI_DEFAULT_FOD_PASSWORD +# - $$FCLI_DEFAULT_FOD_TENANT +# - $$FOD_RELEASE_ID + +steps: +- name: maven:3.9.7 + entrypoint: 'mvn' + args: ['clean', 'package', '-DskipTests'] + +- name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', 'gcr.io/$PROJECT_ID/iwa_java:latest', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$BUILD_ID', '.'] + id: 'build-image-IWAJava' + +- name: 'fortifydocker/fortify-ci-tools:5.4.1-jdk-17' + entrypoint: bash + args: + - -c + - | + fcli fod session login + + scancentral package -bt mvn -oss -o package.zip + + fcli fod sast start --release=$$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes="$$FOD_NOTES" --store=Id + fcli fod sast wait-for ::Id:: --interval=30s + + fcli fod issue list --release=$$FOD_RELEASE_ID + fcli fod session logout + secretEnv: ['FCLI_DEFAULT_FOD_URL', 'FCLI_DEFAULT_FOD_USER', 'FCLI_DEFAULT_FOD_PASSWORD', 'FCLI_DEFAULT_FOD_TENANT'] + env: + - 'FOD_RELEASE_ID=${_FOD_RELEASE_ID}' + - 'FOD_NOTES=Triggered by GCP Pipeline' + id: 'fortify-static-scan' + waitFor: ['build-image-IWAJava'] +availableSecrets: + secretManager: + - versionName: projects/$PROJECT_ID/secrets/FCLI_DEFAULT_FOD_URL/versions/latest + env: 'FCLI_DEFAULT_FOD_URL' + - versionName: projects/$PROJECT_ID/secrets/FCLI_DEFAULT_FOD_USER/versions/latest + env: 'FCLI_DEFAULT_FOD_USER' + - versionName: projects/$PROJECT_ID/secrets/FCLI_DEFAULT_FOD_PASSWORD/versions/latest + env: 'FCLI_DEFAULT_FOD_PASSWORD' + - versionName: projects/$PROJECT_ID/secrets/FCLI_DEFAULT_FOD_TENANT/versions/latest + env: 'FCLI_DEFAULT_FOD_TENANT' \ No newline at end of file diff --git a/devops-integrations/gcp/cloudbuild_fortify_sast_scancentral.yaml b/devops-integrations/gcp/cloudbuild_fortify_sast_scancentral.yaml new file mode 100644 index 0000000..4acd2a1 --- /dev/null +++ b/devops-integrations/gcp/cloudbuild_fortify_sast_scancentral.yaml @@ -0,0 +1,56 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Google Cloud Build pipeline +# The following Google Cloud Build Secrets must be defined before using this step +# - $$FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $$FCLI_DEFAULT_SSC_USER +# - $$FCLI_DEFAULT_SSC_PASSWORD +# - $$FCLI_DEFAULT_SSC_CI_TOKEN +# - $$FCLI_DEFAULT_SSC_URL +# - $$SSC_APP_VERSION_ID + + +steps: +- name: maven:3.9.7 + entrypoint: 'mvn' + args: ['-q', 'clean', 'package', '-DskipTests'] + +- name: 'gcr.io/cloud-builders/docker' + args: ['build', '-t', 'gcr.io/$PROJECT_ID/iwa_java:latest', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$COMMIT_SHA', '-t', 'gcr.io/$PROJECT_ID/iwa_java:$BUILD_ID', '.'] + id: 'build-image-IWAJava' + +- name: 'fortifydocker/fortify-ci-tools:5.4.1-jdk-17' + entrypoint: bash + args: + - -c + - | + echo Setting connection with Fortify Platform + fcli ssc session login + fcli sc-sast session login + + scancentral package -bt mvn -o package.zip + fcli sc-sast scan start --publish-to=$$SSC_APP_VERSION_ID --sensor-version=$$SC_SAST_SENSOR_VERSION --package-file=package.zip --store=Id + + fcli sc-sast scan wait-for ::Id:: --interval=30s + fcli ssc issue count --appversion=$$SSC_APP_VERSION_ID + + echo Terminating connection with Fortify Platform + fcli sc-sast session logout + fcli ssc session logout + secretEnv: ['FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN', 'FCLI_DEFAULT_SSC_USER', 'FCLI_DEFAULT_SSC_PASSWORD', 'FCLI_DEFAULT_SSC_CI_TOKEN', 'FCLI_DEFAULT_SSC_URL'] + env: + - 'FORTIFY_IP=${_PUBLIC_IP}' + - 'SSC_APP_VERSION_ID=${_SSC_APP_VERSION_ID}' + - 'SC_SAST_SENSOR_VERSION=24.2' +availableSecrets: + secretManager: + - versionName: projects/$PROJECT_ID/secrets/fcli_default_sc_sast_client_auth_token/versions/latest + env: 'FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_user/versions/latest + env: 'FCLI_DEFAULT_SSC_USER' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_password/versions/latest + env: 'FCLI_DEFAULT_SSC_PASSWORD' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_ci_token/versions/latest + env: 'FCLI_DEFAULT_SSC_CI_TOKEN' + - versionName: projects/$PROJECT_ID/secrets/fcli_default_ssc_url/versions/latest + env: 'FCLI_DEFAULT_SSC_URL' + id: 'fortify-static-scan' + waitFor: ['build-image-IWAJava'] diff --git a/devops-integrations/gitlab/debricked-sca.yml b/devops-integrations/gitlab/debricked-sca.yml new file mode 100644 index 0000000..d560f80 --- /dev/null +++ b/devops-integrations/gitlab/debricked-sca.yml @@ -0,0 +1,17 @@ +# Integrate Debricked software composition analysis into your Gitlab CICD pipeline +# The following Gitab environment variables must be defined before using this job +# - $DEBRICKED_TOKEN +# - $DEBRICKED_REPO + +debricked-scan: + image: maven:3.9-eclipse-temurin-11 + stage: test + needs: [build] + variables: + DEBRICKED_TOKEN: $DEBRICKED_TOKEN + DEBRICKED_EXCLUSIONS: "*.lock,*.json,*fingerprints*,target/**,samples/**" + script: + - curl -L https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked + - chmod +x debricked + - ./debricked scan --callgraph --prefer-npm -r "${DEBRICKED_REPO}" --access-token="${DEBRICKED_TOKEN}" . + allow_failure: true diff --git a/devops-integrations/gitlab/fortify-dast-api-scancentral.yml b/devops-integrations/gitlab/fortify-dast-api-scancentral.yml new file mode 100644 index 0000000..4a571b8 --- /dev/null +++ b/devops-integrations/gitlab/fortify-dast-api-scancentral.yml @@ -0,0 +1,40 @@ +# Integrate Fortify ScanCentral Dynamic AppSec Testing (DAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_APP_VERSION_ID +# - $SC_DAST_API_SETTINGS + +fortify-dast-api: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: test + needs: [deploy] + only: + variables: + - $CI_COMMIT_BRANCH == "main" + variables: + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_CI_TOKEN: $_FCLI_DEFAULT_SSC_CI_TOKEN + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + SC_DAST_API_SETTINGS: $SC_DAST_API_SETTINGS + SC_DAST_SCAN_NAME: "GitLab-Pipelines-API-Scan" + script: + - fcli ssc session login + - fcli sc-dast session login + + - fcli sc-dast scan start --name "$SC_DAST_SCAN_NAME" --settings $SC_DAST_API_SETTINGS --store=Id + - 'fcli sc-dast scan wait-for ::Id:: --interval=30s' + + - fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + - fcli ssc action run gitlab-dast-report --appversion=$SSC_APP_VERSION_ID --page-size=1000 + + - fcli sc-dast session logout + - fcli ssc session logout + allow_failure: true + artifacts: + reports: + dast: gl-fortify-dast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-dast-fod.yml b/devops-integrations/gitlab/fortify-dast-fod.yml new file mode 100644 index 0000000..23a0af8 --- /dev/null +++ b/devops-integrations/gitlab/fortify-dast-fod.yml @@ -0,0 +1,39 @@ +# Integrate Fortify on Demand Dynamic AppSec Testing (DAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FOD_RELEASE_ID +# - $_FCLI_DEFAULT_FOD_USER +# - $_FCLI_DEFAULT_FOD_PASSWORD +# - $_FCLI_DEFAULT_FOD_TENANT +# - $_FCLI_DEFAULT_FOD_URL +# Note: this assumes the FoD Release has already been configured for a DAST Automated Website scan + +fortify-dast: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: test + needs: [deploy] + only: + variables: + - $CI_COMMIT_BRANCH == "main" + variables: + FCLI_DEFAULT_FOD_USER: $_FCLI_DEFAULT_FOD_USER + FCLI_DEFAULT_FOD_PASSWORD: $_FCLI_DEFAULT_FOD_PASSWORD + FCLI_DEFAULT_FOD_TENANT: $_FCLI_DEFAULT_FOD_TENANT + FCLI_DEFAULT_FOD_URL: $_FCLI_DEFAULT_FOD_URL + FOD_RELEASE_ID: $_FOD_RELEASE_ID + FOD_NOTES: "Triggered by Gitlab Pipeline IID $CI_PIPELINE_IID: $CI_PIPELINE_URL" + script: + - fcli fod session login + + - fcli fod dast start --release=$FOD_RELEASE_ID --store=Id + - 'fcli fod dast wait-for ::Id:: --interval=30s' + + - 'fcli fod issue list --release=$FOD_RELEASE_ID --filters-param "scanType:Dynamic+severty:Critical|High"' + - fcli fod action run gitlab-dast-report --release=$FOD_RELEASE_ID + + - fcli fod session logout + allow_failure: true + artifacts: + reports: + dast: gl-fortify-dast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-dast-scancentral.yml b/devops-integrations/gitlab/fortify-dast-scancentral.yml new file mode 100644 index 0000000..a2df8d8 --- /dev/null +++ b/devops-integrations/gitlab/fortify-dast-scancentral.yml @@ -0,0 +1,33 @@ +# Integrate Fortify ScanCentral Dynamic AppSec Testing (DAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $FCLI_DEFAULT_SSC_URL +# - $FCLI_DEFAULT_SSC_USER +# - $FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_AV_ID +# - $SC_DAST_SETTINGS + +fortify-dast: + image: fortifydocker/fortify-ci-tools:5.4.1-jdk-17 + stage: test + needs: [deploy] + variables: + SC_DAST_SCAN_NAME: "IWA-Java" + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + SC_DAST_CICD_IDENTIFIER: $_SC_DAST_CICD_IDENTIFIER + SSC_APP_VERSION_ID: $_SSC_APP_VERSION_ID + script: + - fcli ssc session login --insecure + - fcli sc-dast session login --insecure + - fcli sc-dast scan start --name=$SC_DAST_SCAN_NAME --settings=$SC_DAST_CICD_IDENTIFIER + + - fcli ssc action run gitlab-dast-report --appversion=$SSC_APP_VERSION_ID --page-size=1000 + + - fcli sc-dast session logout + - fcli ssc session logout + artifacts: + reports: + dast: gl-fortify-dast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-dast-web-scancentral.yml b/devops-integrations/gitlab/fortify-dast-web-scancentral.yml new file mode 100644 index 0000000..58d4eae --- /dev/null +++ b/devops-integrations/gitlab/fortify-dast-web-scancentral.yml @@ -0,0 +1,40 @@ +# Integrate Fortify ScanCentral Dynamic AppSec Testing (DAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_APP_VERSION_ID +# - $SC_DAST_WEB_SETTINGS + +fortify-dast-web: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: test + needs: [deploy] + only: + variables: + - $CI_COMMIT_BRANCH == "main" + variables: + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_CI_TOKEN: $_FCLI_DEFAULT_SSC_CI_TOKEN + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + SC_DAST_WEB_SETTINGS: $SC_DAST_WEB_SETTINGS + SC_DAST_SCAN_NAME: "GitLab-Pipelines-Web-Scan" + script: + - fcli ssc session login + - fcli sc-dast session login + + - fcli sc-dast scan start --name "$SC_DAST_SCAN_NAME" --settings $SC_DAST_WEB_SETTINGS --store=Id + - 'fcli sc-dast scan wait-for ::Id:: --interval=30s' + + - fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + - fcli ssc action run gitlab-dast-report --appversion=$SSC_APP_VERSION_ID --page-size=1000 + + - fcli sc-dast session logout + - fcli ssc session logout + allow_failure: true + artifacts: + reports: + dast: gl-fortify-dast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-fod-gate.yml b/devops-integrations/gitlab/fortify-fod-gate.yml new file mode 100644 index 0000000..555d3b6 --- /dev/null +++ b/devops-integrations/gitlab/fortify-fod-gate.yml @@ -0,0 +1,25 @@ +# Integrate Fortify on Demand Static Security Gate into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FOD_RELEASE_ID +# - $_FCLI_DEFAULT_FOD_USER +# - $_FCLI_DEFAULT_FOD_PASSWORD +# - $_FCLI_DEFAULT_FOD_TENANT +# - $_FCLI_DEFAULT_FOD_URL + +fortify-security-gate: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: report + variables: + FCLI_DEFAULT_FOD_USER: $_FCLI_DEFAULT_FOD_USER + FCLI_DEFAULT_FOD_PASSWORD: $_FCLI_DEFAULT_FOD_PASSWORD + FCLI_DEFAULT_FOD_TENANT: $_FCLI_DEFAULT_FOD_TENANT + FCLI_DEFAULT_FOD_URL: $_FCLI_DEFAULT_FOD_URL + FOD_RELEASE_ID: $_FOD_RELEASE_ID + FOD_NOTES: "Triggered by Gitlab Pipeline IID $CI_PIPELINE_IID: $CI_PIPELINE_URL" + script: + - fcli fod session login + + - fcli fod action run release-summary --release=$FOD_RELEASE_ID + - fcli fod action run check-policy --release=$FOD_RELEASE_ID + + - fcli fod session logout diff --git a/devops-integrations/gitlab/fortify-sast-fod.yml b/devops-integrations/gitlab/fortify-sast-fod.yml new file mode 100644 index 0000000..dc1d9e4 --- /dev/null +++ b/devops-integrations/gitlab/fortify-sast-fod.yml @@ -0,0 +1,37 @@ +# Integrate Fortify on Demand Static AppSec Testing (SAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FOD_RELEASE_ID +# - $_FCLI_DEFAULT_FOD_USER +# - $_FCLI_DEFAULT_FOD_PASSWORD +# - $_FCLI_DEFAULT_FOD_TENANT +# - $_FCLI_DEFAULT_FOD_URL +# Note: this assumes the FoD Release has already been configured + +fortify-sast: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: test + needs: [build] + variables: + FCLI_DEFAULT_FOD_USER: $_FCLI_DEFAULT_FOD_USER + FCLI_DEFAULT_FOD_PASSWORD: $_FCLI_DEFAULT_FOD_PASSWORD + FCLI_DEFAULT_FOD_TENANT: $_FCLI_DEFAULT_FOD_TENANT + FCLI_DEFAULT_FOD_URL: $_FCLI_DEFAULT_FOD_URL + FOD_RELEASE_ID: $_FOD_RELEASE_ID + FOD_NOTES: "Triggered by Gitlab Pipeline IID $CI_PIPELINE_IID: $CI_PIPELINE_URL" + script: + - fcli fod session login + + - scancentral package -bt mvn -oss -o package.zip + - fcli fod sast start --release=$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes="$FOD_NOTES" --store=Id + - 'fcli fod sast wait-for ::Id:: --interval=30s' + + - 'fcli fod issue list --release=$FOD_RELEASE_ID --filters-param "scanType:Static+severty:Critical|High"' + - fcli fod action run gitlab-sast-report --release=$FOD_RELEASE_ID + + - fcli fod session logout + allow_failure: true + artifacts: + reports: + sast: gl-fortify-sast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-sast-scancentral.yml b/devops-integrations/gitlab/fortify-sast-scancentral.yml new file mode 100644 index 0000000..f54b2be --- /dev/null +++ b/devops-integrations/gitlab/fortify-sast-scancentral.yml @@ -0,0 +1,39 @@ +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN +# - $_FCLI_DEFAULT_SSC_CI_TOKEN +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_APP_VERSION_ID + +fortify-sast: + stage: test + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + needs: [build] + variables: + SC_SAST_SENSOR_VERSION: 24.2 + FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN: $_FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_CI_TOKEN: $_FCLI_DEFAULT_SSC_CI_TOKEN + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + script: + - fcli ssc session login + - fcli sc-sast session login + + - scancentral package -bt mvn -o package.zip + - fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SC_SAST_SENSOR_VERSION --package-file=package.zip --store=Id + - 'fcli sc-sast scan wait-for ::Id:: --interval=30s' + + - fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + - fcli ssc action run gitlab-sast-report --appversion=$SSC_APP_VERSION_ID --page-size=1000 + + - fcli sc-sast session logout + - fcli ssc session logout + allow_failure: true + artifacts: + reports: + sast: gl-fortify-sast.json + expire_in: 3 days + when: always diff --git a/devops-integrations/gitlab/fortify-sca.yml b/devops-integrations/gitlab/fortify-sca.yml new file mode 100644 index 0000000..c62fc72 --- /dev/null +++ b/devops-integrations/gitlab/fortify-sca.yml @@ -0,0 +1,51 @@ +# Integrate Debricked software composition analysis into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $DEBRICKED_TOKEN +# - $DEBRICKED_REPO +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_APP_VERSION_ID + +debricked-scan: + image: maven:3.9-eclipse-temurin-11 + stage: test + needs: [build] + variables: + DEBRICKED_TOKEN: $DEBRICKED_TOKEN + DEBRICKED_EXCLUSIONS: "*.lock,*.json,*fingerprints*,target/**,samples/**" + script: + - curl -L https://github.com/debricked/cli/releases/download/release-v2/cli_linux_x86_64.tar.gz | tar -xz debricked + - chmod +x debricked + - ./debricked scan --callgraph --prefer-npm -r "${DEBRICKED_REPO}" --access-token="${DEBRICKED_TOKEN}" . + allow_failure: true + +fortify-sca: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: test + needs: [debricked-scan] + variables: + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + script: + - fcli ssc session login + + - fcli ssc artifact import-debricked --appversion=$SSC_APP_VERSION_ID --debricked-access-token=$DEBRICKED_TOKEN --repository=$DEBRICKED_REPO --branch=$CI_COMMIT_BRANCH --save-sbom-as=debricked-sbom.json --store=Id + + - 'fcli ssc artifact wait-for ::Id:: --interval=30s' + + - fcli ssc action run appversion-summary --appversion=$SSC_APP_VERSION_ID + - fcli ssc action run gitlab-debricked-report --appversion=$SSC_APP_VERSION_ID + + - fcli ssc session logout + allow_failure: true + artifacts: + reports: + dependency_scanning: gl-fortify-debricked-depscan.json + #cyclonedx: debricked-sbom.json + paths: + - debricked-sbom.json + expire_in: 3 days + when: always + diff --git a/devops-integrations/gitlab/fortify-ssc-gate.yml b/devops-integrations/gitlab/fortify-ssc-gate.yml new file mode 100644 index 0000000..d0c1dfa --- /dev/null +++ b/devops-integrations/gitlab/fortify-ssc-gate.yml @@ -0,0 +1,23 @@ +# Integrate Fortify Software Security Center Gate into your Gitlab CICD pipeline +# The following Gitlab environment variables must be defined before using this job +# - $_FCLI_DEFAULT_SSC_CI_TOKEN +# - $_FCLI_DEFAULT_SSC_URL +# - $_FCLI_DEFAULT_SSC_USER +# - $_FCLI_DEFAULT_SSC_PASSWORD +# - $SSC_APP_VERSION_ID + +fortify-security-gate: + image: fortifydocker/fortify-ci-tools:5.5.0-jdk-17 + stage: report + variables: + FCLI_DEFAULT_SSC_USER: $_FCLI_DEFAULT_SSC_USER + FCLI_DEFAULT_SSC_PASSWORD: $_FCLI_DEFAULT_SSC_PASSWORD + FCLI_DEFAULT_SSC_CI_TOKEN: $_FCLI_DEFAULT_SSC_CI_TOKEN + FCLI_DEFAULT_SSC_URL: $_FCLI_DEFAULT_SSC_URL + script: + - fcli ssc session login + + - fcli ssc action run appversion-summary --appversion=$SSC_APP_VERSION_ID --filtersets="default" + - fcli ssc action run check-policy --appversion=$SSC_APP_VERSION_ID + + - fcli ssc session logout diff --git a/devops-integrations/oci/fortify-sast-fod.bash b/devops-integrations/oci/fortify-sast-fod.bash new file mode 100644 index 0000000..889f2c2 --- /dev/null +++ b/devops-integrations/oci/fortify-sast-fod.bash @@ -0,0 +1,84 @@ +#!/bin/bash +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your OCI DevOps Project pipeline + +# *** Configuration *** + +# The following vault secrets must be defined + # FCLI_DEFAULT_FOD_URL + # FCLI_DEFAULT_FOD_TENANT + # FCLI_DEFAULT_FOD_USER + # FCLI_DEFAULT_FOD_PASSWORD (PAT) + # FOD_RELEASE_ID + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +FODUPLOAD_VERSION=5.4.1 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/root/.fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +FODUPLOAD_HOME=$FORTIFY_TOOLS_DIR/FodUpload +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral +fod_notes="Triggered by OCI DevOps Projects" + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + + +# *** Execution *** +echo "Installing FCLI" +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +fcli tool fod-uploader install -v ${FODUPLOAD_VERSION} -d ${FODUPLOAD_HOME} +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + +export PATH=$FCLI_HOME/bin:$FODUPLOAD_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + +echo Setting connection with Fortify Platform +#Use --insecure switch if the SSL certificate is self generated. +fcli fod session login + +echo "Scan starting.." +scancentral package -bt mvn -oss -o package.zip +fcli fod sast start --release=$FOD_RELEASE_ID --file=package.zip --remediation=NonRemediationScanOnly --notes="$FOD_NOTES" --store=Id + +fcli fod sast wait-for ::Id:: --interval=30s + +fcli fod issue list --release=$FOD_RELEASE_ID + +fcli fod session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/devops-integrations/oci/fortify_sast_scancentral.bash b/devops-integrations/oci/fortify_sast_scancentral.bash new file mode 100644 index 0000000..556cdb2 --- /dev/null +++ b/devops-integrations/oci/fortify_sast_scancentral.bash @@ -0,0 +1,87 @@ +#!/bin/bash +# Integrate Fortify ScanCentral Static AppSec Testing (SAST) into your OCI DevOps Project pipeline + +# *** Configuration *** + +# The following vault secrets must be defined + # FCLI_DEFAULT_SC_SAST_CLIENT_AUTH_TOKEN + # FCLI_DEFAULT_SSC_USER + # FCLI_DEFAULT_SSC_PASSWORD + # FCLI_DEFAULT_SSC_CI_TOKEN + # FCLI_DEFAULT_SSC_URL + # SSC_APP_VERSION_ID + +# Local variables (modify as needed) +FCLI_VERSION=v2.4.0 +SCANCENTRAL_VERSION=24.2.0 +FCLI_URL=https://github.com/fortify-ps/fcli/releases/download/${FCLI_VERSION}/fcli-linux.tgz +FCLI_SIG_URL=${FCLI_URL}.rsa_sha256 +FORTIFY_TOOLS_DIR="/root/.fortify/tools" +FCLI_HOME=$FORTIFY_TOOLS_DIR/fcli +SCANCENTRAL_HOME=$FORTIFY_TOOLS_DIR/ScanCentral +ssc_ip=x.x.x.x + +# *** Supported Functions *** +verifySig() { + local src sig + src="$1"; sig="$2" + openssl dgst -sha256 -verify <(echo "-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArij9U9yJVNc53oEMFWYp +NrXUG1UoRZseDh/p34q1uywD70RGKKWZvXIcUAZZwbZtCu4i0UzsrKRJeUwqanbc +woJvYanp6lc3DccXUN1w1Y0WOHOaBxiiK3B1TtEIH1cK/X+ZzazPG5nX7TSGh8Tp +/uxQzUFli2mDVLqaP62/fB9uJ2joX9Gtw8sZfuPGNMRoc8IdhjagbFkhFT7WCZnk +FH/4Co007lmXLAe12lQQqR/pOTeHJv1sfda1xaHtj4/Tcrq04Kx0ZmGAd5D9lA92 +8pdBbzoe/mI5/Sk+nIY3AHkLXB9YAaKJf//Wb1yiP1/hchtVkfXyIaGM+cVyn7AN +VQIDAQAB +-----END PUBLIC KEY-----") -signature "${sig}" "${src}" +} + +installFcli() { + local src sigSrc tgt tmpRoot tmpFile tmpDir + src="$1"; sigSrc="$2"; tgt="$3"; + tmpRoot=$(mktemp -d); tmpFile="$tmpRoot/archive.tmp"; tmpDir="$tmpRoot/extracted" + echo "Downloading file" + wget -O $tmpFile $src + echo "Verifying Signature..." + verifySig "$tmpFile" <(curl -fsSL -o - "$sigSrc") + echo "Unzipping: tar -zxf " + $tmpFile + " -C " + $tmpDir + mkdir $tmpDir + mkdir -p $tgt + + tar -zxf $tmpFile -C $tmpDir + mv $tmpDir/* $tgt + rm -rf $tmpRoot + find $tgt -type f +} + +# *** Execution *** +# Install FCLI +installFcli ${FCLI_URL} ${FCLI_SIG_URL} ${FCLI_HOME}/bin + +# Use when ssc IP is not static +#echo "adding host entry..." +#echo ${ssc_ip} fortify.cyberxdemo.com >> /etc/hosts +#cat /etc/hosts + +fcli tool sc-client install -v ${SCANCENTRAL_VERSION} -d ${SCANCENTRAL_HOME} + + +export PATH=$FCLI_HOME/bin:$SCANCENTRAL_HOME/bin:${PATH} + +echo Setting connection with Fortify Platform +# USE --INSECURE WHEN YOUR SSL CERTIFICATES ARE SELF GENERATED/UNTRUSTED +fcli ssc session login +fcli sc-sast session login + +scancentral package -bt mvn -o package.zip + +fcli sc-sast scan start --publish-to=$SSC_APP_VERSION_ID --sensor-version=$SCANCENTRAL_VERSION --package-file=package.zip --store=Id +fcli sc-sast scan wait-for ::Id:: --interval=30s +fcli ssc issue count --appversion=$SSC_APP_VERSION_ID + +echo Terminating connection with Fortify Platform +fcli sc-sast session logout +fcli ssc session logout +# *** Execution Completes *** + +# *** EoF *** \ No newline at end of file diff --git a/etc/IWA_API.postman_collection.json b/etc/IWA_API.postman_collection.json new file mode 100644 index 0000000..26f347e --- /dev/null +++ b/etc/IWA_API.postman_collection.json @@ -0,0 +1,13254 @@ +{ + "info": { + "_postman_id": "c7236a64-d315-4f3b-b726-49a47dcc149a", + "name": "Insecure Web App (IWA) API", + "description": "This is the REST API for Insecure Web App (IWA) Pharmacy Direct. You can select a development or production server to test the API. Most operations require authentication via a user specific JWT token. To retrieve a JWT token for a user you can use the '/authentication/sign-in' operation below and then copy the value of the 'accessToken' field. This value can then be entered when you click on the 'Authorize' button or lock icons.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "9630190" + }, + "item": [ + { + "name": "api/v3", + "item": [ + { + "name": "orders", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find order by Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find an order by UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"219f1f62-f415-804f-8989-82fce829f5b8\",\n \"user\": {\n \"id\": \"fbbb6785-1ee9-f637-1963-19cfc4a1aec3\",\n \"username\": \"minim dolore eu sit\",\n \"firstName\": \"esse eu dolor non\",\n \"lastName\": \"non dolor in\",\n \"email\": \"dolor qui tempor\",\n \"phone\": \"pariatur esse deserunt mollit\",\n \"address\": \"occaecat Duis\",\n \"city\": \"consequat deserunt enim\",\n \"state\": \"elit in\",\n \"zip\": \"occaecat amet\",\n \"country\": \"Ut fugiat consequat aliqua\",\n \"enabled\": true\n },\n \"orderNum\": \"ullamco\",\n \"orderDate\": \"1973-04-25T12:25:48.314Z\",\n \"cart\": \"consequat dolor offi\",\n \"amount\": 43847209.55708042,\n \"shipped\": false,\n \"shippedDate\": \"1998-01-31T14:45:21.758Z\",\n \"notes\": {}\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Order Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update an order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing order" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"219f1f62-f415-804f-8989-82fce829f5b8\",\n \"user\": {\n \"id\": \"fbbb6785-1ee9-f637-1963-19cfc4a1aec3\",\n \"username\": \"minim dolore eu sit\",\n \"firstName\": \"esse eu dolor non\",\n \"lastName\": \"non dolor in\",\n \"email\": \"dolor qui tempor\",\n \"phone\": \"pariatur esse deserunt mollit\",\n \"address\": \"occaecat Duis\",\n \"city\": \"consequat deserunt enim\",\n \"state\": \"elit in\",\n \"zip\": \"occaecat amet\",\n \"country\": \"Ut fugiat consequat aliqua\",\n \"enabled\": true\n },\n \"orderNum\": \"ullamco\",\n \"orderDate\": \"1973-04-25T12:25:48.314Z\",\n \"cart\": \"consequat dolor offi\",\n \"amount\": 43847209.55708042,\n \"shipped\": false,\n \"shippedDate\": \"1998-01-31T14:45:21.758Z\",\n \"notes\": {}\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Order Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete an order" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Order Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "c9b31f33-17a4-4fcd-927e-c14cdee32201", + "description": "(Required) UUID of the order to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find orders by keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for orders to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"372c0b93-c1d9-7776-18f9-5159d049516e\",\n \"code\": \"qui id\",\n \"name\": \"ex enim voluptate\",\n \"summary\": \"Duis ullamco quis aute\",\n \"description\": \"id in in\",\n \"image\": \"ullamco laboris in\",\n \"price\": -27913647.199027598,\n \"onSale\": true,\n \"salePrice\": 7322939.265867442,\n \"inStock\": false,\n \"timeToStock\": -83974172,\n \"rating\": 19487882,\n \"available\": true\n },\n {\n \"id\": \"6f7601d2-c7af-244f-2dd3-4175a1243416\",\n \"code\": \"irure consequat do eu\",\n \"name\": \"dolor\",\n \"summary\": \"Lorem esse\",\n \"description\": \"anim dolore in\",\n \"image\": \"officia enim occaecat reprehenderit cupidatat\",\n \"price\": 84333309.66977769,\n \"onSale\": true,\n \"salePrice\": -66026868.59564058,\n \"inStock\": true,\n \"timeToStock\": 75635255,\n \"rating\": 42086492,\n \"available\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1988-03-27T05:01:46.781Z\",\n \"errors\": [\n \"do tempor ea\",\n \"anim ex nostrud proident\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/orders?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new order", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new order" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"219f1f62-f415-804f-8989-82fce829f5b8\",\n \"user\": {\n \"id\": \"fbbb6785-1ee9-f637-1963-19cfc4a1aec3\",\n \"username\": \"minim dolore eu sit\",\n \"firstName\": \"esse eu dolor non\",\n \"lastName\": \"non dolor in\",\n \"email\": \"dolor qui tempor\",\n \"phone\": \"pariatur esse deserunt mollit\",\n \"address\": \"occaecat Duis\",\n \"city\": \"consequat deserunt enim\",\n \"state\": \"elit in\",\n \"zip\": \"occaecat amet\",\n \"country\": \"Ut fugiat consequat aliqua\",\n \"enabled\": true\n },\n \"orderNum\": \"ullamco\",\n \"orderDate\": \"1973-04-25T12:25:48.314Z\",\n \"cart\": \"consequat dolor offi\",\n \"amount\": 43847209.55708042,\n \"shipped\": false,\n \"shippedDate\": \"1998-01-31T14:45:21.758Z\",\n \"notes\": {}\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Order Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"userId\": \"urn:uuid:0ce60f2b-441b-9554-78d6-7ef2860906b2\",\n \"orderNum\": \"nisi quis dolor\",\n \"orderDate\": \"1947-03-21T05:57:58.165Z\",\n \"amount\": 83820970.9698413,\n \"cart\": \"irure labore ullamco consequat exercit\",\n \"shipped\": true,\n \"shippedDate\": \"1968-06-30T17:00:01.005Z\",\n \"notes\": {}\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/orders", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "orders" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "products", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find product by Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a product by UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:76ee8768-d4bc-5da5-5aa7-0ea4dbd03a70\",\n \"code\": \"et labore ad irure\",\n \"name\": \"dolor anim voluptate eu reprehenderit\",\n \"summary\": \"elit Ut consectetur dolor\",\n \"description\": \"deserunt dolo\",\n \"image\": \"dolore aliquip\",\n \"price\": -65265961.13949914,\n \"onSale\": false,\n \"salePrice\": 93790070.10770187,\n \"inStock\": false,\n \"timeToStock\": -95676653,\n \"rating\": 52885506,\n \"available\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Product Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update a product", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing product" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:76ee8768-d4bc-5da5-5aa7-0ea4dbd03a70\",\n \"code\": \"et labore ad irure\",\n \"name\": \"dolor anim voluptate eu reprehenderit\",\n \"summary\": \"elit Ut consectetur dolor\",\n \"description\": \"deserunt dolo\",\n \"image\": \"dolore aliquip\",\n \"price\": -65265961.13949914,\n \"onSale\": false,\n \"salePrice\": 93790070.10770187,\n \"inStock\": false,\n \"timeToStock\": -95676653,\n \"rating\": 52885506,\n \"available\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Product Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a product", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete a product" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Product Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "(Required) UUID of the product to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find products by keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for products to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"372c0b93-c1d9-7776-18f9-5159d049516e\",\n \"code\": \"qui id\",\n \"name\": \"ex enim voluptate\",\n \"summary\": \"Duis ullamco quis aute\",\n \"description\": \"id in in\",\n \"image\": \"ullamco laboris in\",\n \"price\": -27913647.199027598,\n \"onSale\": true,\n \"salePrice\": 7322939.265867442,\n \"inStock\": false,\n \"timeToStock\": -83974172,\n \"rating\": 19487882,\n \"available\": true\n },\n {\n \"id\": \"6f7601d2-c7af-244f-2dd3-4175a1243416\",\n \"code\": \"irure consequat do eu\",\n \"name\": \"dolor\",\n \"summary\": \"Lorem esse\",\n \"description\": \"anim dolore in\",\n \"image\": \"officia enim occaecat reprehenderit cupidatat\",\n \"price\": 84333309.66977769,\n \"onSale\": true,\n \"salePrice\": -66026868.59564058,\n \"inStock\": true,\n \"timeToStock\": 75635255,\n \"rating\": 42086492,\n \"available\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/products?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new product", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new product" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:76ee8768-d4bc-5da5-5aa7-0ea4dbd03a70\",\n \"code\": \"et labore ad irure\",\n \"name\": \"dolor anim voluptate eu reprehenderit\",\n \"summary\": \"elit Ut consectetur dolor\",\n \"description\": \"deserunt dolo\",\n \"image\": \"dolore aliquip\",\n \"price\": -65265961.13949914,\n \"onSale\": false,\n \"salePrice\": 93790070.10770187,\n \"inStock\": false,\n \"timeToStock\": -95676653,\n \"rating\": 52885506,\n \"available\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Product Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": true,\n \"code\": \"ut mollit\",\n \"description\": \"dolore in elit quiqui culpa voluptatein exercitation cupidatat pariatur\",\n \"inStock\": false,\n \"name\": \"voluptat\",\n \"onSale\": true,\n \"summary\": \"ullamcodeserunt nostrud cillum ea\",\n \"image\": \"in proident\",\n \"price\": 56987393.67315651,\n \"salePrice\": 66173006.36598087,\n \"timeToStock\": 212,\n \"rating\": 4\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "products" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "messages", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find message by Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a message by UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:998331bd-e8cc-2f7a-2c5f-05862402c3b2\",\n \"user\": {\n \"id\": \"urn:uuid:abacba6c-14bb-49ba-b8df-7d8447869329\",\n \"username\": \"dolore eu in\",\n \"firstName\": \"velit anim in\",\n \"lastName\": \"veniam voluptate\",\n \"email\": \"aliqua\",\n \"phone\": \"pariatur veniam in magna et\",\n \"address\": \"dolor nostrud\",\n \"city\": \"dolor et mollit\",\n \"state\": \"esse sunt nulla\",\n \"zip\": \"sed dolor \",\n \"country\": \"ullamco magna sed velit\",\n \"enabled\": false\n },\n \"text\": \"est ea\",\n \"sentDate\": \"1969-08-11T11:28:52.437Z\",\n \"readDate\": \"1961-12-22T11:40:54.581Z\",\n \"read\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Message Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update a message", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update a users message" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:998331bd-e8cc-2f7a-2c5f-05862402c3b2\",\n \"user\": {\n \"id\": \"urn:uuid:abacba6c-14bb-49ba-b8df-7d8447869329\",\n \"username\": \"dolore eu in\",\n \"firstName\": \"velit anim in\",\n \"lastName\": \"veniam voluptate\",\n \"email\": \"aliqua\",\n \"phone\": \"pariatur veniam in magna et\",\n \"address\": \"dolor nostrud\",\n \"city\": \"dolor et mollit\",\n \"state\": \"esse sunt nulla\",\n \"zip\": \"sed dolor \",\n \"country\": \"ullamco magna sed velit\",\n \"enabled\": false\n },\n \"text\": \"est ea\",\n \"sentDate\": \"1969-08-11T11:28:52.437Z\",\n \"readDate\": \"1961-12-22T11:40:54.581Z\",\n \"read\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Message Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a message", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete a users existing message" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Message Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "6914e47d-2f0a-4deb-a712-12e7801e13e8", + "description": "(Required) UUID of the message to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Finds messages by keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for messages to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"urn:uuid:f98c3903-49f7-fd44-8673-f1a1f2da47b4\",\n \"user\": {\n \"id\": \"urn:uuid:df3e0406-76a3-9581-3e76-04db27aba786\",\n \"username\": \"veniam ea\",\n \"firstName\": \"in\",\n \"lastName\": \"dolore\",\n \"email\": \"in ut pariatur ea Duis\",\n \"phone\": \"occaecat mollit amet\",\n \"address\": \"nisi pariatur eiusmod magna\",\n \"city\": \"ut irure sit aliquip consectetur\",\n \"state\": \"quis pariatur aliquip\",\n \"zip\": \"enim dolore\",\n \"country\": \"adipisicing labore Ut quis\",\n \"enabled\": true\n },\n \"text\": \"anim nostrud\",\n \"sentDate\": \"1972-03-26T22:49:05.220Z\",\n \"readDate\": \"2004-12-07T19:49:19.734Z\",\n \"read\": true\n },\n {\n \"id\": \"d569798b-0662-c0cb-a183-3cf930029a16\",\n \"user\": {\n \"id\": \"urn:uuid:954a8d40-efdb-5d3b-8af2-752ef9e9d401\",\n \"username\": \"nisi in ex sed\",\n \"firstName\": \"nostrud in laboris elit\",\n \"lastName\": \"laboris ex nisi\",\n \"email\": \"sint ad nisi Lorem culpa\",\n \"phone\": \"mollit sunt commodo\",\n \"address\": \"velit\",\n \"city\": \"nulla enim\",\n \"state\": \"fugiat Duis consectetur commodo\",\n \"zip\": \"velit aute ipsum Lorem\",\n \"country\": \"veniam ut ipsum Duis\",\n \"enabled\": true\n },\n \"text\": \"in eiusmod esse do nulla\",\n \"sentDate\": \"1943-11-27T04:24:14.914Z\",\n \"readDate\": \"1988-01-04T14:29:14.864Z\",\n \"read\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new message", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new message for a user" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:998331bd-e8cc-2f7a-2c5f-05862402c3b2\",\n \"user\": {\n \"id\": \"urn:uuid:abacba6c-14bb-49ba-b8df-7d8447869329\",\n \"username\": \"dolore eu in\",\n \"firstName\": \"velit anim in\",\n \"lastName\": \"veniam voluptate\",\n \"email\": \"aliqua\",\n \"phone\": \"pariatur veniam in magna et\",\n \"address\": \"dolor nostrud\",\n \"city\": \"dolor et mollit\",\n \"state\": \"esse sunt nulla\",\n \"zip\": \"sed dolor \",\n \"country\": \"ullamco magna sed velit\",\n \"enabled\": false\n },\n \"text\": \"est ea\",\n \"sentDate\": \"1969-08-11T11:28:52.437Z\",\n \"readDate\": \"1961-12-22T11:40:54.581Z\",\n \"read\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Message Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"text\": \"dolor sedproident ullamco Excepteurmagna\",\n \"userId\": \"urn:uuid:2a21517e-f52e-371f-f519-b44773cea7ce\",\n \"sentDate\": \"1947-03-29T07:54:38.699Z\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/messages", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Get users unread message count", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Get a users unread message count by their UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"urn:uuid:998331bd-e8cc-2f7a-2c5f-05862402c3b2\",\n \"user\": {\n \"id\": \"urn:uuid:abacba6c-14bb-49ba-b8df-7d8447869329\",\n \"username\": \"dolore eu in\",\n \"firstName\": \"velit anim in\",\n \"lastName\": \"veniam voluptate\",\n \"email\": \"aliqua\",\n \"phone\": \"pariatur veniam in magna et\",\n \"address\": \"dolor nostrud\",\n \"city\": \"dolor et mollit\",\n \"state\": \"esse sunt nulla\",\n \"zip\": \"sed dolor \",\n \"country\": \"ullamco magna sed velit\",\n \"enabled\": false\n },\n \"text\": \"est ea\",\n \"sentDate\": \"1969-08-11T11:28:52.437Z\",\n \"readDate\": \"1961-12-22T11:40:54.581Z\",\n \"read\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/messages/unread-count/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "messages", + "unread-count", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "32e7db01-86bc-4687-9ecb-d79b265ac14f", + "description": "(Required) UUID of the user to find messages for. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "reviews", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find review by Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a review by UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update an review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete an review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Review Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "822f734a-3d13-4ebc-bff6-9c36d29866a6", + "description": "(Required) UUID of the review to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find reviews by product and keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0", + "description": "UUID of the product to find reviews for." + }, + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for reviews to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Product and keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"372c0b93-c1d9-7776-18f9-5159d049516e\",\n \"code\": \"qui id\",\n \"name\": \"ex enim voluptate\",\n \"summary\": \"Duis ullamco quis aute\",\n \"description\": \"id in in\",\n \"image\": \"ullamco laboris in\",\n \"price\": -27913647.199027598,\n \"onSale\": true,\n \"salePrice\": 7322939.265867442,\n \"inStock\": false,\n \"timeToStock\": -83974172,\n \"rating\": 19487882,\n \"available\": true\n },\n {\n \"id\": \"6f7601d2-c7af-244f-2dd3-4175a1243416\",\n \"code\": \"irure consequat do eu\",\n \"name\": \"dolor\",\n \"summary\": \"Lorem esse\",\n \"description\": \"anim dolore in\",\n \"image\": \"officia enim occaecat reprehenderit cupidatat\",\n \"price\": 84333309.66977769,\n \"onSale\": true,\n \"salePrice\": -66026868.59564058,\n \"inStock\": true,\n \"timeToStock\": 75635255,\n \"rating\": 42086492,\n \"available\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/reviews?pid=eec467c8-5de9-4c7c-8541-7b31614d31a0&keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "query": [ + { + "key": "pid", + "value": "eec467c8-5de9-4c7c-8541-7b31614d31a0" + }, + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new review", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new review" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"96ab576c-c0ba-aa28-9bbc-2dc6e1f8c9ef\",\n \"product\": {\n \"id\": \"urn:uuid:0547300f-940e-0db3-df53-a92215b455ba\",\n \"code\": \"consectetur consequat officia quis\",\n \"name\": \"cupidatat\",\n \"summary\": \"magna\",\n \"description\": \"veniam anim amet\",\n \"image\": \"minim amet nisi\",\n \"price\": -25610873.854361802,\n \"onSale\": false,\n \"salePrice\": 39954746.859428346,\n \"inStock\": false,\n \"timeToStock\": -81956499,\n \"rating\": -24039134,\n \"available\": true\n },\n \"user\": {\n \"id\": \"1994beb5-b4ba-261c-df92-83fb7fe5a1e4\",\n \"username\": \"nulla anim ut\",\n \"firstName\": \"veniam velit deserunt aliquip\",\n \"lastName\": \"est veniam reprehenderit sint\",\n \"email\": \"est velit nostrud sint\",\n \"phone\": \"qui reprehenderit aute irure\",\n \"address\": \"dolor minim\",\n \"city\": \"minim\",\n \"state\": \"ullamco\",\n \"zip\": \"occaecat laboris\",\n \"country\": \"sit magna quis\",\n \"enabled\": true\n },\n \"reviewDate\": \"1971-01-13T01:17:05.090Z\",\n \"comment\": \"occaecat labore velit\",\n \"rating\": -63362603\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Review Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"productId\": \"urn:uuid:3fee2c70-9b55-653c-8ca5-802cf4387648\",\n \"userId\": \"urn:uuid:f30590af-b640-0ba8-c15d-28e3633f9b6d\",\n \"reviewDate\": \"1972-01-14T07:55:42.330Z\",\n \"comment\": \"qui tempor cillum labore\",\n \"rating\": -48971168\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/reviews", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "reviews" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "roles", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find role Id", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a specific role by its UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Message Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update a role", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing role" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Role Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a role", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete a role" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Role not found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "-7817338", + "description": "(Required) UUID of the role to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find roles by keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for roles to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"id\": \"04cf0720-01da-dd43-aa8f-87e6b2ab9ca7\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"711f68c7-7afa-535f-8e93-b31e458fa9e1\",\n \"name\": \"ROLE_USER\"\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/roles?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new role", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new role" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Role Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": \"c489b15a-6d1d-4f3d-3be8-36570c37c865\",\n \"name\": \"ROLE_CUSTOMER\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/roles", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "roles" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "site", + "item": [ + { + "name": "Check if username is taken", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Check if a user with the specified username already exists in the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"paria\",\n \"firstName\": \"voluptate ut Ut est\",\n \"lastName\": \"cupidatat inc\",\n \"phone\": \"77494218\",\n \"username\": \"nzu00\",\n \"id\": \"459e4949-9495-0287-2a5b-8275f7fa2056\",\n \"address\": \"Duis cillum\",\n \"city\": \"exercitation culpa est\",\n \"state\": \"officia commodo est magna Lorem\",\n \"zip\": \"ut\",\n \"country\": \"sint in\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:69dab483-422a-ef21-a008-64fe6e9eabea\",\n \"name\": \"ROLE_API\"\n },\n {\n \"id\": \"urn:uuid:05e4dcc3-6ce8-593f-2169-8a2009642443\",\n \"name\": \"ROLE_API\"\n }\n ],\n \"enabled\": false\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/username-already-exists/:username", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "username-already-exists", + ":username" + ], + "variable": [ + { + "key": "username", + "value": "user1", + "description": "(Required) Username to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Check if email exists", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Check if a user with the specified email address already exists in the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Not Found", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/email-already-exists/:email", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "email-already-exists", + ":email" + ], + "variable": [ + { + "key": "email", + "value": "user1@localhost.com", + "description": "(Required) Email address to check. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Register a new user", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Register a new user with the site" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"username\": \"nostrud mollit officia non\",\n \"password\": \"commodo voluptate Duis nostrud\",\n \"firstName\": \"do laboris ullamco\",\n \"lastName\": \"ullamco in ea\",\n \"email\": \"elit tem\",\n \"phone\": \"culpa proid\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Already Exists", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"et\",\n \"firstName\": \"occaecat\",\n \"lastName\": \"culpa laboris ex \",\n \"password\": \"ad labore anim dolore\",\n \"phone\": \"777729653851\",\n \"username\": \"pygr\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/register-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "register-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Subscribe a new user", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Subscribe a new user to the newsletter" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Already Exists", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"id\": -44080311,\n \"firstName\": \"dolo\",\n \"lastName\": \"cupidatat sint\",\n \"email\": \"esse commodo\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/subscribe-user", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "subscribe-user" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Sign in", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Sign in to the system" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"password\": \"Duis nisi consectetur ut\",\n \"username\": \"proident ut occaecat minim sunt\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/site/sign-in", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "sign-in" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Get the site status", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Get the site message of the day" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"health\": \"anim aute eu pariatu\",\n \"motd\": \"voluptate magna\"\n },\n {\n \"health\": \"occaecat Ut c\",\n \"motd\": \"culpa ut aliqua aliquip nostrud\"\n }\n]" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/v3/site/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "site", + "status" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + }, + { + "name": "users", + "item": [ + { + "name": "{id}", + "item": [ + { + "name": "Find user by UUID", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Find a specific user by their UUID" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Not Found", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be found. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Update a user", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Update an existing user" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Not Found", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "PUT", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Delete a user", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Delete an existing user" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Bad request", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + }, + { + "name": "User Not Found", + "originalRequest": { + "method": "DELETE", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "db4cfab1-ff1d-4bca-a662-394771841383", + "description": "(Required) UUID of the user to be updated. Cannot be empty." + }, + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Content-Type", + "value": "*/*" + } + ], + "cookie": [], + "body": "" + } + ] + } + ] + }, + { + "name": "Find users by keyword(s)", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex", + "description": "Keyword(s) search for users to be found." + }, + { + "key": "offset", + "value": "21300640", + "description": "Offset of the starting record. 0 indicates the first record." + }, + { + "key": "limit", + "value": "21300640", + "description": "Maximum records to return. The maximum value allowed is 50." + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Keyword search by %keyword% format" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "[\n {\n \"email\": \"proident es\",\n \"firstName\": \"quis est of\",\n \"lastName\": \"minim\",\n \"phone\": \"85896000\",\n \"username\": \"7ecb3hdav\",\n \"id\": \"urn:uuid:e2e33004-5679-a1ad-113c-2a2c73ffa68f\",\n \"address\": \"eu\",\n \"city\": \"ullamco pariatur\",\n \"state\": \"ni\",\n \"zip\": \"Lorem in est\",\n \"country\": \"nostrud elit\",\n \"authorities\": [\n {\n \"id\": \"2205bd02-e0bd-8b8e-46b2-a899fcaf683b\",\n \"name\": \"ROLE_API\"\n },\n {\n \"id\": \"urn:uuid:cc37ab06-0183-69d9-e115-24c67d78d3f0\",\n \"name\": \"ROLE_USER\"\n }\n ],\n \"enabled\": false\n },\n {\n \"email\": \"et Excepteur ullamco\",\n \"firstName\": \"nostrud \",\n \"lastName\": \"dolor dolore elit\",\n \"phone\": \"956293987\",\n \"username\": \"g6j6z1gtwc\",\n \"id\": \"81b9aa12-e539-07ef-0c37-1130cbdd309a\",\n \"address\": \"sunt nostrud anim sed\",\n \"city\": \"quis aliquip veniam officia\",\n \"state\": \"veniam ex\",\n \"zip\": \"dolore occaecat Lorem\",\n \"country\": \"non occaecat\",\n \"authorities\": [\n {\n \"id\": \"2407d3eb-4371-d06c-3a87-a657112a8110\",\n \"name\": \"ROLE_API\"\n },\n {\n \"id\": \"urn:uuid:f282f7db-cc8d-3812-70d7-09151aa57557\",\n \"name\": \"ROLE_TEST\"\n }\n ],\n \"enabled\": true\n }\n]" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "GET", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "url": { + "raw": "{{baseUrl}}/api/v3/users?keywords=nostrud ex&offset=21300640&limit=21300640", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "query": [ + { + "key": "keywords", + "value": "nostrud ex" + }, + { + "key": "offset", + "value": "21300640" + }, + { + "key": "limit", + "value": "21300640" + } + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + }, + { + "name": "Create a new user", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + }, + "description": "Creates a new user" + }, + "response": [ + { + "name": "Success", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"email\": \"nostrud ullamco ipsum\",\n \"firstName\": \"officia\",\n \"lastName\": \"dolore culpa sunt\",\n \"phone\": \"050345789735\",\n \"username\": \"y4kwf\",\n \"id\": \"urn:uuid:19f9d5fc-4530-0350-2a10-d427472c785a\",\n \"address\": \"incididunt sed aliqua\",\n \"city\": \"deserunt id aliquip\",\n \"state\": \"commodo aute nulla magna Lorem\",\n \"zip\": \"est irure magna sunt\",\n \"country\": \"id consequat\",\n \"authorities\": [\n {\n \"id\": \"a035d52a-b947-81e3-2a27-771dc51938ee\",\n \"name\": \"ROLE_TEST\"\n },\n {\n \"id\": \"urn:uuid:d7a2a7dd-7e17-486d-7899-114311f56891\",\n \"name\": \"ROLE_CUSTOMER\"\n }\n ],\n \"enabled\": true\n}" + }, + { + "name": "Bad Request", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Bad Request", + "code": 400, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Unauthorized", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Unauthorized", + "code": 401, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Forbidden", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Forbidden", + "code": 403, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "User Already Exists", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Conflict", + "code": 409, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + }, + { + "name": "Internal Server Error", + "originalRequest": { + "method": "POST", + "header": [ + { + "description": "Added as a part of security scheme: bearer", + "key": "Authorization", + "value": "Bearer " + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"occaecat\",\n \"firstName\": \"elit aute ullamco irure\",\n \"lastName\": \"sunt non amet\",\n \"phone\": \"31861069\",\n \"username\": \"4edketw0\",\n \"id\": \"urn:uuid:d4d037ed-1e66-37ee-96ae-ba97fb9a5098\",\n \"password\": \"est cupidatat\",\n \"confirmPassword\": \"Lorem velit dolore\",\n \"address\": \"enim aute\",\n \"city\": \"consectetur aliquip\",\n \"state\": \"velit nulla dolore consectetur\",\n \"zip\": \"velit mollit\",\n \"country\": \"minim\",\n \"dateCreated\": \"1975-09-15T12:49:46.736Z\",\n \"authorities\": [\n {\n \"id\": \"urn:uuid:aef9ea8c-e30b-2e7a-56fc-a7a90f552fd5\",\n \"name\": \"ROLE_CUSTOMER\"\n },\n {\n \"id\": \"72a1a13d-ee22-e4f3-a1ef-69edb9c067df\",\n \"name\": \"ROLE_ADMIN\"\n }\n ],\n \"enabled\": false\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/api/v3/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v3", + "users" + ], + "variable": [ + { + "key": "protocol", + "value": "{{protocol}}", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "{{environment}}", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + } + ] + } + }, + "status": "Internal Server Error", + "code": 500, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"success\": false,\n \"timestamp\": \"1964-10-03T17:08:53.142Z\",\n \"errors\": [\n \"sed Ut eiusmod id\",\n \"in est dolore\"\n ]\n}" + } + ] + } + ] + } + ] + } + ], + "variable": [ + { + "key": "protocol", + "value": "https", + "type": "any", + "description": " (This can only be one of http,https)" + }, + { + "key": "environment", + "value": "iwa.onfortify.com", + "type": "any", + "description": " (This can only be one of localhost:8888,iwa.onfortify.com)" + }, + { + "key": "baseUrl", + "value": "{{protocol}}://{{environment}}", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/etc/iwa_dev_cft.yaml b/etc/iwa_dev_cft.yaml new file mode 100644 index 0000000..d33d899 --- /dev/null +++ b/etc/iwa_dev_cft.yaml @@ -0,0 +1,85 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: + KeyName: + Description: Name of an existing EC2 KeyPair to enable SSH access to the instance + Type: 'AWS::EC2::KeyPair::KeyName' + ConstraintDescription: Can contain only ASCII characters. + InstanceType: + Description: WebServer EC2 instance type + Type: String + Default: m1.large + ConstraintDescription: must be a valid EC2 instance type. + InboundTraffic: + Description: The IP address CIDR range (x.x.x.x/x) to connect from your local machine. FYI, get your address using http://checkip.amazonaws.com/ + Type: String + MinLength: '9' + MaxLength: '18' + Default: '0.0.0.0/0' + AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})" + ConstraintDescription: Must be a valid CIDR range of the form x.x.x.x/x. +Conditions: + HasDBSnapshotIdentifier: !Not [!Equals [!Ref DBSnapshotIdentifier, '']] +Resources: + InstanceSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 3389 + ToPort: 3389 + CidrIp: + Ref: InboundTraffic + - IpProtocol: tcp + FromPort: 8580 + ToPort: 8580 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 8080 + ToPort: 8080 + CidrIp: 0.0.0.0/0 + DBEC2SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Open IWA DB for access + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 3306 + ToPort: 3306 + CidrIp: 0.0.0.0/0 + IWAJavaInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0cbcba412b2a46866 + InstanceType: m6i.4xlarge + SecurityGroups: + - Ref: InstanceSecurityGroup + Tags: + - Key: "Name" + Value: "IWA Java Application" + IWADBInstance: + Type: AWS::RDS::DBInstance + Properties: + DBName: iwa_db + DBInstanceClass: db.t2.small + AllocatedStorage: !If [HasDBSnapshotIdentifier, !Ref 'AWS::NoValue', !Ref DBAllocatedStorage] + Engine: MySQL + EngineVersion: "8.0.28" + MasterUsername: "root" + MasterUserPassword: "Password123!" + StorageEncrypted: false + SecurityGroups: + Ref: DBEC2SecurityGroup + Ec2Volume: + Type: AWS::EC2::Volume + Properties: + AutoEnableIO: 'false' + Size: '5' + AvailabilityZone: !Select [0, !GetAZs ''] + Tags: + - Key: "Name" + Value: "IWA file storage" +Outputs: + JDBCConnectionString: + Description: JDBC connection string for the database + Value: !Join ['', ['jdbc:mysql://', !GetAtt [IWADBInstance, Endpoint.Address], ':', !GetAtt [IWADBInstance, Endpoint.Port], /iwa_db]] \ No newline at end of file diff --git a/etc/iwa_prod_cft.yaml b/etc/iwa_prod_cft.yaml new file mode 100644 index 0000000..171516e --- /dev/null +++ b/etc/iwa_prod_cft.yaml @@ -0,0 +1,105 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: + KeyName: + Description: Name of an existing EC2 KeyPair to enable SSH access to the instance + Type: 'AWS::EC2::KeyPair::KeyName' + ConstraintDescription: Can contain only ASCII characters. + InstanceType: + Description: WebServer EC2 instance type + Type: String + Default: m1.large + ConstraintDescription: must be a valid EC2 instance type. + InboundTraffic: + Description: The IP address CIDR range (x.x.x.x/x) to connect from your local machine. FYI, get your address using http://checkip.amazonaws.com/ + Type: String + MinLength: '9' + MaxLength: '18' + Default: '0.0.0.0/0' + AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})" + ConstraintDescription: Must be a valid CIDR range of the form x.x.x.x/x. + RdsAuroraClusterParameterGroup: + Type: AWS::RDS::DBClusterParameterGroup + Properties: + Description: RdsAuroraClusterParameterGroup + Family: aurora5.6 + Parameters: + ParameterName: "time_zone" + ParameterValue: "UTC" +Resources: + InstanceSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 3389 + ToPort: 3389 + CidrIp: + Ref: InboundTraffic + - IpProtocol: tcp + FromPort: 8580 + ToPort: 8580 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 8080 + ToPort: 8080 + CidrIp: 0.0.0.0/0 + IWAJavaInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: ami-0cbcba412b2a46866 + InstanceType: m6i.4xlarge + SecurityGroups: + - Ref: InstanceSecurityGroup + Tags: + - Key: "Name" + Value: "IWA Java Application" + LogicalID: + Type: AWS::IAM::User + Properties: + LoginProfile: + Password: "ForgetmeNot123!" + PasswordResetRequired: false + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AWSCodeStarFullAccess + UserName: "iwa_user" + Ec2Volume: + Type: AWS::EC2::Volume + Properties: + AutoEnableIO: 'false' + Size: '5' + AvailabilityZone: !Select [0, !GetAZs ''] + Tags: + - Key: "Name" + Value: "IWA file storage" + RedshiftCluster: + Type: AWS::Redshift::Cluster + Properties: + ClusterType: "multi-node" + DBName: "iwa_dev" + MasterUserPassword: "Password123!" + MasterUsername: "iwaadmin" + NodeType: ds2.xlarge + NumberOfNodes: 2 + VpcSecurityGroupIds: + - Ref: SecurityGroup + PubliclyAccessible: 'true' + ClusterParameterGroupName: + Ref: RedShiftclusterParms + Tags: + - + Key: stack_name + Value: + Ref: AWS::StackName + RedShiftclusterParms: + Type: AWS::Redshift::clusterParameterGroup + Properties: + Description: "redshift parameter group" + ParameterGroupFamily: redshift-1.0 + Parameters: + - ParameterName: "require_ssl" + ParameterValue: "false" +Outputs: + JDBCConnectionString: + Description: JDBC connection string for the database + Value: !Join ['', ['jdbc:mysql://', !GetAtt [IWADBInstance, Endpoint.Address], ':', !GetAtt [IWADBInstance, Endpoint.Port], /iwa_db]] \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f9888ca --- /dev/null +++ b/pom.xml @@ -0,0 +1,625 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.1.8.RELEASE + + + + com.microfocus.example + iwa + 1.0-SNAPSHOT + ${project.artifactId} + ${packaging.type} + Insecure Web App (IWA) Pharmacy Direct + + + + GNU General Public License + https://opensource.org/licenses/gpl-license + + + + + UTF-8 + 1.8 + 1.8 + 1.8 + fod + 1.4.197 + 1.5.0 + 4.5.3 + 3.5.1 + 5.15.2 + 2.9.4 + 0.21.1 + 1.4.3 + 0.9.1 + 2.13.0 + 3.0.8 + 4.0.0-rc-2 + 4.12 + 21.2.3 + true + true + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-security + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + nz.net.ultraq.thymeleaf + thymeleaf-layout-dialect + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + com.h2database + h2 + compile + ${h2.version} + + + + org.passay + passay + ${passay.version} + + + + + org.webjars + bootstrap + ${bootstrap.version} + + + org.webjars + jquery + ${jquery.version} + + + org.webjars + font-awesome + ${font-awesome.version} + + + org.webjars + chartjs + ${chartjs.version} + + + org.webjars.npm + axios + ${axios.version} + + + + org.springdoc + springdoc-openapi-ui + ${springdoc-openapi.version} + + + org.springdoc + springdoc-openapi-security + ${springdoc-openapi.version} + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.module + jackson-module-parameter-names + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + + com.vonage + client + 6.1.0 + + + + org.codehaus.groovy + groovy + ${groovy.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + com.vaadin.external.google + android-json + + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + junit + junit + ${junit.version} + test + + + + org.apache.maven.wagon + wagon-http + 3.4.1 + + + + com.github.cliftonlabs + json-simple + 2.3.1 + + + + org.seleniumhq.selenium + selenium-java + ${selenium.version} + + + + org.apache.tika + tika-core + 1.18 + + + org.json + json + 20220320 + + + + + + + + + dev + + true + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + release + + + + + + jar + + true + + + jar + + + + + + war + + war + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + + + + + fortify + + war + + + + + src/main/configs + + + false + src/main/configs + + **/*.* + + + **/*.java + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + download-sources + package + + sources + + + true + + junit,org.easymock,${project.groupId} + + + false + + + + copy-dependencies + package + + copy-dependencies + + + target/classes/lib + + true + + junit,org.easymock,${project.groupId} + + + false + + + + + true + true + ${project.build.directory} + + + + + + maven-resources-plugin + 3.1.0 + + + copy-fortify-resources + package + + copy-resources + + + ${basedir}/${fod.dir} + + + ${basedir}/ + + src/**/* + pom.xml + Dockerfile* + docker-compose.yml + + + + src/test/**/* + + + + + ${basedir}/target/classes/lib + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-help-plugin + + + show-profiles + compile + + active-profiles + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + build-info + + build-info + + + + + ${project.artifactId} + + + dev + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.2 + + + build-classpath + generate-sources + + build-classpath + + + + + + + unpack-sources + generate-resources + + unpack-dependencies + + + false + true + + bootstrap,jquery + true + sources + jar + target/dependency + + + + + + + + maven-clean-plugin + + + + ${fod.dir} + + + src/main/ui/dist + + + target + + + target/classes + + + + + + clean-target-directory + clean + + clean + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + ${skip.unit.tests} + + **/Test*.java + **/*Test.java + **/*TestCase.java + + + **/PasswordConstraintValidatorTest*.java + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.2 + + ${skip.integration.tests} + + **/IT*.java + **/*IT.java + **/*ITCase.java + + + **/SeleniumFlowIT.java + + + + + integration-test + + integration-test + verify + + + + + + + + + + + + + com.heroku.sdk + heroku-maven-plugin + 3.0.2 + + insecurewebapp + 1.8 + ${project.build.directory}/${project.artifactId}.war + + + + + + + diff --git a/src/main/java/com/microfocus/example/Application.java b/src/main/java/com/microfocus/example/Application.java new file mode 100644 index 0000000..f2704f4 --- /dev/null +++ b/src/main/java/com/microfocus/example/Application.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example; + +import com.microfocus.example.config.StorageProperties; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@SpringBootApplication +@EnableConfigurationProperties(StorageProperties.class) +public class Application { + + public static void main(String... args) throws Exception { + SpringApplication.run(Application.class, args); + } + +} diff --git a/src/main/java/com/microfocus/example/ServletInitializer.java b/src/main/java/com/microfocus/example/ServletInitializer.java new file mode 100644 index 0000000..904cd04 --- /dev/null +++ b/src/main/java/com/microfocus/example/ServletInitializer.java @@ -0,0 +1,32 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Application.class); + } + +} diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiMessageController.java b/src/main/java/com/microfocus/example/api/controllers/ApiMessageController.java new file mode 100644 index 0000000..8a81134 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiMessageController.java @@ -0,0 +1,189 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.payload.request.MessageRequest; +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.entity.Message; +import com.microfocus.example.exception.MessageNotFoundException; +import com.microfocus.example.service.UserService; +import com.microfocus.example.payload.response.MessageResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing message information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/messages") +@Tag(name = "messages", description = "User message operations") +public class ApiMessageController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiMessageController.class); + + @Autowired + private UserService userService; + + @Operation(summary = "Finds messages by keyword(s)", description = "Keyword search by %keyword% format", tags = {"message"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MessageResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getMessagesByKeywords( + @Parameter(description = "Keyword(s) search for messages to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving messages by keyword(s)"); + // TODO: implement keywords, offset and limit + if (keywords.equals(Optional.empty())) { + return ResponseEntity.ok().body( + userService.getAllMessages().stream() + .map(MessageResponse::new) + .collect(Collectors.toList())); + } else { + return new ResponseEntity<>( + userService.getAllMessages().stream() + .map(MessageResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + } + + @Operation(summary = "Find message by Id", description = "Find a message by UUID", tags = {"message"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = MessageResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Message Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity getMessageById( + @Parameter(description = "UUID of the message to be found. Cannot be empty.", example = "6914e47d-2f0a-4deb-a712-12e7801e13e8", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving message with UUID: " + id); + if (!userService.messageExistsById(id)) + throw new MessageNotFoundException("Message with id: " + id.toString() + " does not exist."); + Optional message = userService.findMessageById(id); + return message.map(value -> new ResponseEntity<>(new MessageResponse(value), HttpStatus.OK)).orElse(null); + } + + @Operation(summary = "Create a new message", description = "Creates a new message for a user", tags = {"messages"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = MessageResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "Message Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createMessage( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody MessageRequest newMessage) { + log.debug("API::Creating new message: " + newMessage.toString()); + return new ResponseEntity<>(new MessageResponse(userService.saveMessageFromApi(null, newMessage)), HttpStatus.OK); + } + + @Operation(summary = "Update a message", description = "Update a users message", tags = {"messages"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = MessageResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Message Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateMessage( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody MessageRequest newMessage, + @Parameter(description = "UUID of the message to be updated. Cannot be empty.", example = "6914e47d-2f0a-4deb-a712-12e7801e13e8", required = true) @PathVariable("id") UUID id) { + log.debug("API::Updating message with UUID: " + id); + return new ResponseEntity<>(new MessageResponse(userService.saveMessageFromApi(id, newMessage)), HttpStatus.OK); + } + + @Operation(summary = "Delete a message", description = "Delete a users existing message", tags = {"messages"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Message Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteMessage( + @Parameter(description = "UUID of the message to be updated. Cannot be empty.", example = "6914e47d-2f0a-4deb-a712-12e7801e13e8", required = true) @PathVariable("id") UUID id) { + log.debug("API::Deleting message with UUID: " + id); + userService.deleteMessageById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + + @Operation(summary = "Get users unread message count", description = "Get a users unread message count by their UUID", tags = {"message"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = MessageResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "User Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/unread-count/{id}"}, produces = {"application/json"}) + public ResponseEntity getUnreadMessageCountById( + @Parameter(description = "UUID of the user to find messages for. Cannot be empty.", example = "32e7db01-86bc-4687-9ecb-d79b265ac14f", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving unread message count for user with UUID: " + id); + if (!userService.userExistsById(id)) + throw new MessageNotFoundException("User with id: " + id.toString() + " does not exist."); + return new ResponseEntity(userService.getUserUnreadMessageCount(id), HttpStatus.OK); + + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiOrderController.java b/src/main/java/com/microfocus/example/api/controllers/ApiOrderController.java new file mode 100644 index 0000000..c4b73c1 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiOrderController.java @@ -0,0 +1,192 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.entity.Order; +import com.microfocus.example.exception.OrderNotFoundException; +import com.microfocus.example.payload.request.OrderRequest; +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.payload.response.OrderResponse; +import com.microfocus.example.payload.response.ProductResponse; +import com.microfocus.example.service.ProductService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing order information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/orders") +@Tag(name = "orders", description = "Order operations") +public class ApiOrderController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiOrderController.class); + + @Autowired + private ProductService productService; + + @Operation(summary = "Find order by Id", description = "Find an order by UUID", tags = {"orders"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = OrderResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Order Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity findOrderById( + @Parameter(description = "UUID of the order to be found. Cannot be empty.", example = "c9b31f33-17a4-4fcd-927e-c14cdee32201", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving order with UUID: " + id); + if (!productService.orderExistsById(id)) + throw new OrderNotFoundException("Order with UUID: " + id.toString() + " does not exist."); + Optional order = productService.findOrderById(id); + return order.map(value -> new ResponseEntity<>(new OrderResponse(value), HttpStatus.OK)).orElse(null); + } + + @Operation(summary = "Find orders by keyword(s)", description = "Keyword search by %keyword% format", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProductResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getOrdersByKeywords( + @Parameter(description = "Keyword(s) search for orders to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving orders by keyword(s)"); + // TODO: implement keywords, offset and limit + if (keywords.equals(Optional.empty())) { + return ResponseEntity.ok().body( + productService.getAllOrders().stream() + .map(OrderResponse::new) + .collect(Collectors.toList())); + } else { + String k = (keywords.orElse("")); + Integer o = (offset.orElse(0)); + return new ResponseEntity<>( + productService.getAllOrders(o, k).stream() + .map(OrderResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + } + + @Operation(summary = "Get all orders", description = "get orders info", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProductResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/getOrderInfo"}, produces = {"application/json"}) + public ResponseEntity> getOrdersInfo() { + log.debug("API::Retrieving orders Info:"); + // TODO: implement keywords, offset and limit + return ResponseEntity.ok().body( + productService.getAllOrders().stream() + .map(OrderResponse::new) + .collect(Collectors.toList())); + + } + + @Operation(summary = "Create a new order", description = "Creates a new order", tags = {"orders"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = OrderResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "Order Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createOrder( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody OrderRequest newOrder) { + log.debug("API::Creating new order: " + newOrder.toString()); + return new ResponseEntity<>(new OrderResponse(productService.saveOrderFromApi(null, newOrder)), HttpStatus.OK); + } + + @Operation(summary = "Update an order", description = "Update an existing order", tags = {"orders"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = OrderResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Order Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateOrder( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody OrderRequest newOrder, + @Parameter(description = "UUID of the order to be updated. Cannot be empty.", example = "c9b31f33-17a4-4fcd-927e-c14cdee32201", required = true) @PathVariable("id") UUID id) { + log.debug("API::Updating order with UUID: " + id); + return new ResponseEntity<>(new OrderResponse(productService.saveOrderFromApi(id, newOrder)), HttpStatus.OK); + } + + @Operation(summary = "Delete a order", description = "Delete an order", tags = {"orders"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Order Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteOrder( + @Parameter(description = "UUID of the order to be updated. Cannot be empty.", example = "c9b31f33-17a4-4fcd-927e-c14cdee32201", required = true) @PathVariable("id") UUID id) { + log.debug("API::Deleting order with UUID: " + id); + productService.deleteOrderById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiProductController.java b/src/main/java/com/microfocus/example/api/controllers/ApiProductController.java new file mode 100644 index 0000000..6cce606 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiProductController.java @@ -0,0 +1,220 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.entity.Product; +import com.microfocus.example.exception.ProductNotFoundException; +import com.microfocus.example.payload.request.ProductRequest; +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.payload.response.ProductResponse; +import com.microfocus.example.service.ProductService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing product information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/products") +@Tag(name = "products", description = "Product operations") +public class ApiProductController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiProductController.class); + + @Autowired + private ProductService productService; + + @Operation(summary = "Find products by keyword(s)", description = "Keyword search by %keyword% format", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProductResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getProductsByKeywords( + @Parameter(description = "Keyword(s) search for products to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving products by keyword(s)"); + if (limit.isPresent()) { + productService.setPageSize(limit.orElse(productService.getPageSize())); + } + String k = (keywords.orElse("")); + Integer o = (offset.orElse(0)); + return new ResponseEntity<>( + productService.getAllProducts(o, k).stream() + .map(ProductResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + + @Operation(summary = "Find products by keyword(s) (no pagination)", description = "Keyword search by %keyword% format ", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProductResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/all"}, produces = {"application/json"}) + public ResponseEntity> getProductsByKeywordsNoPagination( + @Parameter(description = "Keyword(s) search for products to be found.") @RequestParam("keywords") Optional keywords) { + log.debug("API::Retrieving products by keyword(s)"); + productService.setPageSize(productService.getPageSize()); + + String k = (keywords.orElse("")); + Integer o = 0; + return new ResponseEntity<>( + productService.getAllProducts(o, k).stream() + .map(ProductResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + + @Operation(summary = "Find product by Id", description = "Find a product by UUID", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Product Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity findProductById( + @Parameter(description = "UUID of the product to be found. Cannot be empty.", example = "eec467c8-5de9-4c7c-8541-7b31614d31a0", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving product with UUID: " + id); + if (!productService.productExistsById(id)) + throw new ProductNotFoundException("Product with UUID: " + id.toString() + " does not exist."); + Optional product = productService.findProductById(id); + return product.map(value -> new ResponseEntity<>(new ProductResponse(value), HttpStatus.OK)).orElse(null); + } + + @Operation(summary = "Create a new product", description = "Creates a new product", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "Product Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createProduct( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody ProductRequest newProduct) { + log.debug("API::Creating new product: " + newProduct.toString()); + return new ResponseEntity<>(new ProductResponse(productService.saveProductFromApi(null, newProduct)), HttpStatus.OK); + } + + @Operation(summary = "Update a product", description = "Update an existing product", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Product Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateProduct( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody ProductRequest newProduct, + @Parameter(description = "UUID of the product to be updated. Cannot be empty.", example = "eec467c8-5de9-4c7c-8541-7b31614d31a0", required = true) @PathVariable("id") UUID id) { + log.debug("API::Updating product with UUID: " + id); + return new ResponseEntity<>(new ProductResponse(productService.saveProductFromApi(id, newProduct)), HttpStatus.OK); + } + + @Operation(summary = "Delete a product", description = "Delete a product", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Product Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteProduct( + @Parameter(description = "UUID of the product to be updated. Cannot be empty.", example = "eec467c8-5de9-4c7c-8541-7b31614d31a0", required = true) @PathVariable("id") UUID id) { + log.debug("API::Deleting product with UUID: " + id); + productService.deleteProductById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + + + @Operation(summary = "Get product image by Id", description = "Get a product's image by its UUID", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Product Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}/image"}, produces = {"application/json"}) + public ResponseEntity findProductImageById( + @Parameter(description = "UUID of the product. Cannot be empty.", example = "eec467c8-5de9-4c7c-8541-7b31614d31a0", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving product image for UUID: " + id); + if (!productService.productExistsById(id)) + throw new ProductNotFoundException("Product with UUID: " + id.toString() + " does not exist."); + Optional product = productService.findProductById(id); + MediaType contentType = MediaType.IMAGE_JPEG; + InputStream in = getClass().getResourceAsStream("/static/img/products/"+product.get().getImage()); + if (in == null) { + log.error("Could not find resource /static/img/products/{}", product.get().getImage()); + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok() + .contentType(contentType) + .body(new InputStreamResource(in)); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiReviewController.java b/src/main/java/com/microfocus/example/api/controllers/ApiReviewController.java new file mode 100644 index 0000000..1555f61 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiReviewController.java @@ -0,0 +1,174 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.entity.Review; +import com.microfocus.example.exception.ReviewNotFoundException; +import com.microfocus.example.payload.request.ReviewRequest; +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.payload.response.ProductResponse; +import com.microfocus.example.payload.response.ReviewResponse; +import com.microfocus.example.service.ProductService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing product review information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/reviews") +@Tag(name = "reviews", description = "Review operations") +public class ApiReviewController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiReviewController.class); + + @Autowired + private ProductService productService; + + @Operation(summary = "Find review by Id", description = "Find a review by UUID", tags = {"reviews"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ReviewResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Review Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity findReviewById( + @Parameter(description = "UUID of the review to be found. Cannot be empty.", example = "822f734a-3d13-4ebc-bff6-9c36d29866a6", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving review with UUID: " + id); + if (!productService.reviewExistsById(id)) + throw new ReviewNotFoundException("Review with UUID: " + id.toString() + " does not exist."); + Optional review = productService.findReviewById(id); + return review.map(value -> new ResponseEntity<>(new ReviewResponse(value), HttpStatus.OK)).orElse(null); + } + + @Operation(summary = "Find reviews by product and keyword(s)", description = "Product and keyword search by %keyword% format", tags = {"products"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProductResponse.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getReviewsByKeywords( + @Parameter(description = "UUID of the product to find reviews for.", example = "eec467c8-5de9-4c7c-8541-7b31614d31a0") @RequestParam("pid") Optional pid, + @Parameter(description = "Keyword(s) search for reviews to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving reviews by keyword(s)" + (pid.map(value -> " for product id:" + value).orElse(""))); + if (limit.isPresent()) { + productService.setPageSize(limit.get()); + } else { + productService.setPageSize(50); + } + String k = (keywords.orElse("")); + Integer o = (offset.orElse(0)); + return pid.map(uuid -> new ResponseEntity<>( + productService.getProductReviews(uuid, o, k).stream() + .map(ReviewResponse::new) + .collect(Collectors.toList()), HttpStatus.OK)).orElseGet(() -> new ResponseEntity<>( + productService.getReviews(o, k).stream() + .map(ReviewResponse::new) + .collect(Collectors.toList()), HttpStatus.OK)); + } + + @Operation(summary = "Create a new review", description = "Creates a new review", tags = {"reviews"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ReviewResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "Review Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createReview( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody ReviewRequest newReview) { + log.debug("API::Creating new review: " + newReview.toString()); + return new ResponseEntity<>(new ReviewResponse(productService.saveReviewFromApi(null, newReview)), HttpStatus.OK); + } + + @Operation(summary = "Update an review", description = "Update an existing review", tags = {"reviews"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ReviewResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Review Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateReview( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody ReviewRequest newReview, + @Parameter(description = "UUID of the review to be updated. Cannot be empty.", example = "822f734a-3d13-4ebc-bff6-9c36d29866a6", required = true) @PathVariable("id") UUID id) { + log.debug("API::Updating review with UUID: " + id); + return new ResponseEntity<>(new ReviewResponse(productService.saveReviewFromApi(id, newReview)), HttpStatus.OK); + } + + @Operation(summary = "Delete a review", description = "Delete an review", tags = {"reviews"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Review Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteReview( + @Parameter(description = "UUID of the review to be updated. Cannot be empty.", example = "822f734a-3d13-4ebc-bff6-9c36d29866a6", required = true) @PathVariable("id") UUID id) { + log.debug("API::Deleting review with UUID: " + id); + productService.deleteReviewById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiRoleController.java b/src/main/java/com/microfocus/example/api/controllers/ApiRoleController.java new file mode 100644 index 0000000..d136488 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiRoleController.java @@ -0,0 +1,161 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.entity.Authority; +import com.microfocus.example.exception.RoleNotFoundException; +import com.microfocus.example.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; + +/** + * A RESTFul controller for accessing role information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/roles") +@Tag(name = "roles", description = "Role operations") +public class ApiRoleController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiRoleController.class); + + @Autowired + private UserService roleService; + + @Operation(summary = "Find roles by keyword(s)", description = "Keyword search by %keyword% format", tags = {"roles"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Authority.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getRolesByKeywords( + @Parameter(description = "Keyword(s) search for roles to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving roles by keyword(s)"); + // TODO: implement keywords, offset and limit + if (keywords.equals(Optional.empty())) { + return ResponseEntity.ok().body(roleService.getAllRoles()); + } else { + return new ResponseEntity<>(roleService.getAllRoles(), HttpStatus.OK); + } + } + + @Operation(summary = "Find role Id", description = "Find a specific role by its UUID", tags = {"roles"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = Authority.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Message Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity findRoleById( + @Parameter(description = "UUID of the role to be found. Cannot be empty.", example = "6bdd6188-d659-4390-8d37-8f090d2ed69a", required = true) @PathVariable("id") Integer id) { + log.debug("API::Retrieving role with UUID: " + id); + if (!roleService.roleExistsById(id)) + throw new RoleNotFoundException("Role with UUID: " + id.toString() + " does not exist."); + Optional role = roleService.findRoleById(id); + return new ResponseEntity<>(role.orElse(null), HttpStatus.OK); + } + + @Operation(summary = "Create a new role", description = "Creates a new role", tags = {"roles"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = Authority.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "Role Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createRole( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody Authority newRole) { + //newRole.setId(0); // set to 0 for sequence id generation + log.debug("API::Creating new role: " + newRole.toString()); + return new ResponseEntity<>(roleService.saveRole(newRole), HttpStatus.OK); + } + + @Operation(summary = "Update a role", description = "Update an existing role", tags = {"roles"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = Authority.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Role Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateRole( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody Authority newRole, + @Parameter(description = "UUID of the role to be updated. Cannot be empty.", example = "6bdd6188-d659-4390-8d37-8f090d2ed69a", required = true) @PathVariable("id") Integer id) { + log.debug("API::Updating role with UUID: " + id); + return new ResponseEntity<>(roleService.saveRole(newRole), HttpStatus.OK); + } + + @Operation(summary = "Delete a role", description = "Delete a role", tags = {"roles"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Role not found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteRole( + @Parameter(description = "UUID of the role to be updated. Cannot be empty.", example = "6bdd6188-d659-4390-8d37-8f090d2ed69a", required = true) @PathVariable("id") Integer id) { + log.debug("API::Deleting role with UUID: " + id); + roleService.deleteRoleById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiSiteController.java b/src/main/java/com/microfocus/example/api/controllers/ApiSiteController.java new file mode 100644 index 0000000..762bde1 --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiSiteController.java @@ -0,0 +1,291 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.RefreshToken; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.ApiBadCredentialsException; +import com.microfocus.example.exception.ApiRefreshTokenException; +import com.microfocus.example.payload.request.LoginRequest; +import com.microfocus.example.payload.request.RefreshTokenRequest; +import com.microfocus.example.payload.request.RegisterUserRequest; +import com.microfocus.example.payload.request.SubscribeUserRequest; +import com.microfocus.example.payload.response.*; +import com.microfocus.example.repository.RoleRepository; +import com.microfocus.example.repository.UserRepository; +import com.microfocus.example.service.RefreshTokenService; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.JwtUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing site information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/site") +@Tag(name = "site", description = "Site operations") +public class ApiSiteController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiSiteController.class); + + @Autowired + private UserService userService; + + @Autowired + AuthenticationManager authenticationManager; + + @Autowired + RefreshTokenService refreshTokenService; + + @Autowired + UserRepository userRepository; + + @Autowired + RoleRepository roleRepository; + + @Bean("ApiSiteControllerPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Autowired + JwtUtils jwtUtils; + + public class SiteStatus { + private String health; + private String motd; + + SiteStatus() { } + SiteStatus(String health, String motd) { + this.health = health; + this.motd = motd; + } + + public String getHealth() { + return health; + } + public void setHealth(String health) { + this.health = health; + } + public String getMotd() { + return motd; + } + public void setMotd(String motd) { + this.motd = motd; + } + } + + @Operation(summary = "Get the site status", description = "Get the site message of the day", tags = {"users"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SiteStatus.class)))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/status"}, produces = {"application/json"}) + public ResponseEntity getSiteStatus() { + log.debug("API::Retrieving Site Status"); + SiteStatus siteStatus = new SiteStatus("GREEN", "The site is currently healthy"); + return ResponseEntity.ok().body(siteStatus); + } + + @Operation(summary = "Check if username is taken", description = "Check if a user with the specified username already exists in the site", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/username-already-exists/{username}"}, produces = {"application/json"}) + public ResponseEntity usernameIsTaken( + @Parameter(description = "Username to check. Cannot be empty.", example = "user1", required = true) @PathVariable("username") String username) { + log.debug("API::Checking for user with username: " + username); + Optional user = userService.findUserByUsername(username); + if (user.isPresent()) { + return new ResponseEntity(Boolean.TRUE, HttpStatus.OK); + } else { + return new ResponseEntity(Boolean.FALSE, HttpStatus.OK); + } + } + + @Operation(summary = "Check if email exists", description = "Check if a user with the specified email address already exists in the site", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/email-already-exists/{email}"}, produces = {"application/json"}) + public ResponseEntity emailIsTaken( + @Parameter(description = "Email address to check. Cannot be empty.", example = "user1@localhost.com", required = true) @PathVariable("email") String email) { + log.debug("API::Checking for user with email: " + email); + Optional user = userService.findUserByEmail(email); + if (user.isPresent()) { + return new ResponseEntity(Boolean.TRUE, HttpStatus.OK); + } else { + return new ResponseEntity(Boolean.FALSE, HttpStatus.OK); + } + } + + @Operation(summary = "Register a new user", description = "Register a new user with the site", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = RegisterUserResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "User Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {"/register-user"}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity registerUser( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody RegisterUserRequest newUser) { + log.debug("API::Registering new user: " + newUser.toString()); + RegisterUserResponse user = userService.registerUser(newUser); + ApiStatusResponse response = new ApiStatusResponse(); + if (user.getEmail().equals(newUser.getEmail())) response.setSuccess(true); + return new ResponseEntity<>(response, HttpStatus.CREATED); + } + + @Operation(summary = "Subscribe a new user", description = "Subscribe a new user to the newsletter", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = SubscribeUserResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "User Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {"/subscribe-user"}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.OK) + public ResponseEntity subscribeUser( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody SubscribeUserRequest newUser) { + log.debug("API::Subscribing a user to the newsletter: " + newUser.toString()); + SubscribeUserResponse user = userService.subscribeUser(newUser); + ApiStatusResponse response = new ApiStatusResponse(); + if ((user.getEmail().equals(newUser.getEmail()))) response.setSuccess(true); + return new ResponseEntity<>(response, HttpStatus.OK); + } + + @Operation(summary = "Sign in", description = "Sign in to the system", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {"/sign-in"}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.OK) + public ResponseEntity signIn(@Valid @RequestBody LoginRequest loginRequest, HttpServletResponse response) { + + Authentication authentication = null; + try { + authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); + } catch (final BadCredentialsException ex) { + throw new ApiBadCredentialsException(loginRequest.getUsername()); + } + + + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = jwtUtils.generateJwtToken(authentication); + + CustomUserDetails iwaUser = (CustomUserDetails) authentication.getPrincipal(); + User user = iwaUser.getUserDetails(); + List roles = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList()); + + Cookie jwtTokenCookie = new Cookie("jwtToken", jwt); + jwtTokenCookie.setSecure(false); + jwtTokenCookie.setHttpOnly(false); + response.addCookie(jwtTokenCookie); + + RefreshToken refreshToken = refreshTokenService.createRefreshToken(user.getId()); + + return ResponseEntity.ok(new JwtResponse(jwt, + refreshToken.getId().toString(), + jwtUtils.getExpirationFromJwtToken(jwt), + user.getId(), + user.getUsername(), + user.getEmail(), + roles)); + } + + @Operation(summary = "Refresh Token", description = "Refresh users JWT access token", tags = {"site"}) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {"/refresh-token"}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.OK) + public ResponseEntity refreshToken(@Valid @RequestBody RefreshTokenRequest refreshTokenRequest) { + + String requestRefreshToken = refreshTokenRequest.getRefreshToken(); + try{ + UUID uuid = UUID.fromString(requestRefreshToken); + log.debug("Refresh token request: " + uuid.toString()); + } catch (IllegalArgumentException ex){ + throw new ApiRefreshTokenException(requestRefreshToken, "Invalid refresh token format."); + } + + return refreshTokenService.findByToken(requestRefreshToken) + .map(refreshTokenService::verifyExpiration) + .map(RefreshToken::getUser) + .map(user -> { + String token = jwtUtils.generateJwtTokenFromUsername(user.getUsername()); + long tokenExpiration = jwtUtils.getExpirationFromJwtToken(token); + return ResponseEntity.ok(new RefreshTokenResponse(token, requestRefreshToken, tokenExpiration)); + }) + .orElseThrow(() -> new ApiRefreshTokenException(requestRefreshToken, + "Refresh token not found in database.")); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/controllers/ApiUserController.java b/src/main/java/com/microfocus/example/api/controllers/ApiUserController.java new file mode 100644 index 0000000..4e2752f --- /dev/null +++ b/src/main/java/com/microfocus/example/api/controllers/ApiUserController.java @@ -0,0 +1,190 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.controllers; + +import com.microfocus.example.payload.response.ApiStatusResponse; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.UserNotFoundException; +import com.microfocus.example.payload.response.UserResponse; +import com.microfocus.example.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * A RESTFul controller for accessing user information. + * + * @author Kevin A. Lee + */ +@RestController +@RequestMapping(value = "/api/v3/users") +@Tag(name = "users", description = "User operations") +public class ApiUserController { + + private static final org.slf4j.Logger log = LoggerFactory.getLogger(ApiUserController.class); + + @Autowired + private UserService userService; + + @Operation(summary = "List all users", description = "List all available users", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = User.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/all"}, produces = {"application/json"}) + public ResponseEntity> getAllUsers( + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Listing users"); + if (limit.isPresent()) { + userService.setPageSize(limit.orElse(userService.getPageSize())); + } + Integer o = (offset.orElse(0)); + return new ResponseEntity<>( + userService.getAllUsers(o, null).stream() + .map(UserResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + + @Operation(summary = "Find users by keyword(s)", description = "Keyword search by %keyword% format", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(array = @ArraySchema(schema = @Schema(implementation = User.class)))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {""}, produces = {"application/json"}) + public ResponseEntity> getUsersByKeywords( + @Parameter(description = "Keyword(s) search for users to be found.") @RequestParam("keywords") Optional keywords, + @Parameter(description = "Offset of the starting record. 0 indicates the first record.") @RequestParam("offset") Optional offset, + @Parameter(description = "Maximum records to return. The maximum value allowed is 50.") @RequestParam("limit") Optional limit) { + log.debug("API::Retrieving users by keyword(s)"); + if (limit.isPresent()) { + userService.setPageSize(limit.orElse(userService.getPageSize())); + } + String k = (keywords.orElse("")); + Integer o = (offset.orElse(0)); + return new ResponseEntity<>( + userService.getAllUsers(o, k).stream() + .map(UserResponse::new) + .collect(Collectors.toList()), HttpStatus.OK); + } + + @Operation(summary = "Find user by UUID", description = "Find a specific user by their UUID", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "User Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @GetMapping(value = {"/{id}"}, produces = {"application/json"}) + public ResponseEntity findUserById( + @Parameter(description = "UUID of the user to be found. Cannot be empty.", example = "db4cfab1-ff1d-4bca-a662-394771841383", required = true) @PathVariable("id") UUID id) { + log.debug("API::Retrieving user with UUID: " + id); + if (!userService.userExistsById(id)) + throw new UserNotFoundException("User with UUID: " + id.toString() + " does not exist."); + Optional user = userService.findUserById(id); + return user.map(value -> new ResponseEntity<>(new UserResponse(value), HttpStatus.OK)).orElse(null); + } + + @Operation(summary = "Create a new user", description = "Creates a new user", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "409", description = "User Already Exists", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PostMapping(value = {""}, produces = {"application/json"}, consumes = {"application/json"}) + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity createUser( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody User newUser) { + //newUser.setId(new UUID()); // set to 0 for sequence id generation + log.debug("API::Creating new user: " + newUser.toString()); + return new ResponseEntity<>(new UserResponse(userService.saveUser(newUser)), HttpStatus.OK); + } + + @Operation(summary = "Update a user", description = "Update an existing user", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "User Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + }) + @PutMapping(value = {"/{id}"}, produces = {"application/json"}, consumes = {"application/json"}) + public ResponseEntity updateUser( + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "") @Valid @RequestBody User newUser, + @Parameter(description = "UUID of the user to be updated. Cannot be empty.", example = "db4cfab1-ff1d-4bca-a662-394771841383", required = true) @PathVariable("id") UUID id) { + log.debug("API::Updating user with UUID: " + id); + return new ResponseEntity<>(new UserResponse(userService.saveUserFromApi(newUser)), HttpStatus.OK); + } + + @Operation(summary = "Delete a user", description = "Delete an existing user", tags = {"users"}, security = @SecurityRequirement(name = "JWT Authentication")) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Success", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad request", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "403", description = "Forbidden", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))), + @ApiResponse(responseCode = "404", description = "User Not Found", content = @Content(schema = @Schema(implementation = ApiStatusResponse.class))) + }) + @DeleteMapping (value = {"/{id}"}) + public ResponseEntity deleteUser( + @Parameter(description = "UUID of the user to be updated. Cannot be empty.", example = "db4cfab1-ff1d-4bca-a662-394771841383", required = true) @PathVariable("id") UUID id) { + log.debug("API::Deleting user with UUID: " + id); + userService.deleteUserById(id); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(true) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .build(); + return new ResponseEntity<>(apiStatusResponse, HttpStatus.OK); + } + +} + + diff --git a/src/main/java/com/microfocus/example/api/utils/ApiUtil.java b/src/main/java/com/microfocus/example/api/utils/ApiUtil.java new file mode 100644 index 0000000..3d3a33d --- /dev/null +++ b/src/main/java/com/microfocus/example/api/utils/ApiUtil.java @@ -0,0 +1,47 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.api.utils; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.server.ResponseStatusException; + +public class ApiUtil { + + public static void setExampleResponse(NativeWebRequest req, String contentType, String example) { + try { + req.getNativeResponse(HttpServletResponse.class).addHeader("Content-Type", contentType); + req.getNativeResponse(HttpServletResponse.class).getOutputStream().print(example); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void checkApiKey(NativeWebRequest req) { + if (!"1".equals(System.getenv("DISABLE_API_KEY")) && !"special-key".equals(req.getHeader("api_key"))) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing API key!"); + } + } +} diff --git a/src/main/java/com/microfocus/example/config/CorsConfiguration.java b/src/main/java/com/microfocus/example/config/CorsConfiguration.java new file mode 100644 index 0000000..80d74e2 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/CorsConfiguration.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfiguration implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") + .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS") + .exposedHeaders("Authorization"); + } +} diff --git a/src/main/java/com/microfocus/example/config/CustomMessageSourceConfiguration.java b/src/main/java/com/microfocus/example/config/CustomMessageSourceConfiguration.java new file mode 100644 index 0000000..14f47b4 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/CustomMessageSourceConfiguration.java @@ -0,0 +1,50 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +/** + * Custom message source configuration + * @author Kevin A. Lee + */ +@Configuration +public class CustomMessageSourceConfiguration { + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:messages"); + messageSource.setDefaultEncoding("UTF-8"); + return messageSource; + } + + @Bean + public LocalValidatorFactoryBean getValidator() { + LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); + bean.setValidationMessageSource(messageSource()); + return bean; + } + +} diff --git a/src/main/java/com/microfocus/example/config/FaviconConfiguration.java b/src/main/java/com/microfocus/example/config/FaviconConfiguration.java new file mode 100644 index 0000000..49de820 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/FaviconConfiguration.java @@ -0,0 +1,65 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import com.microfocus.example.web.controllers.admin.AdminDefaultController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Configure web browser favicon configuration for custom location + * @author Kevin A. Lee + */ +@Configuration +public class FaviconConfiguration { + + private static final Logger log = LoggerFactory.getLogger(FaviconConfiguration.class); + + @Value("${server.servlet.context-path}") + private String contextPath; + + @Bean("CustomFaviconHandlerMapping") + public SimpleUrlHandlerMapping faviconHandlerMapping() { + SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); + mapping.setOrder(Integer.MIN_VALUE); + mapping.setUrlMap(Collections.singletonMap(contextPath + "/img/favicons/favicon.png", + faviconRequestHandler())); + return mapping; + } + + @Bean("CustomerFaviconRequestHandler") + protected ResourceHttpRequestHandler faviconRequestHandler() { + ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); + requestHandler.setLocations(Arrays + . asList(new ClassPathResource("/"))); + return requestHandler; + } + +} diff --git a/src/main/java/com/microfocus/example/config/LocaleConfiguration.java b/src/main/java/com/microfocus/example/config/LocaleConfiguration.java new file mode 100644 index 0000000..3cef80b --- /dev/null +++ b/src/main/java/com/microfocus/example/config/LocaleConfiguration.java @@ -0,0 +1,34 @@ +package com.microfocus.example.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.support.RequestContextUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.Locale; + +@Configuration +public class LocaleConfiguration { + + public static final Locale DEFAULT_LOCALE = new Locale("en", "GB"); + + @Bean + public Locale getLocale() { + if (RequestContextHolder.getRequestAttributes() != null) { + final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder + .getRequestAttributes()).getRequest(); + final LocaleResolver localeResolver = RequestContextUtils + .getLocaleResolver(request); + if (localeResolver != null) { + Locale locale = localeResolver.resolveLocale(request); + if (locale != null) { + return locale; + } + } + } + return DEFAULT_LOCALE; + } +} diff --git a/src/main/java/com/microfocus/example/config/OpenApi30Configuration.java b/src/main/java/com/microfocus/example/config/OpenApi30Configuration.java new file mode 100644 index 0000000..eca7860 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/OpenApi30Configuration.java @@ -0,0 +1,70 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.servers.ServerVariable; +import org.springframework.context.annotation.Configuration; + +@Configuration +@OpenAPIDefinition(info = @Info( + title = "Insecure Web App (IWA) API", + description = "This is the REST API for Insecure Web App (IWA) Pharmacy Direct. You can select a development or production server to test the API. " + + "Most operations require authentication via a user specific JWT token. To retrieve a JWT token for a user you can use the " + + " '/authentication/sign-in' operation below and then copy the value of the 'accessToken' field. This value can then be " + + "entered when you click on the 'Authorize' button or lock icons.", + version = "v3") + /*servers = @Server( + url = "{protocol}://{environment}", + variables = { + @ServerVariable( + name = "protocol", + allowableValues = {"http","https"}, + defaultValue = "https"), + @ServerVariable( + name = "environment", + allowableValues = {"localhost:8080", "localhost:8888", "iwa.onfortify.com"}, + defaultValue = "localhost:8080" + ) + } + + )*/ +) +/*@SecurityScheme( + name = "basicAuth", + type = SecuritySchemeType.HTTP, + scheme = "basic" +)*/ +@SecurityScheme( + name = "JWT Authentication", + description = "Type into the textbox: {your JWT token}.", + type = SecuritySchemeType.HTTP, + bearerFormat = "JWT", + in = SecuritySchemeIn.HEADER, + scheme = "bearer" +) +public class OpenApi30Configuration { + +} diff --git a/src/main/java/com/microfocus/example/config/StorageProperties.java b/src/main/java/com/microfocus/example/config/StorageProperties.java new file mode 100644 index 0000000..1b44652 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/StorageProperties.java @@ -0,0 +1,23 @@ +package com.microfocus.example.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.io.File; + +@ConfigurationProperties("storage") +public class StorageProperties { + + /** + * Folder location for storing files + */ + private String location = System.getProperty("user.home") + File.separatorChar + "upload-dir"; + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + +} diff --git a/src/main/java/com/microfocus/example/config/WebConfigConfiguration.java b/src/main/java/com/microfocus/example/config/WebConfigConfiguration.java new file mode 100644 index 0000000..e1d1938 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/WebConfigConfiguration.java @@ -0,0 +1,43 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import org.h2.server.web.WebServlet; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Custom web application configuration, including database console access + * @author Kevin A. Lee + */ +@Configuration +public class WebConfigConfiguration { + + @SuppressWarnings("rawtypes") + @Bean + ServletRegistrationBean h2servletRegistration(){ + @SuppressWarnings("unchecked") + ServletRegistrationBean registrationBean = new ServletRegistrationBean( new WebServlet()); + registrationBean.addUrlMappings("/console/*"); + return registrationBean; + } + +} diff --git a/src/main/java/com/microfocus/example/config/WebSecurityConfiguration.java b/src/main/java/com/microfocus/example/config/WebSecurityConfiguration.java new file mode 100644 index 0000000..98f61c6 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/WebSecurityConfiguration.java @@ -0,0 +1,246 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config; + +import com.microfocus.example.config.handlers.*; +import com.microfocus.example.service.CustomUserDetailsService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.firewall.DefaultHttpFirewall; +import org.springframework.security.web.firewall.HttpFirewall; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +/** + * Configure Spring Security for custom application + * @author Kevin A. Lee + */ +@Configuration +@EnableWebSecurity +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + private static final Logger log = LoggerFactory.getLogger(WebSecurityConfiguration.class); + + public static final String REALM_NAME = "IWA"; + + @Autowired + private CustomUserDetailsService userDetailsService; + + @Autowired + private BasicAuthenticationEntryPointCustom basicAuthenticationEntryPoint; + + @Autowired + private ApiAccessDeniedHandler apiAccessDeniedHandler; + + @Autowired + private AuthenticationEntryPointJwt unauthorizedHandler; + + @Bean + public AuthenticationTokenFilter authenticationJwtTokenFilter() { + return new AuthenticationTokenFilter(); + } + + @Bean + public HttpMethodOverrideHeaderFilter httpMethodOverrideHeaderFilter() { + return new HttpMethodOverrideHeaderFilter(); + } + + @Autowired + private SessionRegistry sessionRegistry; + + @Value("${spring.profiles.active:Unknown}") + private String activeProfile; + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Configuration + @Order(1) + public class ApiConfigurationAdapter extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + + /*http.cors().and().csrf().disable() + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + .authorizeRequests().antMatchers("/api/auth/**").permitAll() + .antMatchers("/api/test/**").permitAll() + .anyRequest().authenticated();*/ + + httpSecurity.antMatcher("/api/**") + .authorizeRequests() + .antMatchers("/api/v3/site/**").permitAll() + .antMatchers(HttpMethod.GET, "/api/v3/users/unread-message-count").permitAll() + .antMatchers(HttpMethod.PUT, "/api/v3/users/*").permitAll() + .antMatchers(HttpMethod.GET,"/api/v3/products").permitAll() + .antMatchers(HttpMethod.GET,"/api/v3/products/**").permitAll() + .antMatchers(HttpMethod.GET, "/api/v3/reviews").permitAll() + .antMatchers(HttpMethod.GET, "/api/v3/reviews/**").permitAll() + .antMatchers(HttpMethod.GET, "/api/**").authenticated() + .antMatchers(HttpMethod.DELETE, "/api/**").hasAnyRole("ADMIN", "API") + .antMatchers(HttpMethod.POST, "/api/**").hasAnyRole("ADMIN", "API") + .antMatchers(HttpMethod.PUT, "/api/**").hasAnyRole("ADMIN", "API") + .antMatchers(HttpMethod.PATCH, "/api/**").hasAnyRole("ADMIN", "API") + .and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler) + .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + //.and().httpBasic().authenticationEntryPoint(basicAuthenticationEntryPoint) + .and().exceptionHandling().accessDeniedHandler(apiAccessDeniedHandler) + .and().csrf().disable(); + + httpSecurity.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + + } + + } + + @Configuration + @Order(2) + public class UserConfigurationAdapter extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + if (activeProfile.contains("dev")) { + log.info("Running development profile"); + httpSecurity.csrf().disable(); + httpSecurity.headers().frameOptions().disable(); + httpSecurity.cors().disable(); + httpSecurity.headers().xssProtection().disable(); + } + + /* + http.headers() + .contentSecurityPolicy("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/"); + */ + + httpSecurity.authorizeRequests() + .antMatchers("/", + "/products/**", + "/services/**", + "/prescriptions/**", + "/advice/**", + "/cart", + "/login", + "/logout", + "/user/register", + "/user/files/download/unverified", + "/backdoor", + "/swagger-resources/**", + "/swagger-ui/**", + "/swagger-ui.html", + "/v3/api-docs/**", + "/console/*", + "/favicon.ico", + "/js/**/*", + "/css/**/*", + "/fonts/**/*", + "/img/**/*", + "/webjars/**/*").permitAll() + // Only admin can access /admin portal + .antMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().fullyAuthenticated(); + + httpSecurity.authorizeRequests().and() + .exceptionHandling() + .accessDeniedPage("/access-denied"); + + httpSecurity.authorizeRequests().and().formLogin() + .loginProcessingUrl("/j_spring_security_check") + .successHandler(CustomAuthenticationSuccessHandler()) + .loginPage("/login") + .failureUrl("/login?error=true") + .usernameParameter("email") + .passwordParameter("password") + .permitAll(); + + httpSecurity.authorizeRequests().and().logout() + .invalidateHttpSession(true) + .clearAuthentication(true) + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .logoutSuccessUrl("/login?logout") + .permitAll(); + + httpSecurity.sessionManagement().maximumSessions(10) + .sessionRegistry(sessionRegistry()) + .expiredUrl("/login?expire"); + + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) { + auth.authenticationProvider(authenticationProvider()); + } + + @Bean + public AuthenticationSuccessHandler CustomAuthenticationSuccessHandler(){ + return new CustomAuthenticationSuccessHandler(); + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + @Bean + public HttpFirewall getHttpFirewall() { + return new DefaultHttpFirewall(); + } + + } + + @Bean("WebSecurityConfigurationPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/ApiAccessDeniedHandler.java b/src/main/java/com/microfocus/example/config/handlers/ApiAccessDeniedHandler.java new file mode 100644 index 0000000..3e91502 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/ApiAccessDeniedHandler.java @@ -0,0 +1,69 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.microfocus.example.payload.response.ApiStatusResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; + +@Component +public class ApiAccessDeniedHandler implements AccessDeniedHandler { + + private static final Logger log = LoggerFactory.getLogger(ApiAccessDeniedHandler.class); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) + throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + ResponseEntity apiError = new ResponseEntity(apiStatusResponse, HttpStatus.FORBIDDEN); + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + String jsonString = mapper.writeValueAsString(apiError.getBody()); + PrintWriter writer = response.getWriter(); + writer.println(jsonString); + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/AuthenticationEntryPointJwt.java b/src/main/java/com/microfocus/example/config/handlers/AuthenticationEntryPointJwt.java new file mode 100644 index 0000000..86f5c5d --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/AuthenticationEntryPointJwt.java @@ -0,0 +1,72 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.microfocus.example.payload.response.ApiStatusResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +@Component +public class AuthenticationEntryPointJwt implements AuthenticationEntryPoint { + + private static final Logger logger = LoggerFactory.getLogger(AuthenticationEntryPointJwt.class); + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException ex) throws IOException, ServletException { + //logger.error("Unauthorized error: {}", authException.getMessage()); + //response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + ResponseEntity apiError = new ResponseEntity(apiStatusResponse, HttpStatus.UNAUTHORIZED); + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + String jsonString = mapper.writeValueAsString(apiError.getBody()); + PrintWriter writer = response.getWriter(); + writer.println(jsonString); + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/AuthenticationTokenFilter.java b/src/main/java/com/microfocus/example/config/handlers/AuthenticationTokenFilter.java new file mode 100644 index 0000000..ed1e571 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/AuthenticationTokenFilter.java @@ -0,0 +1,83 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.microfocus.example.service.CustomUserDetailsService; +import com.microfocus.example.utils.JwtUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +public class AuthenticationTokenFilter extends OncePerRequestFilter { + + @Autowired + private JwtUtils jwtUtils; + + @Autowired + private CustomUserDetailsService userDetailsService; + + private static final Logger log = LoggerFactory.getLogger(AuthenticationTokenFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + String jwt = parseJwt(request); + if (jwt != null && jwtUtils.validateJwtToken(jwt)) { + String username = jwtUtils.getUserNameFromJwtToken(jwt); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception e) { + logger.error("Cannot set user authentication: {}", e); + } + + filterChain.doFilter(request, response); + } + + private String parseJwt(HttpServletRequest request) { + String headerAuth = request.getHeader("Authorization"); + + if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { + log.debug("Found jwtToken in header: " + headerAuth.substring(7, headerAuth.length())); + return headerAuth.substring(7, headerAuth.length()); + } + + return null; + } +} diff --git a/src/main/java/com/microfocus/example/config/handlers/BasicAuthenticationEntryPointCustom.java b/src/main/java/com/microfocus/example/config/handlers/BasicAuthenticationEntryPointCustom.java new file mode 100644 index 0000000..e0c84d4 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/BasicAuthenticationEntryPointCustom.java @@ -0,0 +1,77 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.microfocus.example.config.WebSecurityConfiguration; +import com.microfocus.example.payload.response.ApiStatusResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; + +@Component +public class BasicAuthenticationEntryPointCustom extends BasicAuthenticationEntryPoint { + + private static final Logger log = LoggerFactory.getLogger(BasicAuthenticationEntryPointCustom.class); + + @Override + public void commence( + HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) + throws IOException { + response.addHeader("WWW-Authenticate", "Basic realm='" + getRealmName() + "'"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + ResponseEntity apiError = new ResponseEntity(apiStatusResponse, HttpStatus.UNAUTHORIZED); + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + String jsonString = mapper.writeValueAsString(apiError.getBody()); + PrintWriter writer = response.getWriter(); + writer.println(jsonString); + } + + @Override + public void afterPropertiesSet() { + setRealmName(WebSecurityConfiguration.REALM_NAME); + //super.afterPropertiesSet(); + } +} diff --git a/src/main/java/com/microfocus/example/config/handlers/CustomAuthenticationSuccessHandler.java b/src/main/java/com/microfocus/example/config/handlers/CustomAuthenticationSuccessHandler.java new file mode 100644 index 0000000..4a5fabb --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/CustomAuthenticationSuccessHandler.java @@ -0,0 +1,157 @@ +/* + Insecure Web App (IWA) + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.utils.JwtUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.WebAttributes; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Custom Url Authentication Success Handler + * @author Kevin A. Lee + */ +public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + private static final Logger log = LoggerFactory.getLogger(CustomAuthenticationSuccessHandler.class); + + private static final String VERIFICATION_URL = "/verify"; + private static final String USER_HOME_URL = "/user"; + private static final String ADMIN_HOME_URL = "/admin"; + private static final String INDEX_URL = "/"; + + @Autowired + private JwtUtils jwtUtils; + + private static RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, + HttpServletResponse response, Authentication authentication) + throws IOException { + + HttpSession session = request.getSession(false); + + CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); + String mobile = customUserDetails.getMobile(); + boolean isAdmin = customUserDetails.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); + + if (isAdmin) { + log.debug("User is ADMIN, bypassing verification"); + bypassVerification(request, response, authentication); + } else if (mobile.isEmpty() || !requestAndRegisterVerification(mobile)) { + log.debug("No mobile phone provided, bypassing verification"); + bypassVerification(request, response, authentication); + } else if (!mobile.isEmpty() && requestAndRegisterVerification(mobile)) { + log.debug("Using users mobile number for verification: " + mobile); + session.setAttribute("mobileDigits", + mobile.length() > 2 ? mobile.substring(mobile.length() - 2) : mobile); + redirectStrategy.sendRedirect(request, response, VERIFICATION_URL); + } else { + String targetUrl = getTargetUrl(request, response, authentication); + log.debug("Redirecting to: " + targetUrl); + redirectStrategy.sendRedirect(request, response, targetUrl); + clearAuthenticationAttributes(request); + } + } + + public static String getTargetUrl(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) { + + HttpSession session = request.getSession(false); + CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); + boolean isAdmin = customUserDetails.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN")); + boolean isUser = !isAdmin; + String targetUrl = INDEX_URL; + + if (isAdmin) { + targetUrl = ADMIN_HOME_URL; + } else { + String loginReferer = (String) session.getAttribute("loginReferer"); + if (loginReferer == null || loginReferer.isEmpty()) { + log.debug("No loginReferer; redirecting to users home page"); + targetUrl = USER_HOME_URL; + } else { + targetUrl = loginReferer; + String targetPath = null; + try { + targetPath = new URL(targetUrl).getPath(); + } catch (MalformedURLException ex) { + log.error(ex.getLocalizedMessage()); + } + if (targetUrl.contains("?")) targetUrl = targetUrl.substring(0, targetUrl.indexOf("?")); + if (targetPath.endsWith("/cart")) { + targetUrl = targetUrl.replace("/cart", "/cart/checkout"); + } else if (targetPath.endsWith("/login")) { + targetUrl = targetUrl.replace("/login", "/user"); + } else if (targetPath.endsWith("/register")) { + targetUrl = targetUrl.replace("/register", "/"); + } else if (targetPath.equals("/")) { + targetUrl = targetUrl + "user"; + } + + } + } + return targetUrl; + } + + protected void clearAuthenticationAttributes(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session == null) { + return; + } + session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); + } + + private boolean requestAndRegisterVerification(String mobile) { + /*try { + return verificationService.requestVerification(mobile) != null; + } catch (VerificationRequestFailedException e) { + return false; + }*/ + return true; + } + + private void bypassVerification(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + String jwtToken = jwtUtils.generateAndSetSession(request, response, authentication); + String targetUrl = getTargetUrl(request, response, authentication); + log.debug("Redirecting to: " + targetUrl); + redirectStrategy.sendRedirect(request, response, targetUrl); + clearAuthenticationAttributes(request); + } + + protected RedirectStrategy getRedirectStrategy() { + return redirectStrategy; + } + + protected void setRedirectStrategy(RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; + } +} \ No newline at end of file diff --git a/src/main/java/com/microfocus/example/config/handlers/GlobalExceptionHandler.java b/src/main/java/com/microfocus/example/config/handlers/GlobalExceptionHandler.java new file mode 100644 index 0000000..747ebac --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/GlobalExceptionHandler.java @@ -0,0 +1,111 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.microfocus.example.exception.ApiBadCredentialsException; +import com.microfocus.example.exception.ApiRefreshTokenException; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.microfocus.example.exception.ServerErrorException; +import com.microfocus.example.exception.UserNotFoundException; +import com.microfocus.example.payload.response.ApiStatusResponse; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; + +import javax.servlet.http.HttpServletRequest; + +@ControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + public static final String DEFAULT_ERROR_VIEW = "error/default"; + public static final String SERVER_ERROR_VIEW = "error/500-internal-error"; + + @ExceptionHandler(ApiBadCredentialsException.class) + public ResponseEntity badCredentials(final ApiBadCredentialsException ex, final WebRequest request) { + log.debug("GlobalExceptionHandler::badCredentials"); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(ApiRefreshTokenException.class) + public ResponseEntity refreshToken(final ApiRefreshTokenException ex, final WebRequest request) { + log.debug("GlobalExceptionHandler::refreshToken"); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(ServerErrorException.class) + public ModelAndView handleServerErrorException(final ServerErrorException ex, final HttpServletRequest request) { + log.error("error:" + ex.toString()); + ModelAndView mav = new ModelAndView(); + mav.addObject("exception", ExceptionUtils.getStackTrace(ex)); + mav.addObject("url", request.getRequestURL()); + mav.setViewName(SERVER_ERROR_VIEW); + return mav; + } + + @ExceptionHandler({Exception.class}) + public ModelAndView handleAll(HttpServletRequest request, final Exception ex) throws Exception { + log.debug("GlobalExceptionHandler::handleAll"); + log.error("error:" + ex.toString()); + // If the exception is annotated with @ResponseStatus rethrow it and let + // AnnotationUtils is a Spring Framework utility class. + if (AnnotationUtils.findAnnotation + (ex.getClass(), ResponseStatus.class) != null) + throw ex; + + // Otherwise setup and send the user to a default error-view. + ModelAndView mav = new ModelAndView(); + mav.addObject("exception", ExceptionUtils.getStackTrace(ex)); + mav.addObject("url", request.getRequestURL()); + mav.setViewName(DEFAULT_ERROR_VIEW); + return mav; + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/GlobalRestExceptionHandler.java b/src/main/java/com/microfocus/example/config/handlers/GlobalRestExceptionHandler.java new file mode 100644 index 0000000..1839285 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/GlobalRestExceptionHandler.java @@ -0,0 +1,355 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.microfocus.example.exception.*; +import com.microfocus.example.payload.response.ApiStatusResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.TypeMismatchException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpMediaTypeNotSupportedException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.support.MissingServletRequestPartException; +import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +//@Order(Ordered.HIGHEST_PRECEDENCE) +@RestControllerAdvice +public class GlobalRestExceptionHandler extends ResponseEntityExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalRestExceptionHandler.class); + + // Custom exception handlers + + @ExceptionHandler(ApiBadCredentialsException.class) + public ResponseEntity badCredentials(final ApiBadCredentialsException ex, final WebRequest request) { + log.debug("GlobalRestExceptionHandler::badCredentials"); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(UserNotFoundException.class) + public ResponseEntity userNotFound(final UserNotFoundException ex, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(RoleNotFoundException.class) + public ResponseEntity roleNotFound(final RoleNotFoundException ex, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(MessageNotFoundException.class) + public ResponseEntity messageNotFound(final MessageNotFoundException ex, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(ProductNotFoundException.class) + public ResponseEntity productNotFound(final ProductNotFoundException ex, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, HttpStatus.NOT_FOUND); + } + + // Generic HTTP exception handlers + + // 400 + + @Override + protected ResponseEntity handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + + HttpHeaders httpHeader = new HttpHeaders(); + List acceptHeader = MediaType.parseMediaTypes(Arrays.asList(request.getHeaderValues(HttpHeaders.ACCEPT))); + + ArrayList errors = new ArrayList<>(); + for (final FieldError error : ex.getBindingResult().getFieldErrors()) { + errors.add(error.getField() + ": " + error.getDefaultMessage()); + } + for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) { + errors.add(error.getObjectName() + ": " + error.getDefaultMessage()); + } + + if (acceptHeader.stream().anyMatch(mediaType -> mediaType.isCompatibleWith(MediaType.APPLICATION_JSON))) { + httpHeader.setContentType(MediaType.APPLICATION_JSON); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + ResponseEntity apiError = new ResponseEntity(apiStatusResponse, HttpStatus.BAD_REQUEST); + return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request); + } else if (acceptHeader.stream().anyMatch(mediaType -> mediaType.isCompatibleWith(MediaType.TEXT_PLAIN))) { + httpHeader.setContentType(MediaType.TEXT_PLAIN); + return new ResponseEntity<>(errors.toString(), httpHeader, status); + } else { + return ResponseEntity.status(status).body(null); + } +} + + @Override + protected ResponseEntity handleBindException(final BindException ex, final HttpHeaders headers, + final HttpStatus status, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + for (final FieldError error : ex.getBindingResult().getFieldErrors()) { + errors.add(error.getField() + ": " + error.getDefaultMessage()); + } + for (final ObjectError error : ex.getBindingResult().getGlobalErrors()) { + errors.add(error.getObjectName() + ": " + error.getDefaultMessage()); + } + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + ResponseEntity apiError = new ResponseEntity(apiStatusResponse, HttpStatus.BAD_REQUEST); + return handleExceptionInternal(ex, apiError, headers, HttpStatus.BAD_REQUEST, request); + } + + @Override + protected ResponseEntity handleTypeMismatch(final TypeMismatchException ex, final HttpHeaders headers, + final HttpStatus status, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType(); + errors.add(error); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity<>(apiStatusResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + @Override + protected ResponseEntity handleMissingServletRequestPart(final MissingServletRequestPartException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final String error = ex.getRequestPartName() + " part is missing"; + errors.add(error); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity<>(apiStatusResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + @Override + protected ResponseEntity handleMissingServletRequestParameter(final MissingServletRequestParameterException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final String error = ex.getParameterName() + " parameter is missing"; + errors.add(error); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity<>(apiStatusResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler({MethodArgumentTypeMismatchException.class}) + public ResponseEntity handleMethodArgumentTypeMismatch(final MethodArgumentTypeMismatchException ex, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName(); + errors.add(error); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity<>(apiStatusResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler({ConstraintViolationException.class}) + public ResponseEntity handleConstraintViolation(final ConstraintViolationException ex, final WebRequest request) { + ArrayList errors = new ArrayList<>(); + for (final ConstraintViolation violation : ex.getConstraintViolations()) { + errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage()); + } + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST); + } + + // 404 + + @Override + protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + ArrayList errors = new ArrayList<>(); + //final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, new HttpHeaders(), HttpStatus.NOT_FOUND); + } + + @Override + protected ResponseEntity handleNoHandlerFoundException(final NoHandlerFoundException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL(); + errors.add(error); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, new HttpHeaders(), HttpStatus.NOT_FOUND); + } + + // 405 + + @Override + protected ResponseEntity handleHttpRequestMethodNotSupported(final HttpRequestMethodNotSupportedException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final StringBuilder builder = new StringBuilder(); + builder.append(ex.getMethod()); + builder.append(" method is not supported for this request. Supported methods are "); + ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " ")); + errors.add(builder.toString()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity<>(apiStatusResponse, new HttpHeaders(), HttpStatus.METHOD_NOT_ALLOWED); + } + + // 415 + + @Override + protected ResponseEntity handleHttpMediaTypeNotSupported(final HttpMediaTypeNotSupportedException ex, + final HttpHeaders headers, final HttpStatus status, + final WebRequest request) { + ArrayList errors = new ArrayList<>(); + final StringBuilder builder = new StringBuilder(); + builder.append(ex.getContentType()); + builder.append(" media type is not supported. Supported media types are "); + ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " ")); + errors.add(builder.toString()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, new HttpHeaders(), HttpStatus.UNSUPPORTED_MEDIA_TYPE); + } + + // 500 + + @ExceptionHandler({Exception.class}) + public ResponseEntity handleAll(final Exception ex, final WebRequest request) { + log.debug("GlobalRestExceptionHandler::handleAll"); + log.error("error:" + ex.toString()); + ArrayList errors = new ArrayList<>(); + errors.add(ex.getLocalizedMessage()); + final ApiStatusResponse apiStatusResponse = new ApiStatusResponse + .ApiResponseBuilder() + .withSuccess(false) + .atTime(LocalDateTime.now(ZoneOffset.UTC)) + .withErrors(errors) + .build(); + return new ResponseEntity(apiStatusResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/HttpMethodOverrideHeaderFilter.java b/src/main/java/com/microfocus/example/config/handlers/HttpMethodOverrideHeaderFilter.java new file mode 100644 index 0000000..4ddf589 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/HttpMethodOverrideHeaderFilter.java @@ -0,0 +1,48 @@ +package com.microfocus.example.config.handlers; + +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Locale; + +/* Adding this to create an HTTP Verb tunnelling vulnerability + * Copied from https://stackoverflow.com/questions/15365660/spring-mvc-and-x-http-method-override-parameter + */ +public class HttpMethodOverrideHeaderFilter extends OncePerRequestFilter { + private static final String X_HTTP_METHOD_OVERRIDE_HEADER = "X-HTTP-Method-Override"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String headerValue = request.getHeader(X_HTTP_METHOD_OVERRIDE_HEADER); + if (RequestMethod.POST.name().equals(request.getMethod()) && StringUtils.hasLength(headerValue)) { + String method = headerValue.toUpperCase(Locale.ENGLISH); + HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method); + filterChain.doFilter(wrapper, response); + } + else { + filterChain.doFilter(request, response); + } + } + + private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { + private final String method; + + public HttpMethodRequestWrapper(HttpServletRequest request, String method) { + super(request); + this.method = method; + } + + @Override + public String getMethod() { + return this.method; + } + } +} diff --git a/src/main/java/com/microfocus/example/config/handlers/LoggingAccessDeniedHandler.java b/src/main/java/com/microfocus/example/config/handlers/LoggingAccessDeniedHandler.java new file mode 100644 index 0000000..db8a9c0 --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/LoggingAccessDeniedHandler.java @@ -0,0 +1,62 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Class to log all Access Denials + * @author Kevin A. Lee + */ +@Component +public class LoggingAccessDeniedHandler implements AccessDeniedHandler { + + private static Logger log = LoggerFactory.getLogger(LoggingAccessDeniedHandler.class); + + @Override + public void handle(HttpServletRequest request, + HttpServletResponse response, + AccessDeniedException ex) throws IOException, ServletException { + + log.info("LoggingAccessDeniedHandler"); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + if (auth != null) { + log.info(auth.getName() + + " was trying to access protected resource: " + + request.getRequestURI()); + } + + response.sendRedirect(request.getContextPath() + "/access-denied"); + + } + +} diff --git a/src/main/java/com/microfocus/example/config/handlers/UrlAuthenticationSuccessHandler.java b/src/main/java/com/microfocus/example/config/handlers/UrlAuthenticationSuccessHandler.java new file mode 100644 index 0000000..3798cff --- /dev/null +++ b/src/main/java/com/microfocus/example/config/handlers/UrlAuthenticationSuccessHandler.java @@ -0,0 +1,145 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.config.handlers; + +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.User; +import com.microfocus.example.utils.JwtUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.security.web.WebAttributes; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.net.URL; +import java.util.Collection; + +/** + * Custom Url Authentication Success Handler + * @author Kevin A. Lee + */ +public class UrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + @Autowired + private JwtUtils jwtUtils; + + private static final Logger log = LoggerFactory.getLogger(UrlAuthenticationSuccessHandler.class); + + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, + HttpServletResponse response, Authentication authentication) + throws IOException { + + log.debug("UrlAuthenticationSuccessHandler:onAuthenticationSuccess"); + HttpSession session = request.getSession(false); + + CustomUserDetails iwaUser = (CustomUserDetails) authentication.getPrincipal(); + User user = iwaUser.getUserDetails(); + String jwtToken = jwtUtils.generateJwtToken(authentication); + log.debug("Generated jwtToken: " + jwtToken); + session.setAttribute("userId", user.getId()); + if (session.getAttribute("username") != null) { + if (request.getParameter("username") != null) { + session.setAttribute("username", request.getParameter("username")); + } + } else { + session.setAttribute("username", user.getUsername()); + } + session.setAttribute("authorities", authentication.getAuthorities()); + session.setAttribute("jwtToken", jwtToken); + + handle(request, response, authentication); + clearAuthenticationAttributes(request); + } + + protected void handle(HttpServletRequest request, + HttpServletResponse response, Authentication authentication) + throws IOException { + + boolean isUser = false; + boolean isAdmin = false; + String targetUrl = request.getParameter("referer"); + //if (targetUrl.endsWith("/")) targetUrl = targetUrl.substring(0, targetUrl.length()); + String targetPath = new URL(targetUrl).getPath(); + + Collection authorities = authentication.getAuthorities(); + for (GrantedAuthority grantedAuthority : authorities) { + if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) { + isAdmin = true; + break; + } else if (grantedAuthority.getAuthority().equals("ROLE_USER")) { + isUser = true; + } + } + if (isAdmin) { + targetUrl = "/admin"; + } else if (isUser) { + log.debug("targetPath=" + targetPath); + log.debug("targetUrl=" + targetUrl); + if (targetUrl.contains("?")) targetUrl = targetUrl.substring(0, targetUrl.indexOf("?")); + if (targetPath.endsWith("/cart")) { + targetUrl = targetUrl.replace("/cart", "/cart/checkout"); + } else if (targetPath.endsWith("/login")) { + targetUrl = targetUrl.replace("/login", "/user"); + } else if (targetPath.endsWith("/register")) { + targetUrl = targetUrl.replace("/register", "/"); + } else if (targetPath.equals("/")) { + targetUrl = targetUrl + "user"; + } + // else use referring URL + } else { + throw new IllegalStateException(); + } + + if (response.isCommitted()) { + log.debug("Response has already been committed. Unable to redirect to "+ targetUrl); + return; + } + + log.debug("Redirecting to: " + targetUrl); + redirectStrategy.sendRedirect(request, response, targetUrl); + } + + protected void clearAuthenticationAttributes(HttpServletRequest request) { + HttpSession session = request.getSession(false); + if (session == null) { + return; + } + session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); + } + + protected RedirectStrategy getRedirectStrategy() { + return redirectStrategy; + } + + public void setRedirectStrategy(RedirectStrategy redirectStrategy) { + this.redirectStrategy = redirectStrategy; + } +} diff --git a/src/main/java/com/microfocus/example/entity/Authority.java b/src/main/java/com/microfocus/example/entity/Authority.java new file mode 100644 index 0000000..f3b350c --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Authority.java @@ -0,0 +1,83 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.UUID; + +/** + * Spring Security "Authority" entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "authorities") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Authority { + + private static final long serialVersionUID = 1L; + + @Id + //@GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + //private Integer id; + + @Enumerated(EnumType.STRING) + private AuthorityType name; + + public Authority() {} + + public Authority(AuthorityType name) { + this.name = name; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public AuthorityType getName() { + return name; + } + + public void setName(AuthorityType name) { + this.name = name; + } + + @Override + public String toString() { + return "Authority{" + + "id=" + id + + ", name=" + name + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/entity/AuthorityType.java b/src/main/java/com/microfocus/example/entity/AuthorityType.java new file mode 100644 index 0000000..66551f6 --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/AuthorityType.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +/** + * Supported Authentication type + * @author Kevin A. Lee + */ +public enum AuthorityType { + ROLE_ADMIN, + ROLE_USER, + ROLE_API, + ROLE_GUEST, + ROLE_CUSTOMER, + ROLE_SUPERVISOR, + ROLE_TEST +} diff --git a/src/main/java/com/microfocus/example/entity/CustomUserDetails.java b/src/main/java/com/microfocus/example/entity/CustomUserDetails.java new file mode 100644 index 0000000..c6bb563 --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/CustomUserDetails.java @@ -0,0 +1,94 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * Custom User Details implementation + * @author Kevin A. Lee + */ +public class CustomUserDetails implements UserDetails { + + private static final long serialVersionUID = 5285723341944805637L; + + private User user; + + public CustomUserDetails(User user) { + this.user = user; + } + + @Override + public Collection getAuthorities() { + return user.getAuthorities().stream().map(authority -> new SimpleGrantedAuthority(authority.getName().toString())).collect(Collectors.toList()); + } + + public UUID getId() { + return user.getId(); + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + public String getName() { + return user.getFirstName(); + } + + public String getEmail() { + return user.getEmail(); + } + + public String getMobile() { + return user.getPhone(); + } + + public boolean isEnabled() { return user.getEnabled(); } + + public User getUserDetails() { + return user; + } +} diff --git a/src/main/java/com/microfocus/example/entity/Message.java b/src/main/java/com/microfocus/example/entity/Message.java new file mode 100644 index 0000000..c98d34c --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Message.java @@ -0,0 +1,142 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +/** + * A user message. + * + * @author Kevin A. Lee + */ +@Entity +@Table(name = "messages") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Message implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + //@GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + //private Integer id; + + @ManyToOne + private User user; + + @NotEmpty(message = "{message.text.notEmpty}") + @Size(min = 20, message = "{message.text.invalidLength}") + private String text; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @DateTimeFormat(pattern = "MM-dd-yyyy") + @Column(name = "sent_date") + private Date sentDate; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + @Column(name = "read_date") + private Date readDate; + + @NotNull + private Boolean read; + + public Message() { + this.read = false; + Calendar calendar = Calendar.getInstance(); + this.sentDate = calendar.getTime(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getText() { + return this.text; + } + + public void setText(String text) { + this.text = text; + } + + @Basic + @Temporal(TemporalType.DATE) + public Date getSentDate() { + return this.sentDate; + } + + public void setSentDate(Date sentDate) { + this.sentDate = sentDate; + } + + @Basic + @Temporal(TemporalType.DATE) + public Date getReadDate() { + return this.readDate; + } + + public void setReadDate(Date readDate) { + this.readDate = readDate; + } + + public Boolean getRead() { + return this.read; + } + + public void setRead(Boolean read) { + this.read = read; + } + + @Override + public String toString() { + return "Message(" + id + " to: " + user.getUsername() + " on: " + sentDate.toString() + ":" + text + "!)"; + } + +} diff --git a/src/main/java/com/microfocus/example/entity/Order.java b/src/main/java/com/microfocus/example/entity/Order.java new file mode 100644 index 0000000..4ff2565 --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Order.java @@ -0,0 +1,187 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +/** + * Order entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "orders") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Order implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @ManyToOne + private User user; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @Column(name = "order_num") + private String orderNum; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @DateTimeFormat(pattern = "MM-dd-yyyy") + @Column(name = "order_date") + private Date orderDate; + + private float amount; + + private String cart; + + private String credit_card; + + @NotNull + private Boolean shipped; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @DateTimeFormat(pattern = "MM-dd-yyyy") + @Column(name = "shipped_date") + private Date shippedDate; + + @Column(name = "order_notes") + private String notes; + + public Order() { + this.shipped = false; + Calendar calendar = Calendar.getInstance(); + this.orderDate = calendar.getTime(); + } + + public Order(UUID id, User user, Date orderDate, String orderNum, float amount, String cart, boolean shipped, String notes, String credit_card) { + this.id = id; + this.user = user; + this.orderDate = orderDate; + this.orderNum = orderNum; + this.amount = amount; + this.cart = cart; + this.shipped = shipped; + this.credit_card = credit_card; + this.setNotes(notes); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Date getOrderDate() { + return orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public String getOrderNum() { + return orderNum; + } + + public void setOrderNum(String orderNum) { + this.orderNum = orderNum; + } + + public float getAmount() { + return amount; + } + + public void setAmount(float amount) { + this.amount = amount; + } + + public String getCart() { + return this.cart; + } + + public void setCart(String cart) { + this.cart = cart; + } + + public String getCreditCard() { + return this.credit_card; + } + + public void setCreditCard(String credit_card) { + this.credit_card = credit_card; + } + + public Boolean getShipped() { + return shipped; + } + + public void setShipped(Boolean shipped) { + this.shipped = shipped; + } + + public Date getShippedDate() { + return shippedDate; + } + + public void setShippedDate(Date shippedDate) { + this.shippedDate = shippedDate; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + @Override + public String toString() { + return "Order(" + id + " : " + orderNum + " for: " + user.getUsername() + " amount : " + amount + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/entity/Product.java b/src/main/java/com/microfocus/example/entity/Product.java new file mode 100644 index 0000000..a5558b8 --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Product.java @@ -0,0 +1,227 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.util.UUID; + +/** + * Product entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "products") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Product implements Serializable { + + private static final long serialVersionUID = 1L; + public static final String TABLE_NAME = "products"; + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @NotEmpty(message = "{product.code.notEmpty}") + @Size(min = 6, max = 40, message = "{product.code.invalidLength}") + private String code; + + @NotEmpty(message = "{product.name.notEmpty}") + @Size(min = 6, max = 40, message = "{product.name.invalidLength}") + private String name; + + @NotEmpty(message = "{product.summary.notEmpty}") + @Size(min = 10, message = "{product.summary.invalidLength}") + private String summary; + + @NotEmpty(message = "{product.description.notEmpty}") + @Size(min = 40, message = "{product.description.invalidLength}") + private String description; + + private String image; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float price; + + @NotNull + @Column(name = "on_sale") + private Boolean onSale; + + @Min(value = 0, message = "{product.price.invalidValue}") + @Column(name = "sale_price") + private float salePrice; + + @NotNull + @Column(name = "in_stock") + private Boolean inStock; + + @Min(value = 1, message = "{product.time_to_stock.invalidValue}") + @Max(value = 365, message = "{product.time_to_stock.invalidValue}") + @Column(name = "time_to_stock") + private int timeToStock; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + @NotNull + private Boolean available; + + public Product() { + } + + public Product(UUID id, String code, String name, String summary, String description, String image, + float price, boolean onSale, float salePrice, boolean inStock, int timeToStock, int rating, + boolean available) { + this.id = id; + this.code = code; + this.name = name; + this.summary = summary; + this.description = description; + this.image = image; + this.price = price; + this.onSale = onSale; + this.salePrice = salePrice; + this.inStock = inStock; + this.timeToStock = timeToStock; + this.rating = rating; + this.available = available; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Boolean getOnSale() { + return onSale; + } + + public void setOnSale(Boolean onSale) { + this.onSale = onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public void setSalePrice(float salePrice) { + this.salePrice = salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public void setInStock(Boolean inStock) { + this.inStock = inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public void setTimeToStock(int timeToStock) { + this.timeToStock = timeToStock; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + + @Override + public String toString() { + return "Product(" + id + " : " + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/entity/RefreshToken.java b/src/main/java/com/microfocus/example/entity/RefreshToken.java new file mode 100644 index 0000000..76489ea --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/RefreshToken.java @@ -0,0 +1,94 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.microfocus.example.utils.JwtUtils; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.Instant; +import java.util.UUID; + +/** + * A JWT refresh token. + * + * @author Kevin A. Lee + */ +@Entity +@Table(name = "refresh_tokens") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class RefreshToken implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @OneToOne + @JoinColumn(name = "user_id", referencedColumnName = "id") + private User user; + + @Column(name = "expiry_date", nullable = false) + private Instant expiryDate; + + public RefreshToken() { + JwtUtils jwtUtils = new JwtUtils(); + this.expiryDate = jwtUtils.getDefaultExpiration(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Instant getExpiryDate() { + return this.expiryDate; + } + + public void setExpiryDate(Instant expiryDate) { + this.expiryDate = expiryDate; + } + + @Override + public String toString() { + return "RefreshToken(" + id + " for: " + user.getUsername() + " expires on: " + expiryDate.toString() + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/entity/Review.java b/src/main/java/com/microfocus/example/entity/Review.java new file mode 100644 index 0000000..0401b8e --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Review.java @@ -0,0 +1,154 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.UUID; + +/** + * Product Review entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "reviews") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Review implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @ManyToOne + private Product product; + + @ManyToOne + private User user; + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @DateTimeFormat(pattern = "MM-dd-yyyy") + @Column(name = "review_date") + private Date reviewDate; + + private String comment; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + @NotNull + private Boolean visible; + + public Review() { + this.visible = false; + Calendar calendar = Calendar.getInstance(); + this.reviewDate = calendar.getTime(); + } + + public Review(UUID id, User user, Product product, Date reviewDate, String comment, int rating, boolean visible) { + this.id = id; + this.product = product; + this.user = user; + this.reviewDate = reviewDate; + this.comment = comment; + this.rating = rating; + this.visible = visible; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public Product getProduct() { + return this.product; + } + + public void setProduct(Product product) { + this.product = product; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Date getReviewDate() { + return reviewDate; + } + + public void setReviewDate(Date reviewDate) { + this.reviewDate = reviewDate; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getVisible() { + return visible; + } + + public void setVisible(Boolean visible) { + this.visible = visible; + } + + @Override + public String toString() { + return "Review(" + id + " of: " + product.getName() + " by: " + user.getUsername() + " on : " + reviewDate + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/entity/User.java b/src/main/java/com/microfocus/example/entity/User.java new file mode 100644 index 0000000..ac00017 --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/User.java @@ -0,0 +1,298 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import java.io.Serializable; +import java.util.*; + +import javax.persistence.*; +import javax.validation.constraints.*; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import org.hibernate.annotations.GenericGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +/** + * Custom User entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "users") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + @Bean("UserPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 4, max = 10, message = "{user.username.invalidLength}") + @Pattern(regexp = "(^[a-z|0-9]{4,10}$)", message = "{user.username.invalidCharacters}") + @Column(nullable = false, unique = true) + private String username; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private String password; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Transient + private String confirmPassword; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstname.invalidLength}") + @Column(name = "first_name") + private String firstName; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstname.invalidLength}") + @Column(name = "last_name") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + @Column(unique = true) + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^(?!0+$)[0-9]{7,12}$)", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @Column(name = "date_created") + private Date dateCreated; + + private String gender; + + @JsonProperty("enabled") + private boolean enabled; + + //@JsonProperty(access = JsonProperty.Access.READ_ONLY) + @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }, fetch = FetchType.EAGER) + @JoinTable(name = "user_authorities", + joinColumns = { @JoinColumn(name = "user_id") }, + inverseJoinColumns = { @JoinColumn(name = "authority_id") }) + private Set authorities = new HashSet<>(); + + public User() { + } + + public User(UUID id, String username, String password, String firstName, String lastName, String email, String phone, + String address, String city, String state, String zip, String country, String gender, boolean enabled) { + this.id = id; + this.username = username; + this.password = password; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.phone = phone; + this.address = address; + this.city = city; + this.state = state; + this.zip = zip; + this.country = country; + this.gender = gender; + this.enabled = enabled; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @JsonIgnore + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @JsonIgnore + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Date getDateCreated() { + return dateCreated; + } + + public void setDateCreated(Date dateCreated) { + this.dateCreated = dateCreated; + } + + public boolean getEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public Set getAuthorities() { + return authorities; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } + + public static User fromUserDetails(UserDetails user) { + Set authorities = new HashSet<>(Collections.emptySet()); + for (GrantedAuthority a : user.getAuthorities()) { + authorities.add(new Authority(AuthorityType.valueOf(a.getAuthority()))); + } + User utmp = new User(); + utmp.setUsername(user.getUsername()); + utmp.setPassword(user.getPassword()); + utmp.setAuthorities(authorities); + utmp.setEnabled(user.isEnabled()); + return utmp; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", authorities=" + authorities + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/entity/Verification.java b/src/main/java/com/microfocus/example/entity/Verification.java new file mode 100644 index 0000000..7af1e8f --- /dev/null +++ b/src/main/java/com/microfocus/example/entity/Verification.java @@ -0,0 +1,90 @@ +/* + Insecure Web App (IWA) + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.entity; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * MFA Verification entity + * @author Kevin A. Lee + */ +@Entity +@Table(name = "verifications") +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Verification implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @Column(name = "phone", nullable = false) + private String phone; + + @Column(name = "request_id", nullable = false) + private String requestId; + + @Column(name = "expiration_date") + private Date expirationDate; + + public Verification() { + } + + public Verification(String phone, String requestId, Date expirationDate) { + this.phone = phone; + this.requestId = requestId; + this.expirationDate = expirationDate; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public Date getExpirationDate() { + return expirationDate; + } + + public void setExpirationDate(Date expirationDate) { + this.expirationDate = expirationDate; + } + + @Override + public String toString() { + return "Verification{" + + "phone='" + phone + '\'' + + ", requestId='" + requestId + '\'' + + ", expirationDate=" + expirationDate + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/microfocus/example/exception/ApiBadCredentialsException.java b/src/main/java/com/microfocus/example/exception/ApiBadCredentialsException.java new file mode 100644 index 0000000..54480c1 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/ApiBadCredentialsException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if a user is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ApiBadCredentialsException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ApiBadCredentialsException(String username) { + super("Invalid credentials for username: " + username); + } +} diff --git a/src/main/java/com/microfocus/example/exception/ApiRefreshTokenException.java b/src/main/java/com/microfocus/example/exception/ApiRefreshTokenException.java new file mode 100644 index 0000000..e0ebf67 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/ApiRefreshTokenException.java @@ -0,0 +1,33 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.FORBIDDEN) +public class ApiRefreshTokenException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ApiRefreshTokenException(String token, String message) { + super(String.format("Failed for [%s]: %s", token, message)); + } +} diff --git a/src/main/java/com/microfocus/example/exception/BackupException.java b/src/main/java/com/microfocus/example/exception/BackupException.java new file mode 100644 index 0000000..39a1c63 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/BackupException.java @@ -0,0 +1,33 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +/** + * Generic Exception for handling Password errors + * @author Kevin A. Lee + */ +public class BackupException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public BackupException(String message) { + super(message); + } +} diff --git a/src/main/java/com/microfocus/example/exception/CustomRestServiceException.java b/src/main/java/com/microfocus/example/exception/CustomRestServiceException.java new file mode 100644 index 0000000..dcb48cd --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/CustomRestServiceException.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +public class CustomRestServiceException extends Exception { + + public CustomRestServiceException() { + super(); + } + + public CustomRestServiceException(String message) { + super(message); + } + + public CustomRestServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/microfocus/example/exception/EmailAddressTakenException.java b/src/main/java/com/microfocus/example/exception/EmailAddressTakenException.java new file mode 100644 index 0000000..86df488 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/EmailAddressTakenException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 403 if an email address is already taken by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 403 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.FORBIDDEN) +public class EmailAddressTakenException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public EmailAddressTakenException(String email) { + super("The email address '" + email + "' is already taken"); + } +} diff --git a/src/main/java/com/microfocus/example/exception/InvalidPasswordException.java b/src/main/java/com/microfocus/example/exception/InvalidPasswordException.java new file mode 100644 index 0000000..df2f1c6 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/InvalidPasswordException.java @@ -0,0 +1,33 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +/** + * Generic Exception for handling Password errors + * @author Kevin A. Lee + */ +public class InvalidPasswordException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public InvalidPasswordException(String message) { + super(message); + } +} diff --git a/src/main/java/com/microfocus/example/exception/MessageNotFoundException.java b/src/main/java/com/microfocus/example/exception/MessageNotFoundException.java new file mode 100644 index 0000000..02c20d1 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/MessageNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if a message is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class MessageNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public MessageNotFoundException(String messageNumber) { + super("No such message: " + messageNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/NotAuthorisedException.java b/src/main/java/com/microfocus/example/exception/NotAuthorisedException.java new file mode 100644 index 0000000..3498d97 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/NotAuthorisedException.java @@ -0,0 +1,32 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +public class NotAuthorisedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public NotAuthorisedException(String userName) { + super("Not authorised: " + userName); + } +} diff --git a/src/main/java/com/microfocus/example/exception/OrderNotFoundException.java b/src/main/java/com/microfocus/example/exception/OrderNotFoundException.java new file mode 100644 index 0000000..d425ca4 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/OrderNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if an order is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class OrderNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public OrderNotFoundException(String orderNumber) { + super("No such order: " + orderNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/ProductNotFoundException.java b/src/main/java/com/microfocus/example/exception/ProductNotFoundException.java new file mode 100644 index 0000000..8f991e6 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/ProductNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if a product is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ProductNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ProductNotFoundException(String productNumber) { + super("No such product: " + productNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/ReviewNotFoundException.java b/src/main/java/com/microfocus/example/exception/ReviewNotFoundException.java new file mode 100644 index 0000000..27dd367 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/ReviewNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if an order is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ReviewNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ReviewNotFoundException(String orderNumber) { + super("No such review: " + orderNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/RoleNotFoundException.java b/src/main/java/com/microfocus/example/exception/RoleNotFoundException.java new file mode 100644 index 0000000..483fe4b --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/RoleNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if a role is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class RoleNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public RoleNotFoundException(String roleNumber) { + super("No such role: " + roleNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/ServerErrorException.java b/src/main/java/com/microfocus/example/exception/ServerErrorException.java new file mode 100644 index 0000000..719859a --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/ServerErrorException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 404 if a product is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) +public class ServerErrorException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ServerErrorException(String msg) { + super(msg); + } +} diff --git a/src/main/java/com/microfocus/example/exception/StorageException.java b/src/main/java/com/microfocus/example/exception/StorageException.java new file mode 100644 index 0000000..041c782 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/StorageException.java @@ -0,0 +1,12 @@ +package com.microfocus.example.exception; + +public class StorageException extends RuntimeException { + + public StorageException(String message) { + super(message); + } + + public StorageException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/microfocus/example/exception/StorageFileNotFoundException.java b/src/main/java/com/microfocus/example/exception/StorageFileNotFoundException.java new file mode 100644 index 0000000..92f046a --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/StorageFileNotFoundException.java @@ -0,0 +1,11 @@ +package com.microfocus.example.exception; +public class StorageFileNotFoundException extends StorageException { + + public StorageFileNotFoundException(String message) { + super(message); + } + + public StorageFileNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/microfocus/example/exception/UserLockedOutException.java b/src/main/java/com/microfocus/example/exception/UserLockedOutException.java new file mode 100644 index 0000000..4502d71 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/UserLockedOutException.java @@ -0,0 +1,33 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +/** + * Generic Exception for handling Password errors + * @author Kevin A. Lee + */ +public class UserLockedOutException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public UserLockedOutException(String message) { + super(message); + } +} diff --git a/src/main/java/com/microfocus/example/exception/UserNotFoundException.java b/src/main/java/com/microfocus/example/exception/UserNotFoundException.java new file mode 100644 index 0000000..7abe34e --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/UserNotFoundException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.http.HttpStatus; + +/** + * Allow the controller to return a 404 if a user is not found by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 404 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.NOT_FOUND) +public class UserNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public UserNotFoundException(String userNumber) { + super("No such user: " + userNumber); + } +} diff --git a/src/main/java/com/microfocus/example/exception/UsernameTakenException.java b/src/main/java/com/microfocus/example/exception/UsernameTakenException.java new file mode 100644 index 0000000..7a75293 --- /dev/null +++ b/src/main/java/com/microfocus/example/exception/UsernameTakenException.java @@ -0,0 +1,39 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Allow the controller to return a 403 if a username is already taken by simply + * throwing this exception. The @ResponseStatus causes Spring MVC to return a + * 403 instead of the usual 500. + * @author Kevin A. Lee + */ +@ResponseStatus(HttpStatus.FORBIDDEN) +public class UsernameTakenException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public UsernameTakenException(String username) { + super("The username '" + username + "' is already taken"); + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/LoginRequest.java b/src/main/java/com/microfocus/example/payload/request/LoginRequest.java new file mode 100644 index 0000000..fe42f84 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/LoginRequest.java @@ -0,0 +1,46 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import javax.validation.constraints.NotBlank; + +public class LoginRequest { + @NotBlank + private String username; + + @NotBlank + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/MessageRequest.java b/src/main/java/com/microfocus/example/payload/request/MessageRequest.java new file mode 100644 index 0000000..4316baa --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/MessageRequest.java @@ -0,0 +1,112 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.microfocus.example.entity.Message; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.UUID; + +/** + * Message Request DTO + * + * @author Kevin A. Lee + */ +public class MessageRequest { + + @JsonIgnore + private Integer id; + + private UUID userId; + + @NotEmpty(message = "{message.text.notEmpty}") + @Size(min = 40, message = "{message.text.invalidLength}") + private String text; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date sentDate; + + @JsonIgnore + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date readDate; + + private Boolean read; + + public MessageRequest() { + } + + public MessageRequest(Message message) { + this.userId = message.getUser().getId(); + this.text = message.getText(); + this.sentDate = message.getSentDate(); + this.read = false; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public UUID getUserId() { + return userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Date getSentDate() { + return sentDate; + } + + public void setSentDate(Date sentDate) { + this.sentDate = sentDate; + } + + public Date getReadDate() { + return readDate; + } + + public void setReadDate(Date readDate) { + this.readDate = readDate; + } + + @Override + public String toString() { + return "MessageRequest(user: " + userId + " message: " + text.substring(0,40) + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/request/OrderRequest.java b/src/main/java/com/microfocus/example/payload/request/OrderRequest.java new file mode 100644 index 0000000..94454d5 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/OrderRequest.java @@ -0,0 +1,162 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.User; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.Date; +import java.util.UUID; + +/** + * Order Request DTO + * + * @author Kevin A. Lee + */ +public class OrderRequest { + + @JsonIgnore + private UUID id; + + private UUID userId; + + private String orderNum; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date orderDate; + + private float amount; + + private String cart; + + private String creditCard; + + private Boolean shipped; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date shippedDate; + + private Object notes; + + public OrderRequest() { + } + + public OrderRequest(Order order) { + this.userId = order.getUser().getId(); + this.orderNum = order.getOrderNum(); + this.orderDate = order.getOrderDate(); + this.amount = order.getAmount(); + this.cart = order.getCart(); + this.creditCard = order.getCreditCard(); + this.shipped = order.getShipped(); + this.shippedDate = order.getShippedDate(); + this.setNotes(order.getNotes()); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public UUID getUserId() { + return this.userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public Date getOrderDate() { + return orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public String getOrderNum() { + return orderNum; + } + + public void setOrderNum(String name) { + this.orderNum = orderNum; + } + + public float getAmount() { + return amount; + } + + public void setAmount(float amount) { + this.amount = amount; + } + + public String getCart() { + return this.cart; + } + + public void setCart(String cart) { + this.cart = cart; + } + + public String getCreditCard() { + return this.creditCard; + } + + public void setCreditCard(String creditCard) { + this.creditCard = creditCard; + } + + public Boolean getShipped() { + return shipped; + } + + public void setShipped(Boolean shipped) { + this.shipped = shipped; + } + + public Date getShippedDate() { + return shippedDate; + } + + public void setShippedDate(Date shippedDate) { + this.shippedDate = shippedDate; + } + + public Object getNotes() { + return notes; + } + + public void setNotes(Object notes) { + this.notes = notes; + } + + @Override + public String toString() { + return "OrderRequest(" + id + " : " + orderNum + " for: " + userId + " amount : " + amount + ")"; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/ProductRequest.java b/src/main/java/com/microfocus/example/payload/request/ProductRequest.java new file mode 100644 index 0000000..1b3fd8e --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/ProductRequest.java @@ -0,0 +1,211 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import com.microfocus.example.entity.Product; + +import javax.persistence.*; +import javax.validation.constraints.*; +import java.io.Serializable; +import java.util.UUID; + +/** + * Product Request DTO + * + * @author Kevin A. Lee + */ +public class ProductRequest { + + @JsonIgnore + private UUID id; + + @NotEmpty(message = "{product.code.notEmpty}") + @Size(min = 6, max = 40, message = "{product.code.invalidLength}") + private String code; + + @NotEmpty(message = "{product.name.notEmpty}") + @Size(min = 6, max = 40, message = "{product.name.invalidLength}") + private String name; + + @NotEmpty(message = "{product.summary.notEmpty}") + @Size(min = 10, message = "{product.summary.invalidLength}") + private String summary; + + @NotEmpty(message = "{product.description.notEmpty}") + @Size(min = 40, message = "{product.description.invalidLength}") + private String description; + + private String image; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float price; + + @NotNull + @Column(name = "on_sale") + private Boolean onSale; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float salePrice; + + @NotNull + private Boolean inStock; + + @Min(value = 0, message = "{product.timeToStock.invalidValue}") + @Max(value = 365, message = "{product.timeToStock.invalidValue}") + private int timeToStock; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + @NotNull + private Boolean available; + + public ProductRequest() { + } + + public ProductRequest(Product product) { + this.code = product.getCode(); + this.name = product.getName(); + this.summary = product.getSummary(); + this.description = product.getDescription(); + this.image = product.getImage(); + this.price = product.getPrice(); + this.onSale = product.getOnSale(); + this.salePrice = product.getSalePrice(); + this.inStock = product.getInStock(); + this.timeToStock = product.getTimeToStock(); + this.rating = product.getRating(); + this.available = product.getAvailable(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Boolean getOnSale() { + return onSale; + } + + public void setOnSale(Boolean onSale) { + this.onSale = onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public void setSalePrice(float salePrice) { + this.salePrice = salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public void setInStock(Boolean inStock) { + this.inStock = inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public void setTimeToStock(int timeToStock) { + this.timeToStock = timeToStock; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + + @Override + public String toString() { + return "ProductRequest(" + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/request/RefreshTokenRequest.java b/src/main/java/com/microfocus/example/payload/request/RefreshTokenRequest.java new file mode 100644 index 0000000..3f3730b --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/RefreshTokenRequest.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import javax.validation.constraints.NotBlank; + +public class RefreshTokenRequest { + @NotBlank + private String refreshToken; + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/RegisterUserRequest.java b/src/main/java/com/microfocus/example/payload/request/RegisterUserRequest.java new file mode 100644 index 0000000..7fb8c4e --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/RegisterUserRequest.java @@ -0,0 +1,129 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.microfocus.example.entity.User; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.UUID; + +/** + * Register User Request DTO + * + * @author Kevin A. Lee + */ +public class RegisterUserRequest { + + @Bean("RegisterUserPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @NotEmpty(message = "{user.username.notEmpty}") + @Pattern(regexp = "(^[a-z|0-9]{4,10}$)", message = "{user.username.invalidFormat}") + @Column(nullable = false, unique = true) + private String username; + + @NotEmpty(message = "{user.password.notEmpty}") + private String password; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstname.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstname.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + @Column(unique = true) + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^(?!0+$)[0-9]{7,12}$)", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + public RegisterUserRequest() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + @Override + public String toString() { + return "RegisterUserRequest{" + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/ReviewRequest.java b/src/main/java/com/microfocus/example/payload/request/ReviewRequest.java new file mode 100644 index 0000000..b0a0f24 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/ReviewRequest.java @@ -0,0 +1,113 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.microfocus.example.entity.Review; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; +import java.util.UUID; + +/** + * Review Request DTO + * + * @author Kevin A. Lee + */ +public class ReviewRequest { + + @JsonIgnore + private UUID id; + + private UUID productId; + + private UUID userId; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date reviewDate; + + private String comment; + + private int rating; + + public ReviewRequest() { + } + + public ReviewRequest(Review review) { + this.productId = review.getProduct().getId(); + this.userId = review.getUser().getId(); + this.reviewDate = review.getReviewDate(); + this.comment = review.getComment(); + this.rating = review.getRating(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public UUID getProductId() { + return this.productId; + } + + public void setProductId(UUID productId) { + this.productId = productId; + } + + public UUID getUserId() { + return this.userId; + } + + public void setUserId(UUID userId) { + this.userId = userId; + } + + public Date getReviewDate() { + return reviewDate; + } + + public void setReviewDate(Date reviewDate) { + this.reviewDate = reviewDate; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + @Override + public String toString() { + return "ReviewRequest(" + id + " of: " + productId + " by: " + userId + " on : " + reviewDate + ")"; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/SubscribeUserRequest.java b/src/main/java/com/microfocus/example/payload/request/SubscribeUserRequest.java new file mode 100644 index 0000000..72f64cd --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/SubscribeUserRequest.java @@ -0,0 +1,87 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * Subscribe User Request DTO + * + * @author Kevin A. Lee + */ +public class SubscribeUserRequest { + + private Integer id; + private String firstName; + private String lastName; + private String email; + + public SubscribeUserRequest() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String toString() { + return "SubscribeUserRequest{" + + "id='" + id + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email=" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/payload/request/UserRequest.java b/src/main/java/com/microfocus/example/payload/request/UserRequest.java new file mode 100644 index 0000000..24146af --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/request/UserRequest.java @@ -0,0 +1,228 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.request; + +import com.microfocus.example.entity.User; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.UUID; + +/** + * User Request DTO + * + * @author Kevin A. Lee + */ +public class UserRequest { + + @Bean("UserRequestPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 2, max = 10, message = "{user.username.invalidLength}") + private String username; + + @NotEmpty(message = "{user.password.notEmpty}") + private String password; + + private String confirmPassword; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 6, max = 40, message = "{user.firstname.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.firstname.notEmpty}") + @Size(min = 6, max = 40, message = "{user.firstname.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + @Column(unique = true) + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^$|[0-9]{10})", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + private String gender; + + private Boolean enabled; + + public UserRequest() { + } + + public UserRequest(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip= user.getZip(); + this.country = user.getCountry(); + this.setGender(user.getGender()); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + @Override + public String toString() { + return "UserRequest{" + + "id=" + id + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/payload/response/ApiStatusResponse.java b/src/main/java/com/microfocus/example/payload/response/ApiStatusResponse.java new file mode 100644 index 0000000..d482347 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/ApiStatusResponse.java @@ -0,0 +1,112 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +/** + * API Status Response + * + * @author Kevin A. Lee + */ +public class ApiStatusResponse { + + private Boolean success; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private LocalDateTime timestamp; + private ArrayList errors; + public Boolean getSuccess() { + return success; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + public LocalDateTime getTimestamp() { + return timestamp; + } + + @JsonSerialize(using = LocalDateTimeSerializer.class) + public void setTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + } + + public ArrayList getErrors() { + return errors; + } + + public void setErrors(ArrayList errors) { + this.errors = errors; + } + + public ApiStatusResponse() { + this.success = true; + this.timestamp = LocalDateTime.now(); + this.errors = new ArrayList<>(); + } + + public static final class ApiResponseBuilder { + private Boolean success; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private LocalDateTime timestamp; + private ArrayList errors; + + public ApiResponseBuilder() { + } + + public static ApiResponseBuilder anApiResponse() { + return new ApiResponseBuilder(); + } + + public ApiResponseBuilder withSuccess(Boolean success) { + this.success = success; + return this; + } + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + public ApiResponseBuilder atTime(LocalDateTime timestamp) { + this.timestamp = timestamp; + return this; + } + + public ApiResponseBuilder withErrors(ArrayList errors) { + this.errors = errors; + return this; + } + + public ApiStatusResponse build() { + ApiStatusResponse apiErrorResponse = new ApiStatusResponse(); + apiErrorResponse.success = this.success; + apiErrorResponse.timestamp = this.timestamp; + apiErrorResponse.errors = this.errors; + return apiErrorResponse; + } + } +} diff --git a/src/main/java/com/microfocus/example/payload/response/JwtResponse.java b/src/main/java/com/microfocus/example/payload/response/JwtResponse.java new file mode 100644 index 0000000..d7b082c --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/JwtResponse.java @@ -0,0 +1,104 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import java.util.List; +import java.util.UUID; + +public class JwtResponse { + private String token; + private String refreshToken; + private String type = "Bearer"; + private long expiration; + private UUID id; + private String username; + private String email; + private List roles; + + public JwtResponse(String accessToken, String refreshToken, long expiration, UUID id, String username, String email, List roles) { + this.token = accessToken; + this.refreshToken = refreshToken; + this.expiration = expiration; + this.id = id; + this.username = username; + this.email = email; + this.roles = roles; + } + + public String getAccessToken() { + return token; + } + + public void setAccessToken(String accessToken) { + this.token = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshTokenToken(String refreshToken) { + this.token = refreshToken; + } + + public long getTokenExpiration() { + return expiration; + } + + public void setTokenExpiration(long expiration) { + this.expiration = expiration; + } + + public String getTokenType() { + return type; + } + + public void setTokenType(String tokenType) { + this.type = tokenType; + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public List getRoles() { + return roles; + } +} diff --git a/src/main/java/com/microfocus/example/payload/response/MessageResponse.java b/src/main/java/com/microfocus/example/payload/response/MessageResponse.java new file mode 100644 index 0000000..c11c58d --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/MessageResponse.java @@ -0,0 +1,79 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.microfocus.example.entity.Message; +import java.util.Date; +import java.util.UUID; + +/** + * Message Response DTO + * + * @author Kevin A. Lee + */ +public class MessageResponse { + + private UUID id; + private UserResponse user; + private String text; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Date sentDate; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Date readDate; + private Boolean read; + + public MessageResponse() { + } + + public MessageResponse(Message message) { + this.id = message.getId(); + this.user = new UserResponse(message.getUser()); + this.text = message.getText(); + this.sentDate = message.getSentDate(); + this.readDate = message.getReadDate(); + this.read = message.getRead(); + } + + public UUID getId() { + return id; + } + + public UserResponse getUser() { + return user; + } + + public String getText() { + return text; + } + + public Date getSentDate() { + return sentDate; + } + + public Date getReadDate() { + return readDate; + } + + public Boolean getRead() { + return read; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/OrderResponse.java b/src/main/java/com/microfocus/example/payload/response/OrderResponse.java new file mode 100644 index 0000000..d09ba73 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/OrderResponse.java @@ -0,0 +1,113 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; + +import java.util.Date; +import java.util.UUID; + +/** + * Order Response DTO + * + * @author Kevin A. Lee + */ +public class OrderResponse { + + private UUID id; + private UserResponse user; + private String orderNum; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Date orderDate; + private String cart; + + private String credit_card; + private float amount; + private Boolean shipped; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Date shippedDate; + private Object notes; + + public OrderResponse() { + } + + public OrderResponse(Order order) { + this.id = order.getId(); + this.user = new UserResponse(order.getUser()); + this.orderNum = order.getOrderNum(); + this.orderDate = order.getOrderDate(); + this.cart = order.getCart(); + this.credit_card = order.getCreditCard(); + this.amount = order.getAmount(); + this.shipped = order.getShipped(); + this.shippedDate = order.getShippedDate(); + this.setNotes(order.getNotes()); + } + + public UUID getId() { + return id; + } + + public UserResponse getUser() { + return user; + } + + public String getOrderNum() { + return orderNum; + } + + public Date getOrderDate() { + return orderDate; + } + + public String getCart() { + return cart; + } + + public String getCrediCard() {return credit_card;} + + public float getAmount() { + return amount; + } + + public Boolean getShipped() { + return shipped; + } + + public Date getShippedDate() { + return shippedDate; + } + + public Object getNotes() { + return notes; + } + + public void setNotes(Object notes) { + this.notes = notes; + } + + @Override + public String toString() { + return "OrderResponse(" + orderNum + " : amount : " + amount + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/ProductResponse.java b/src/main/java/com/microfocus/example/payload/response/ProductResponse.java new file mode 100644 index 0000000..b024e1a --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/ProductResponse.java @@ -0,0 +1,123 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.microfocus.example.entity.Product; + +import java.util.UUID; + +/** + * Product Response DTO + * + * @author Kevin A. Lee + */ +public class ProductResponse { + + private UUID id; + private String code; + private String name; + private String summary; + private String description; + private String image; + private float price; + private Boolean onSale; + private float salePrice; + private Boolean inStock; + private int timeToStock; + private int rating; + private Boolean available; + + public ProductResponse() { + } + + public ProductResponse(Product product) { + this.id = product.getId(); + this.code = product.getCode(); + this.name = product.getName(); + this.summary = product.getSummary(); + this.description = product.getDescription(); + this.image = product.getImage(); + this.price = product.getPrice(); + this.onSale = product.getOnSale(); + this.salePrice = product.getSalePrice(); + this.inStock = product.getInStock(); + this.timeToStock = product.getTimeToStock(); + this.rating = product.getRating(); + this.available = product.getAvailable(); + } + + public UUID getId() { + return id; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + public String getSummary() { + return summary; + } + + public String getDescription() { + return description; + } + + public String getImage() { + return image; + } + + public float getPrice() { + return price; + } + + public Boolean getOnSale() { + return onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public int getRating() { + return rating; + } + + public Boolean getAvailable() { + return available; + } + + @Override + public String toString() { + return "ProductResponse(" + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/RefreshTokenResponse.java b/src/main/java/com/microfocus/example/payload/response/RefreshTokenResponse.java new file mode 100644 index 0000000..432fcae --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/RefreshTokenResponse.java @@ -0,0 +1,58 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +public class RefreshTokenResponse { + private String token; + private String refreshToken; + private long expiration; + + + public RefreshTokenResponse(String accessToken, String refreshToken, long expiration) { + this.token = accessToken; + this.refreshToken = refreshToken; + this.expiration = expiration; + } + + public String getAccessToken() { + return token; + } + + public void setAccessToken(String accessToken) { + this.token = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshTokenToken(String refreshToken) { + this.token = refreshToken; + } + + public long getTokenExpiration() { + return expiration; + } + + public void setTokenExpiration(long expiration) { + this.expiration = expiration; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/RegisterUserResponse.java b/src/main/java/com/microfocus/example/payload/response/RegisterUserResponse.java new file mode 100644 index 0000000..5a7b1dd --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/RegisterUserResponse.java @@ -0,0 +1,91 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.microfocus.example.entity.User; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * Register User Response DTO + * + * @author Kevin A. Lee + */ +public class RegisterUserResponse { + + private String username; + private String password; + private String firstName; + private String lastName; + private String email; + private String phone; + + public RegisterUserResponse() { + } + + public RegisterUserResponse(User u) { + this.username = u.getUsername(); + this.firstName = u.getFirstName(); + this.lastName = u.getLastName(); + this.email = u.getEmail(); + this.phone = u.getPhone(); + } + + public RegisterUserResponse(String username, String password, String firstName, String lastName, + String email, String phone) { + this.username = username; + this.password = password; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.phone = phone; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + + public String getPhone() { + return phone; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/ReviewResponse.java b/src/main/java/com/microfocus/example/payload/response/ReviewResponse.java new file mode 100644 index 0000000..1ff68af --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/ReviewResponse.java @@ -0,0 +1,85 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.Review; +import com.microfocus.example.entity.User; + +import java.util.Date; +import java.util.UUID; + +/** + * Order Response DTO + * + * @author Kevin A. Lee + */ +public class ReviewResponse { + + private UUID id; + private ProductResponse product; + private UserResponse user; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss") + private Date reviewDate; + private String comment; + private int rating; + + public ReviewResponse() { + } + + public ReviewResponse(Review review) { + this.id = review.getId(); + this.product = new ProductResponse(review.getProduct()); + this.user = new UserResponse(review.getUser()); + this.reviewDate = review.getReviewDate(); + this.comment = review.getComment(); + this.rating = review.getRating(); + } + + public UUID getId() { + return id; + } + + public ProductResponse getProduct() { + return this.product; + } + + public UserResponse getUser() { + return user; + } + + public Date getReviewDate() { + return reviewDate; + } + + public String getComment() { + return comment; + } + + public int getRating() { + return rating; + } + + public String toString() { + return "ReviewResponse(" + id + " of: " + product.getName() + " by: " + user.getUsername() + " on : " + reviewDate + ")"; + } +} diff --git a/src/main/java/com/microfocus/example/payload/response/SubscribeUserResponse.java b/src/main/java/com/microfocus/example/payload/response/SubscribeUserResponse.java new file mode 100644 index 0000000..a269568 --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/SubscribeUserResponse.java @@ -0,0 +1,63 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; + +/** + * Subscribe User Response DTO + * + * @author Kevin A. Lee + */ +public class SubscribeUserResponse { + + private Integer id; + private String firstName; + private String lastName; + private String email; + + public SubscribeUserResponse() { + } + + public SubscribeUserResponse(Integer id, String firstName, String lastName, String email) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + + public Integer getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + +} diff --git a/src/main/java/com/microfocus/example/payload/response/UserResponse.java b/src/main/java/com/microfocus/example/payload/response/UserResponse.java new file mode 100644 index 0000000..debe77b --- /dev/null +++ b/src/main/java/com/microfocus/example/payload/response/UserResponse.java @@ -0,0 +1,122 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.payload.response; + +import com.microfocus.example.entity.User; + +import java.util.UUID; + +/** + * User Response DTO + * + * @author Kevin A. Lee + */ +public class UserResponse { + + private UUID id; + private String username; + private String firstName; + private String lastName; + private String email; + private String phone; + private String address; + private String city; + private String state; + private String zip; + private String country; + private String gender; + private Boolean enabled; + + public UserResponse() { + } + + public UserResponse(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip= user.getZip(); + this.country = user.getCountry(); + this.setGender(user.getGender()); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public String getUsername() { + return username; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getEmail() { + return email; + } + + public String getPhone() { + return phone; + } + + public String getAddress() { + return address; + } + + public String getCity() { + return city; + } + + public String getState() { + return state; + } + + public String getZip() { + return zip; + } + + public String getCountry() { + return country; + } + + public Boolean getEnabled() { + return enabled; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + +} diff --git a/src/main/java/com/microfocus/example/repository/MessageRepository.java b/src/main/java/com/microfocus/example/repository/MessageRepository.java new file mode 100644 index 0000000..0e60fe8 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/MessageRepository.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for Message Repository + * @author Kevin A. Lee + */ +public interface MessageRepository extends MessageRepositoryBasic, MessageRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/MessageRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/MessageRepositoryBasic.java new file mode 100644 index 0000000..32488c9 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/MessageRepositoryBasic.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Message; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +/** + * Interface for Message Repository + * + * @author Kevin A. Lee + */ +public interface MessageRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/MessageRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/MessageRepositoryCustom.java new file mode 100644 index 0000000..8e66827 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/MessageRepositoryCustom.java @@ -0,0 +1,49 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Product; +import com.microfocus.example.payload.request.MessageRequest; +import com.microfocus.example.web.form.MessageForm; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Interface for Message Repository + * @author Kevin A. Lee + */ +public interface MessageRepositoryCustom { + + public List findByUserId(UUID userId); + + public long countByUserId(UUID userId); + + public long countUnreadByUserId(UUID userId); + + public void markMessageAsReadById(UUID messageId); + + //public Message save(MessageRequest message); + + public Message save(MessageForm message); + +} diff --git a/src/main/java/com/microfocus/example/repository/MessageRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/MessageRepositoryImpl.java new file mode 100644 index 0000000..c930525 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/MessageRepositoryImpl.java @@ -0,0 +1,100 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.User; +import com.microfocus.example.payload.request.MessageRequest; +import com.microfocus.example.web.form.MessageForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.EntityManager; +import javax.persistence.EntityNotFoundException; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Implementation of Custom Message Repository + * @author Kevin A. Lee + */ +@Transactional +public class MessageRepositoryImpl implements MessageRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(MessageRepositoryImpl.class); + + private final MessageRepositoryBasic messageRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public MessageRepositoryImpl(MessageRepositoryBasic messageRepositoryBasic) { + this.messageRepositoryBasic = messageRepositoryBasic; + } + + @SuppressWarnings("unchecked") + public List findByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT m FROM Message m WHERE m.user.id = ?1", + Message.class); + query.setParameter(1, userId); + return query.getResultList(); + } + + @SuppressWarnings("unchecked") + public long countByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT count(m) FROM Message m WHERE m.user.id = ?1", + Long.class); + query.setParameter(1, userId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public long countUnreadByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT count(m) FROM Message m WHERE m.user.id = ?1 AND m.read = false", + Long.class); + query.setParameter(1, userId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public void markMessageAsReadById(UUID messageId) { + Query query = entityManager.createQuery( + "UPDATE Message m SET m.read = true WHERE m.id = ?1"); + query.setParameter(1, messageId); + query.executeUpdate(); + } + + public Message save(MessageForm message) { + Message m = new Message(); + m.setUser(message.getUser()); + m.setText(message.getText()); + return messageRepositoryBasic.save(m); + } + +} diff --git a/src/main/java/com/microfocus/example/repository/OrderRepository.java b/src/main/java/com/microfocus/example/repository/OrderRepository.java new file mode 100644 index 0000000..b81b7b4 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/OrderRepository.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for Order Repository + * @author Kevin A. Lee + */ +public interface OrderRepository extends OrderRepositoryBasic, OrderRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/OrderRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/OrderRepositoryBasic.java new file mode 100644 index 0000000..12a6ada --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/OrderRepositoryBasic.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Order; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +/** + * Interface for Order Repository + * + * @author Kevin A. Lee + */ +public interface OrderRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/OrderRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/OrderRepositoryCustom.java new file mode 100644 index 0000000..8c3327b --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/OrderRepositoryCustom.java @@ -0,0 +1,56 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.Product; +import com.microfocus.example.web.form.MessageForm; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Interface for Order Repository + * @author Kevin A. Lee + */ +public interface OrderRepositoryCustom { + + public List findByUserId(UUID userId); + + public Optional findByNumber(String code); + + List listOrders(int offset, int limit); + + public long countByUserId(UUID userId); + + public long countNotShippedByUserId(UUID userId); + + public void markOrderAsShippedById(UUID orderId); + + List findOrdersByKeywords(String keywords, int offset, int limit); + + + //public Order save(OrderRequest order); + + //public Order save(OrderForm order); + +} diff --git a/src/main/java/com/microfocus/example/repository/OrderRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/OrderRepositoryImpl.java new file mode 100644 index 0000000..80cfdd4 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/OrderRepositoryImpl.java @@ -0,0 +1,140 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Order; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Implementation of Custom Order Repository + * @author Kevin A. Lee + */ +@Transactional +public class OrderRepositoryImpl implements OrderRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(OrderRepositoryImpl.class); + + private final OrderRepositoryBasic orderRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public OrderRepositoryImpl(OrderRepositoryBasic orderRepositoryBasic) { + this.orderRepositoryBasic = orderRepositoryBasic; + } + + @SuppressWarnings("unchecked") + public List findByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT o FROM Order o WHERE o.user.id = ?1", + Order.class); + query.setParameter(1, userId); + return query.getResultList(); + } + + @SuppressWarnings("unchecked") + public Optional findByNumber(String code) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT o FROM Order o WHERE lower(o.orderNum) = lower(?1)", + Order.class); + q.setParameter(1, code); + result = (List)q.getResultList(); + Optional optionalOrder = Optional.empty(); + if (!result.isEmpty()) { + optionalOrder = Optional.of(result.get(0)); + } + return optionalOrder; + } + + @SuppressWarnings("unchecked") + public List listOrders(int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT o FROM Order o", + Order.class); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public List findOrdersByKeywords(String keywords, int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT o FROM Order o WHERE lower(o.orderNum) LIKE lower(?1)", + Order.class); + q.setParameter(1, "%"+keywords+"%"); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public long countByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT count(o) FROM Order o WHERE o.user.id = ?1", + Long.class); + query.setParameter(1, userId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public long countNotShippedByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT count(o) FROM Order o WHERE o.user.id = ?1 AND o.shipped = false", + Long.class); + query.setParameter(1, userId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public void markOrderAsShippedById(UUID OrderId) { + Query query = entityManager.createQuery( + "UPDATE Order o SET o.shipped = true WHERE o.id = ?1"); + query.setParameter(1, OrderId); + query.executeUpdate(); + } + + /*public Order save(OrderRequest order) { + Order o = new Order(); + o.setOrderNum(order.getOrderNum()); + o.setUser(order.getUserId()); + o.setOrderDate(order.getOrderDate()); + o.setAmount(order.getAmount()); + o.setCart(order.getCart()); + o.setShipped(order.getShipped()); + o.setShippedDate(order.getShippedDate()); + return orderRepositoryBasic.save(o); + }*/ + +} diff --git a/src/main/java/com/microfocus/example/repository/ProductRepository.java b/src/main/java/com/microfocus/example/repository/ProductRepository.java new file mode 100644 index 0000000..62760f8 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/ProductRepository.java @@ -0,0 +1,183 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Product; +import com.microfocus.example.repository.mapper.ProductMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Repository +public class ProductRepository { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public ProductRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public int count() { + String sqlQuery = "select count(*) from products"; + return jdbcTemplate.queryForObject(sqlQuery, Integer.class); + } + + public List findAll(int offset, int limit) { + String sqlQuery = "select * from products" + + " LIMIT " + limit + " OFFSET " + offset; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public List findAvailable(int offset, int limit) { + String sqlQuery = "select * from products" + + " where available = true " + + " LIMIT " + limit + " OFFSET " + offset; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public Optional findById(UUID id) { + List result = new ArrayList<>(); + String query = id.toString(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE id = '" + query + "'"; + result = jdbcTemplate.query(sqlQuery, new ProductMapper()); + Optional optionalProduct = Optional.empty(); + if (!result.isEmpty()) { + optionalProduct = Optional.of(result.get(0)); + } + return optionalProduct; + } + + public boolean existsById(UUID id) { + List result = new ArrayList<>(); + String query = id.toString().toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE id = '" + query + "'"; + result = jdbcTemplate.query(sqlQuery, new ProductMapper()); + if (result.isEmpty()) { + return false; + } + return true; + } + + public Optional findByCode(String code) { + List result = new ArrayList<>(); + String query = code.toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE lower(code) = '" + query + "'"; + result = jdbcTemplate.query(sqlQuery, new ProductMapper()); + Optional optionalProduct = Optional.empty(); + if (!result.isEmpty()) { + optionalProduct = Optional.of(result.get(0)); + } + return optionalProduct; + } + + public List findByKeywords(String keywords, int offset, int limit) { + String query = keywords.toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE lower(name) LIKE '%" + query + "%' " + + " OR lower(summary) LIKE '%" + query + "%'" + + " OR lower(description) LIKE '%" + query + "%'" + + " LIMIT " + limit + " OFFSET " + offset; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public List findByKeywordsFromProductName(String keywords) { + String query = keywords.toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE lower(name) LIKE '%" + query + "%' "; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public List findAvailableByKeywords(String keywords, int offset, int limit) { + String query = keywords.toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE lower(name) LIKE '%" + query + "%' " + + " OR lower(summary) LIKE '%" + query + "%'" + + " OR lower(description) LIKE '%" + query + "%'" + + " AND available = true " + + " LIMIT " + limit + " OFFSET " + offset; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public List findAvailableByKeywordsFromProductName(String keywords) { + String query = keywords.toLowerCase(); + String sqlQuery = "SELECT * FROM " + getTableName() + + " WHERE available = true AND lower(name) LIKE '%" + query + "%' "; + return jdbcTemplate.query(sqlQuery, new ProductMapper()); + } + + public Product save(Product p) { + UUID uuid = (p.getId() != null ? p.getId() : UUID.randomUUID()); + Product pRet = null; + int status = 0; + if (p.getId() != null && existsById(p.getId())) { + log.debug("Updating existing product: " + p.getId()); + String sqlQuery = "UPDATE " + getTableName() + " SET" + + " code = ?, name = ?, rating = ?, summary = ?, description = ?, image = ?, price = ?," + + " on_sale = ?, sale_price = ?, in_stock = ?, time_to_stock = ?, available = ?" + + " WHERE id = ?"; + status = jdbcTemplate.update(sqlQuery, p.getCode(), p.getName(), p.getRating(), p.getSummary(), + p.getDescription(), p.getImage(), p.getPrice(), p.getOnSale(), p.getSalePrice(), p.getInStock(), + p.getTimeToStock(), p.getAvailable(), + p.getId()); + } else { + log.debug("Creating new product"); + String sqlQuery = "INSERT INTO " + getTableName() + + " (id, code, name, rating, summary, description, image, price, on_sale, sale_price, in_stock, time_to_stock, available)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + status = jdbcTemplate.update(sqlQuery, uuid.toString(), + p.getCode(), p.getName(), p.getRating(), p.getSummary(), p.getDescription(), p.getImage(), + p.getPrice(), p.getOnSale(), p.getSalePrice(), p.getInStock(), p.getTimeToStock(), p.getAvailable()); + } + if (status != 0) { + Optional optionalProduct = findById(uuid); + pRet = (optionalProduct.isPresent() ? optionalProduct.get() : null); + } + return pRet; + } + + public void deleteById(UUID id) { + if (existsById(id)) { + log.debug("Deleting existing product: " + id.toString()); + String sqlQuery = "DELETE FROM " + getTableName() + " WHERE id = ?"; + int status = jdbcTemplate.update(sqlQuery, id); + if (status != 0) { + log.debug("Successfully deleted product"); + } + } + } + + String getTableName() { + return Product.TABLE_NAME; + } + +} diff --git a/src/main/java/com/microfocus/example/repository/RefreshTokenRepository.java b/src/main/java/com/microfocus/example/repository/RefreshTokenRepository.java new file mode 100644 index 0000000..7d6caeb --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RefreshTokenRepository.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for Refresh Token Repository + * @author Kevin A. Lee + */ +public interface RefreshTokenRepository extends RefreshTokenRepositoryBasic, RefreshTokenRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryBasic.java new file mode 100644 index 0000000..36f7dda --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryBasic.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +/** + * Interface for Refresh Token Repository + * + * @author Kevin A. Lee + */ +public interface RefreshTokenRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryCustom.java new file mode 100644 index 0000000..638e581 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryCustom.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.User; +import org.springframework.data.jpa.repository.Modifying; + +/** + * Interface for Refresh Token Repository + * @author Kevin A. Lee + */ +public interface RefreshTokenRepositoryCustom { + + @Modifying + int deleteByUser(User user); + +} diff --git a/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryImpl.java new file mode 100644 index 0000000..8dadcee --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RefreshTokenRepositoryImpl.java @@ -0,0 +1,58 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; + +/** + * Implementation of Custom Refresh Token Repository + * @author Kevin A. Lee + */ +@Transactional +public class RefreshTokenRepositoryImpl implements RefreshTokenRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(RefreshTokenRepositoryImpl.class); + + private final RefreshTokenRepositoryBasic refreshTokenRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public RefreshTokenRepositoryImpl(RefreshTokenRepositoryBasic refreshTokenRepositoryBasic) { + this.refreshTokenRepositoryBasic = refreshTokenRepositoryBasic; + } + + @SuppressWarnings("unchecked") + public int deleteByUser(User user) { + Query query = entityManager.createQuery( + "DELETE FROM RefreshToken rt WHERE rt.user.id = ?1", + Long.class); + query.setParameter(1, user.getId()); + return (int)(query.getFirstResult()); + } + +} diff --git a/src/main/java/com/microfocus/example/repository/ReviewRepository.java b/src/main/java/com/microfocus/example/repository/ReviewRepository.java new file mode 100644 index 0000000..7933cfa --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/ReviewRepository.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for Product Review Repository + * @author Kevin A. Lee + */ +public interface ReviewRepository extends ReviewRepositoryBasic, ReviewRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/ReviewRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/ReviewRepositoryBasic.java new file mode 100644 index 0000000..958927c --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/ReviewRepositoryBasic.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Review; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +/** + * Interface for Product Review Repository + * + * @author Kevin A. Lee + */ +public interface ReviewRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/ReviewRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/ReviewRepositoryCustom.java new file mode 100644 index 0000000..939e5cf --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/ReviewRepositoryCustom.java @@ -0,0 +1,50 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Review; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Interface for Product Review Repository + * @author Kevin A. Lee + */ +public interface ReviewRepositoryCustom { + + public List findProductReviews(UUID productId); + + public List findByUserId(UUID userId); + + List findReviews(int offset, int limit); + + public long countByUserId(UUID userId); + + public long countByProductId(UUID productId); + + List findReviewsByKeywords(String keywords, int offset, int limit); + + List findProductReviewsByKeywords(UUID productId, String keywords, int offset, int limit); + + public Review addProductReview(UUID productId, UUID userId, String comment, int rating); + +} diff --git a/src/main/java/com/microfocus/example/repository/ReviewRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/ReviewRepositoryImpl.java new file mode 100644 index 0000000..9932c35 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/ReviewRepositoryImpl.java @@ -0,0 +1,155 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Review; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Implementation of Custom Product Review Repository + * @author Kevin A. Lee + */ +@Transactional +public class ReviewRepositoryImpl implements ReviewRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(ReviewRepositoryImpl.class); + + private final ReviewRepositoryBasic reviewRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public ReviewRepositoryImpl(ReviewRepositoryBasic reviewRepositoryBasic) { + this.reviewRepositoryBasic = reviewRepositoryBasic; + } + + @SuppressWarnings("unchecked") + public List findProductReviews(UUID productId) { + Query query = entityManager.createQuery( + "SELECT r FROM Review r WHERE r.product.id = ?1", + Review.class); + query.setParameter(1, productId); + return query.getResultList(); + } + + @SuppressWarnings("unchecked") + public List findByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT r FROM Review r WHERE r.user.id = ?1", + Review.class); + query.setParameter(1, userId); + return query.getResultList(); + } + + @SuppressWarnings("unchecked") + public List findReviews(int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT r FROM Review r", + Review.class); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public long countByProductId(UUID productId) { + Query query = entityManager.createQuery( + "SELECT count(r) FROM Review r WHERE r.product.id = ?1", + Long.class); + query.setParameter(1, productId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public long countByUserId(UUID userId) { + Query query = entityManager.createQuery( + "SELECT count(r) FROM Review r WHERE r.user.id = ?1", + Long.class); + query.setParameter(1, userId); + return (long)(query.getSingleResult()); + } + + @SuppressWarnings("unchecked") + public List findReviewsByKeywords(String keywords, int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT r FROM Review r WHERE lower(r.comment) LIKE lower(?1)", + Review.class); + q.setParameter(1, "%"+keywords+"%"); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public List findProductReviewsByKeywords(UUID productId, String keywords, int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT r FROM Review r WHERE r.product.id = '?1' AND lower(r.comment) LIKE lower(?2)", + Review.class); + q.setParameter(1, productId.toString()); + q.setParameter(2, "%"+keywords+"%"); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + public Review addProductReview(UUID productId, UUID userId, String comment, int rating) { + UUID reviewId = UUID.randomUUID(); + entityManager.createNativeQuery( + "INSERT INTO reviews (id, product_id, user_id, comment, rating) " + + "VALUES (" + + reviewId + "," + + productId + "," + + userId + "," + + "'" + comment + "'," + + rating + + ")") + .executeUpdate(); + return entityManager.find(Review.class, reviewId); + } + + /*public Review save(ReviewRequest Review) { + Review o = new Review(); + o.setReviewNum(Review.getReviewNum()); + o.setUser(Review.getUserId()); + o.setReviewDate(Review.getReviewDate()); + o.setAmount(Review.getAmount()); + o.setCart(Review.getCart()); + o.setShipped(Review.getShipped()); + o.setShippedDate(Review.getShippedDate()); + return ReviewRepositoryBasic.save(o); + }*/ + +} diff --git a/src/main/java/com/microfocus/example/repository/RoleRepository.java b/src/main/java/com/microfocus/example/repository/RoleRepository.java new file mode 100644 index 0000000..cda8436 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RoleRepository.java @@ -0,0 +1,32 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.Product; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Interface for Role Repository + * @author Kevin A. Lee + */ +public interface RoleRepository extends RoleRepositoryBasic, RoleRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/RoleRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/RoleRepositoryBasic.java new file mode 100644 index 0000000..1d9297f --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RoleRepositoryBasic.java @@ -0,0 +1,34 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Interface for Role Repository + * @author Kevin A. Lee + */ +@Repository +public interface RoleRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/RoleRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/RoleRepositoryCustom.java new file mode 100644 index 0000000..ab9d2ff --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RoleRepositoryCustom.java @@ -0,0 +1,36 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.User; + +import java.util.List; +import java.util.Optional; + +/** + * Interface for Custom Role Repository + * @author Kevin A. Lee + */ +public interface RoleRepositoryCustom { + + Optional findByName(String roleName); + +} diff --git a/src/main/java/com/microfocus/example/repository/RoleRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/RoleRepositoryImpl.java new file mode 100644 index 0000000..5096992 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/RoleRepositoryImpl.java @@ -0,0 +1,70 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.AuthorityType; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.UserLockedOutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.Query; +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Implementation of Custom Role Repository + * @author Kevin A. Lee + */ +@Repository +@Transactional +public class RoleRepositoryImpl implements RoleRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(RoleRepositoryImpl.class); + + private final RoleRepositoryBasic roleRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public RoleRepositoryImpl(RoleRepositoryBasic roleRepositoryBasic) { + this.roleRepositoryBasic = roleRepositoryBasic; + } + + @Override + @SuppressWarnings("unchecked") + public Optional findByName(String roleName) { + Query q = entityManager.createQuery( + "SELECT a FROM Authority a WHERE a.name = ?1", Authority.class); + q.setParameter(1, AuthorityType.valueOf(roleName)); + Authority result = (Authority) q.getSingleResult(); + Optional optionalAuthority = Optional.empty(); + if (q.getMaxResults() > 0) { + optionalAuthority = Optional.of(result); + } + return optionalAuthority; + } +} diff --git a/src/main/java/com/microfocus/example/repository/UserRepository.java b/src/main/java/com/microfocus/example/repository/UserRepository.java new file mode 100644 index 0000000..a3b5545 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/UserRepository.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for User Repository + * @author Kevin A. Lee + */ +public interface UserRepository extends UserRepositoryBasic, UserRepositoryCustom { + +} diff --git a/src/main/java/com/microfocus/example/repository/UserRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/UserRepositoryBasic.java new file mode 100644 index 0000000..d7914d7 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/UserRepositoryBasic.java @@ -0,0 +1,35 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +/** + * Interface for User Repository + * @author Kevin A. Lee + */ +@Repository +public interface UserRepositoryBasic extends JpaRepository { + +} diff --git a/src/main/java/com/microfocus/example/repository/UserRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/UserRepositoryCustom.java new file mode 100644 index 0000000..121820e --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/UserRepositoryCustom.java @@ -0,0 +1,45 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.User; + +import java.util.List; +import java.util.Optional; + +/** + * Interface for Custom User Repository + * @author Kevin A. Lee + */ +public interface UserRepositoryCustom { + + Optional findUserByUsername(String username); + + Optional findUserByEmail(String email); + + List listUsers(int offset, int limit); + + List findUsersByUsername(String username); + + List findUsersByKeywords(String keywords, int offset, int limit); + + List findUsersByEnabledAndUsername(boolean enabled, String username); + +} diff --git a/src/main/java/com/microfocus/example/repository/UserRepositoryImpl.java b/src/main/java/com/microfocus/example/repository/UserRepositoryImpl.java new file mode 100644 index 0000000..441aa9f --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/UserRepositoryImpl.java @@ -0,0 +1,204 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.AuthorityType; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.UserLockedOutException; +import javax.persistence.Query; + +import org.hibernate.Session; +import org.hibernate.jdbc.ReturningWork; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.*; + +/** + * Implementation of Custom User Repository + * @author Kevin A. Lee + */ +@Repository +@Transactional +public class UserRepositoryImpl implements UserRepositoryCustom { + + private static final Logger log = LoggerFactory.getLogger(UserRepositoryImpl.class); + + private final UserRepositoryBasic userRepositoryBasic; + + @PersistenceContext + EntityManager entityManager; + + public UserRepositoryImpl(UserRepositoryBasic userRepositoryBasic) { + this.userRepositoryBasic = userRepositoryBasic; + } + + @Override + @SuppressWarnings("unchecked") + public Optional findUserByUsername(String username) throws UserLockedOutException, UsernameNotFoundException { + List users = new ArrayList<>(); + + Session session = entityManager.unwrap(Session.class); + Integer authorityCount = session.doReturningWork(new ReturningWork() { + + @Override + public Integer execute(Connection con) throws SQLException { + Integer authorityCount = 0; + try { + Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ResultSet results = stmt.executeQuery( + "SELECT u.*, a.name as authority " + + "FROM users u, authorities a INNER JOIN user_authorities ua on a.id = ua.authority_id " + + "WHERE u.id = ua.user_id AND u.username LIKE '" + username + "'"); + if (results.next()) { + log.debug("Found matching user in database for: " + username); + results.beforeFirst(); + User utmp = new User(); + Set authorities = new HashSet<>(); + while (results.next()) { + if (authorityCount == 0) { + utmp = new User(results.getObject("id", UUID.class), + results.getString("username"), + results.getString("password"), + results.getString("first_name"), + results.getString("last_name"), + results.getString("email"), + results.getString("phone"), + results.getString("address"), + results.getString("city"), + results.getString("state"), + results.getString("zip"), + results.getString("country"), + results.getString("gender"), + results.getBoolean("enabled") + ); + utmp.setCountry(results.getString("country")); + utmp.setAddress(results.getString("address")); + utmp.setState(results.getString("state")); + utmp.setZip(results.getString("zip")); + log.debug("Adding authority " + results.getString("authority") + " for user"); + authorities.add(new Authority(AuthorityType.valueOf(results.getString("authority")))); + authorityCount++; + } else { + log.debug("Adding authority " + results.getString("authority") + " for user"); + authorities.add(new Authority(AuthorityType.valueOf(results.getString("authority")))); + } + } + if (!authorities.isEmpty()) { + utmp.setAuthorities(authorities); + } + users.add(utmp); + } else { + log.debug("No matching users found"); + } + } catch (SQLException ex) { + log.error(ex.getLocalizedMessage()); + } + return authorityCount; + } + }); + + Optional optionalUser = Optional.empty(); + if (!users.isEmpty()) { + optionalUser = Optional.of(users.get(0)); + } else { + log.debug("Unable to find username: " + username); + } + return optionalUser; + } + + @Override + @SuppressWarnings("unchecked") + public Optional findUserByEmail(String email) { + List users = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT u FROM User u WHERE u.email = :email", + User.class); + q.setParameter("email", email); + users = (List) q.getResultList(); + Optional optionalUser = Optional.empty(); + if (!users.isEmpty()) { + optionalUser = Optional.of(users.get(0)); + } else { + log.debug("Unable to find email: " + email); + } + return optionalUser; + } + + @SuppressWarnings("unchecked") + public List findUsersByUsername(String username) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT u FROM User u WHERE u.username = :username", + User.class); + q.setParameter("username", username); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public List listUsers(int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT u FROM User u", + User.class); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public List findUsersByKeywords(String keywords, int offset, int limit) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT u FROM User u WHERE lower(u.firstName) LIKE lower(?1)" + + " OR lower(u.lastName) LIKE lower(?1) ", + User.class); + q.setParameter(1, "%"+keywords+"%"); + q.setFirstResult(offset); + q.setMaxResults(limit); + result = (List)q.getResultList(); + return result; + } + + @SuppressWarnings("unchecked") + public List findUsersByEnabledAndUsername(boolean enabled, String username) { + List result = new ArrayList<>(); + Query q = entityManager.createQuery( + "SELECT u FROM User u WHERE u.enabled = :enabled AND u.username LIKE :username", + User.class); + q.setParameter("enabled", enabled); + q.setParameter("username", username); + result = (List)q.getResultList(); + return result; + } +} diff --git a/src/main/java/com/microfocus/example/repository/VerificationRepository.java b/src/main/java/com/microfocus/example/repository/VerificationRepository.java new file mode 100644 index 0000000..a18b52f --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/VerificationRepository.java @@ -0,0 +1,24 @@ +/* + Insecure Web App (IWA) + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +/** + * Interface for MFA Verification Repository + * @author Kevin A. Lee + */ +public interface VerificationRepository extends VerificationRepositoryBasic, VerificationRepositoryCustom { + +} \ No newline at end of file diff --git a/src/main/java/com/microfocus/example/repository/VerificationRepositoryBasic.java b/src/main/java/com/microfocus/example/repository/VerificationRepositoryBasic.java new file mode 100644 index 0000000..6ab988f --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/VerificationRepositoryBasic.java @@ -0,0 +1,28 @@ +/* + Insecure Web App (IWA) + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Verification; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Interface for MFA Verification Repository + * + * @author Kevin A. Lee + */ +public interface VerificationRepositoryBasic extends JpaRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/microfocus/example/repository/VerificationRepositoryCustom.java b/src/main/java/com/microfocus/example/repository/VerificationRepositoryCustom.java new file mode 100644 index 0000000..e42883a --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/VerificationRepositoryCustom.java @@ -0,0 +1,33 @@ +/* + Insecure Web App (IWA) + Copyright (C) 2022 Micro Focus or one of its affiliates + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.repository; + +import com.microfocus.example.entity.Verification; + +import java.util.Date; +import java.util.Optional; + +/** + * Interface for MFA Verification Repository + * @author Kevin A. Lee + */ +public interface VerificationRepositoryCustom { + + Optional findByPhone(String phone); + + void deleteByExpirationDateBefore(Date date); + +} \ No newline at end of file diff --git a/src/main/java/com/microfocus/example/repository/mapper/ProductMapper.java b/src/main/java/com/microfocus/example/repository/mapper/ProductMapper.java new file mode 100644 index 0000000..02f4bc4 --- /dev/null +++ b/src/main/java/com/microfocus/example/repository/mapper/ProductMapper.java @@ -0,0 +1,28 @@ +package com.microfocus.example.repository.mapper; + +import com.microfocus.example.entity.Product; +import org.springframework.jdbc.core.RowMapper; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +public class ProductMapper implements RowMapper { + public Product mapRow(ResultSet rs, int rowNum) throws SQLException { + Product product = new Product(); + product.setId(UUID.fromString(rs.getString("id"))); + product.setCode(rs.getString("code")); + product.setName(rs.getString("name")); + product.setRating(rs.getInt("rating")); + product.setSummary(rs.getString(("summary"))); + product.setDescription(rs.getString("description")); + product.setImage(rs.getString("image")); + product.setPrice(rs.getFloat("price")); + product.setOnSale(rs.getBoolean("on_sale")); + product.setSalePrice(rs.getFloat("sale_price")); + product.setInStock(rs.getBoolean("in_stock")); + product.setTimeToStock(rs.getInt("time_to_stock")); + product.setAvailable(rs.getBoolean("available")); + return product; + } +} diff --git a/src/main/java/com/microfocus/example/service/CustomUserDetailsService.java b/src/main/java/com/microfocus/example/service/CustomUserDetailsService.java new file mode 100644 index 0000000..3300ad2 --- /dev/null +++ b/src/main/java/com/microfocus/example/service/CustomUserDetailsService.java @@ -0,0 +1,67 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.service; + +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.UserLockedOutException; +import com.microfocus.example.repository.UserRepositoryCustom; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.Optional; + +/** + * Implementation of basic User Details Service for spring security database authentication + * @author Kevin A. Lee + */ +@Service +public class CustomUserDetailsService implements UserDetailsService { + + private static final Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class); + + @Autowired + private UserRepositoryCustom userRepository; + + @Transactional + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Optional user = Optional.ofNullable(null); + try { + user = userRepository.findUserByUsername(username); + if (!user.isPresent()) { + user = userRepository.findUserByEmail(username); + } + log.debug(String.valueOf(user)); + if (!user.isPresent()) { + throw new UsernameNotFoundException("User with email: " + username + " not found."); + } + } catch (UserLockedOutException ignored) { + // Do something here + } + return new CustomUserDetails(user.get()); + } +} diff --git a/src/main/java/com/microfocus/example/service/FileSystemStorageService.java b/src/main/java/com/microfocus/example/service/FileSystemStorageService.java new file mode 100644 index 0000000..a5a6197 --- /dev/null +++ b/src/main/java/com/microfocus/example/service/FileSystemStorageService.java @@ -0,0 +1,193 @@ +package com.microfocus.example.service; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import com.microfocus.example.config.StorageProperties; +import com.microfocus.example.exception.StorageException; +import com.microfocus.example.exception.StorageFileNotFoundException; +import org.apache.tika.Tika; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; +import org.springframework.web.multipart.MultipartFile; + +@Service +public class FileSystemStorageService implements StorageService { + + private static final Logger log = LoggerFactory.getLogger(FileSystemStorageService.class); + + private final Path rootLocation; + + @Autowired + public FileSystemStorageService(StorageProperties properties) { + this.rootLocation = Paths.get(properties.getLocation()); + if (!Files.exists(this.rootLocation)) { + log.debug("Creating storage service directory: " + rootLocation.toString()); + try { + Files.createDirectory(rootLocation); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void store(MultipartFile file) { + try { + if (file.isEmpty()) { + throw new StorageException("Failed to store empty file."); + } + Path destinationFile = this.rootLocation.resolve( + Paths.get(file.getOriginalFilename())) + .normalize().toAbsolutePath(); + if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) { + // This is a security check + throw new StorageException( + "Cannot store file outside current directory."); + } + try (InputStream inputStream = file.getInputStream()) { + Files.copy(inputStream, destinationFile, + StandardCopyOption.REPLACE_EXISTING); + } + } + catch (IOException e) { + throw new StorageException("Failed to store file.", e); + } + } + + @Override + public void store(Path path, String dstFileName) { + try { + if (Objects.isNull(path) || path.toFile().length() < 1L) { + throw new StorageException("Failed to store empty file."); + } + + Path destinationFile = Paths.get(dstFileName) + .normalize().toAbsolutePath(); + + if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) { + // This is a security check + throw new StorageException( + "Cannot store file outside current directory."); + } + + try (InputStream inputStream = Files.newInputStream(path)) { + Files.copy(inputStream, destinationFile, + StandardCopyOption.REPLACE_EXISTING); + } + } + catch (IOException e) { + throw new StorageException("Failed to store file.", e); + } + } + + private boolean checkMimeType(Path p, List mimeTypeList) { + Tika tika = new Tika(); + String fileMimeType = ""; + try { + fileMimeType = tika.detect(p.toFile()); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (mimeTypeList.contains(fileMimeType)) + return true; + + return false; + + } + + + @Override + public Stream loadAll(List mimeTypeList) { + try { + return Files.walk(this.rootLocation, 1) + .filter(path -> !path.equals(this.rootLocation)) + .filter(path -> checkMimeType(path, mimeTypeList)) + .map(this.rootLocation::relativize); + } + catch (IOException e) { + throw new StorageException("Failed to read stored files", e); + } + + } + + @Override + public Stream loadAll() { + try { + return Files.walk(this.rootLocation, 1) + .filter(path -> !path.equals(this.rootLocation)) + .map(this.rootLocation::relativize); + } + catch (IOException e) { + throw new StorageException("Failed to read stored files", e); + } + + } + + @Override + public Path load(String filename) { + return rootLocation.resolve(filename); + } + + @Override + public Resource loadAsResource(String filename) { + return loadAsResource(filename, false); + } + + /* + * To expose OWASP A01:2021 - Broken Access Control + */ + @Override + public Resource loadAsResource(String filename, boolean traverse) { + try { + Path file = null; + if (traverse) { + file = Paths.get(filename); + } else { + file = load(filename); + } + + Resource resource = new UrlResource(file.toUri()); + if (resource.exists() || resource.isReadable()) { + return resource; + } + else { + throw new StorageFileNotFoundException( + "Could not read file: " + filename); + + } + } + catch (MalformedURLException e) { + throw new StorageFileNotFoundException("Could not read file: " + filename, e); + } + } + + @Override + public void deleteAll() { + FileSystemUtils.deleteRecursively(rootLocation.toFile()); + } + + @Override + public void init() { + try { + Files.createDirectories(rootLocation); + } + catch (IOException e) { + throw new StorageException("Could not initialize storage", e); + } + } +} diff --git a/src/main/java/com/microfocus/example/service/ProductService.java b/src/main/java/com/microfocus/example/service/ProductService.java new file mode 100644 index 0000000..fa30de8 --- /dev/null +++ b/src/main/java/com/microfocus/example/service/ProductService.java @@ -0,0 +1,369 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.service; + +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.Review; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.OrderNotFoundException; +import com.microfocus.example.exception.ProductNotFoundException; +import com.microfocus.example.exception.ReviewNotFoundException; +import com.microfocus.example.payload.request.OrderRequest; +import com.microfocus.example.payload.request.ProductRequest; +import com.microfocus.example.payload.request.ReviewRequest; +import com.microfocus.example.repository.OrderRepository; +import com.microfocus.example.repository.ProductRepository; +import com.microfocus.example.repository.ReviewRepository; +import com.microfocus.example.repository.UserRepository; +import com.microfocus.example.web.form.OrderForm; +import com.microfocus.example.web.form.admin.AdminNewProductForm; +import com.microfocus.example.web.form.admin.AdminOrderForm; +import com.microfocus.example.web.form.admin.AdminProductForm; +import com.microfocus.example.web.form.admin.AdminReviewForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * Product Service for products and orders to hide business logs / database persistence + * + * @author Kevin A. Lee + */ +@Service +@Transactional +public class ProductService { + + private static final Logger log = LoggerFactory.getLogger(ProductService.class); + + @Autowired + private ProductRepository productRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private UserRepository userRepository; + + @Value("${app.data.page-size:25}") + private Integer pageSize; + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Optional findProductById(UUID id) { + return productRepository.findById(id); + } + + public Optional findProductByCode(String code) { + return productRepository.findByCode(code); + } + + public List getAllProducts() { + return productRepository.findAll(1, productRepository.count()); + } + + public List getAllProducts(Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return productRepository.findByKeywordsFromProductName(keywords); + } else { + return productRepository.findAll(offset, pageSize); + } + } + + public List getAllActiveProducts(Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return productRepository.findAvailableByKeywordsFromProductName(keywords); + } + return productRepository.findAvailable(offset, pageSize); + } + + public long count() { + return productRepository.count(); + } + + public Product saveProduct(Product product) { + return productRepository.save(product); + } + + public Product getProductById(UUID id) { + return productRepository.findById(id).get(); + } + + public void deleteProductById(UUID id) { + productRepository.deleteById(id); + } + + public boolean productExistsById(UUID id) { + return productRepository.existsById(id); + } + + public Product saveProductFromApi(UUID productId, ProductRequest product) { + Product ptmp = new Product(); + // are we creating a new product or updating an existing product? + if (productId == null) { + ptmp.setId(null); + } else { + ptmp.setId(productId); + // check it exists + if (!productExistsById(productId)) + throw new ProductNotFoundException("Product not found with id: " + productId); + } + ptmp.setCode(product.getCode()); + ptmp.setName(product.getName()); + ptmp.setSummary(product.getSummary()); + ptmp.setDescription(product.getDescription()); + ptmp.setImage(product.getImage()); + ptmp.setPrice(product.getPrice()); + ptmp.setOnSale(product.getOnSale()); + ptmp.setSalePrice(product.getSalePrice()); + ptmp.setInStock(product.getInStock()); + ptmp.setTimeToStock(product.getTimeToStock()); + ptmp.setRating(product.getRating()); + ptmp.setAvailable(product.getAvailable()); + return productRepository.save(ptmp); + } + + public Product saveProductFromAdminProductForm(AdminProductForm adminProductForm) throws ProductNotFoundException { + Optional optionalProduct = productRepository.findByCode(adminProductForm.getCode()); + if (optionalProduct.isPresent()) { + Product ptmp = optionalProduct.get(); + ptmp.setName(adminProductForm.getName()); + ptmp.setSummary(adminProductForm.getSummary()); + ptmp.setDescription(adminProductForm.getDescription()); + ptmp.setImage(adminProductForm.getImage()); + ptmp.setPrice(adminProductForm.getPrice()); + ptmp.setOnSale(adminProductForm.getOnSale()); + ptmp.setSalePrice(adminProductForm.getSalePrice()); + ptmp.setInStock(adminProductForm.getInStock()); + ptmp.setTimeToStock(adminProductForm.getTimeToStock()); + ptmp.setRating(adminProductForm.getRating()); + ptmp.setAvailable(adminProductForm.getAvailable()); + return productRepository.save(ptmp); + } else { + throw new ProductNotFoundException("Product not found: " + adminProductForm.getCode()); + } + } + + public Product newProductFormAdminNewProductForm(AdminNewProductForm productForm) { + Product ptmp = new Product(); + ptmp.setCode(productForm.getCode()); + ptmp.setName(productForm.getName()); + ptmp.setSummary(productForm.getSummary()); + ptmp.setDescription(productForm.getDescription()); + ptmp.setPrice(productForm.getPrice()); + ptmp.setOnSale(productForm.getOnSale() != null ? productForm.getOnSale() : false); + ptmp.setSalePrice(productForm.getSalePrice()); + ptmp.setInStock(productForm.getInStock() != null ? productForm.getInStock() : false); + ptmp.setTimeToStock(productForm.getTimeToStock()); + ptmp.setImage(productForm.getImage()); + ptmp.setAvailable(productForm.getAvailable() != null ? productForm.getAvailable() : false); + Product newProduct = productRepository.save(ptmp); + return newProduct; + } + + // + // Reviews + // + + public Optional findReviewById(UUID id) { + return reviewRepository.findById(id); + } + + public List findReviewsByProductId(UUID productId) { + return reviewRepository.findProductReviews(productId); + } + + public List findReviewByUserId(UUID userId) { + return reviewRepository.findByUserId(userId); + } + + public List getReviews() { return reviewRepository.findAll(); } + + public List getReviews(Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return reviewRepository.findReviewsByKeywords(keywords, offset, pageSize); + } else { + return reviewRepository.findReviews(offset, pageSize); + } + } + + public List getProductReviews(UUID pid) { + return reviewRepository.findProductReviews(pid); + } + + public List getProductReviews(UUID pid, Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return reviewRepository.findProductReviewsByKeywords(pid, keywords, offset, pageSize); + } else { + return reviewRepository.findProductReviews(pid); + } + } + + public boolean reviewExistsById(UUID id) { + return reviewRepository.existsById(id); + } + + public void deleteReviewById(UUID id) { + reviewRepository.deleteById(id); + } + + public Review saveReviewFromAdminReviewForm(AdminReviewForm adminReviewForm) throws ReviewNotFoundException { + Optional optionalReview = reviewRepository.findById(adminReviewForm.getId()); + if (optionalReview.isPresent()) { + Review rtmp = optionalReview.get(); + rtmp.setComment(adminReviewForm.getComment()); + rtmp.setRating(adminReviewForm.getRating()); + rtmp.setVisible(adminReviewForm.getVisible()); + return rtmp; + } else { + throw new ReviewNotFoundException("Review not found: " + adminReviewForm.getId()); + } + } + + public Review saveReviewFromApi(UUID reviewId, ReviewRequest review) { + Review rtmp = new Review(); + // are we creating a new review or updating an existing review? + if (reviewId == null) { + rtmp.setId(null); + rtmp.setReviewDate(new Date()); + } else { + rtmp.setId(reviewId); + rtmp.setReviewDate(rtmp.getReviewDate()); + // check it exists + if (!reviewExistsById(reviewId)) + throw new ReviewNotFoundException("Review not found with id: " + reviewId); + } + Optional optionalProduct = productRepository.findById(review.getProductId()); + if (optionalProduct.isPresent()) { + rtmp.setProduct(optionalProduct.get()); + } + Optional optionalUser = userRepository.findById(review.getUserId()); + if (optionalUser.isPresent()) { + rtmp.setUser(optionalUser.get()); + } + rtmp.setComment(review.getComment()); + rtmp.setRating(review.getRating()); + return reviewRepository.save(rtmp); + } + + // + // Orders + // + + public Order newOrderFromOrderForm(OrderForm orderForm) { + Order otmp = new Order(); + otmp.setUser(orderForm.getUser()); + otmp.setCart(orderForm.getCart()); + otmp.setAmount(orderForm.getAmount()); + otmp.setNotes((String)orderForm.getNotes()); + otmp.setOrderDate(new Date()); + Random r = new Random(); + int low = 10; + int high = 100; + int result = r.nextInt(high-low) + low; + String formatted = String.format("%03d", result); + otmp.setOrderNum("OID-P100-"+formatted); + Order newOrder = orderRepository.saveAndFlush(otmp); + return newOrder; + } + + public Optional findOrderById(UUID id) { + return orderRepository.findById(id); + } + + public Optional findOrderByNumber(String number) { + return orderRepository.findByNumber(number); + } + + public List getAllOrders() { return orderRepository.findAll(); } + + public List getAllOrders(Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return orderRepository.findOrdersByKeywords(keywords, offset, pageSize); + } + return orderRepository.listOrders(offset, pageSize); + } + + public void deleteOrderById(UUID id) { + orderRepository.deleteById(id); + } + + public Order saveOrderFromAdminOrderForm(AdminOrderForm adminOrderForm) throws OrderNotFoundException { + Optional optionalOrder = orderRepository.findById(adminOrderForm.getId()); + if (optionalOrder.isPresent()) { + Order otmp = optionalOrder.get(); + otmp.setOrderNum(adminOrderForm.getOrderNum()); + otmp.setOrderDate(adminOrderForm.getOrderDate()); + otmp.setAmount(adminOrderForm.getAmount()); + otmp.setCart(adminOrderForm.getCart()); + otmp.setShipped(adminOrderForm.getShipped()); + otmp.setShippedDate(adminOrderForm.getShippedDate()); + otmp.setNotes((String)adminOrderForm.getNotes()); + return otmp; + } else { + throw new ProductNotFoundException("Order not found: " + adminOrderForm.getOrderNum()); + } + } + + public boolean orderExistsById(UUID id) { + return orderRepository.existsById(id); + } + + public Order saveOrderFromApi(UUID orderId, OrderRequest order) { + Order otmp = new Order(); + // are we creating a new order or updating an existing order? + if (orderId == null) { + otmp.setId(null); + otmp.setOrderDate(new Date()); + } else { + otmp.setId(orderId); + otmp.setOrderDate(otmp.getOrderDate()); + // check it exists + if (!orderExistsById(orderId)) + throw new OrderNotFoundException("Order not found with id: " + orderId); + } + otmp.setOrderNum(order.getOrderNum()); + otmp.setCart(order.getCart()); + otmp.setAmount(order.getAmount()); + Optional optionalUser = userRepository.findById(order.getUserId()); + if (optionalUser.isPresent()) { + otmp.setUser(optionalUser.get()); + } + otmp.setShipped(order.getShipped()); + otmp.setShippedDate(order.getShippedDate()); + otmp.setNotes((String)order.getNotes()); + return orderRepository.save(otmp); + } + +} diff --git a/src/main/java/com/microfocus/example/service/RefreshTokenService.java b/src/main/java/com/microfocus/example/service/RefreshTokenService.java new file mode 100644 index 0000000..caf3fe5 --- /dev/null +++ b/src/main/java/com/microfocus/example/service/RefreshTokenService.java @@ -0,0 +1,84 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.service; + +import com.microfocus.example.entity.RefreshToken; +import com.microfocus.example.exception.ApiRefreshTokenException; +import com.microfocus.example.repository.RefreshTokenRepository; +import com.microfocus.example.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.Optional; +import java.util.UUID; + +/** + * Refresh Token Service to hide business logic / database persistence for Refresh Tokens + * @author Kevin A. Lee + */ +@Service +@Transactional +public class RefreshTokenService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final String SERVICE_NAME = getClass().getName(); + + @Value("${app.jwt.refresh-ms}") + private int jwtRefreshMs; + + @Autowired + private RefreshTokenRepository refreshTokenRepository; + + @Autowired + private UserRepository userRepository; + + public Optional findByToken(String token) { + return refreshTokenRepository.findById(UUID.fromString(token)); + } + + public RefreshToken createRefreshToken(UUID userId) { + RefreshToken refreshToken = new RefreshToken(); + + refreshToken.setUser(userRepository.findById(userId).get()); + refreshToken.setExpiryDate(Instant.now().plusMillis(jwtRefreshMs)); + refreshToken.setId(UUID.randomUUID()); + + refreshToken = refreshTokenRepository.save(refreshToken); + return refreshToken; + } + + public RefreshToken verifyExpiration(RefreshToken refreshToken) { + if (refreshToken.getExpiryDate().compareTo(Instant.now()) < 0) { + refreshTokenRepository.delete(refreshToken); + throw new ApiRefreshTokenException(refreshToken.getId().toString(), "Refresh token is expired - please sign-in again!"); + } + + return refreshToken; + } + + public int deleteByUserId(UUID userId) { + return refreshTokenRepository.deleteByUser((userRepository.findById(userId).get())); + } +} diff --git a/src/main/java/com/microfocus/example/service/StorageService.java b/src/main/java/com/microfocus/example/service/StorageService.java new file mode 100644 index 0000000..149676a --- /dev/null +++ b/src/main/java/com/microfocus/example/service/StorageService.java @@ -0,0 +1,29 @@ +package com.microfocus.example.service; + +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Stream; + +public interface StorageService { + + void init(); + + void store(MultipartFile file); + void store(Path path, String dstFileName); + + Stream loadAll(); + Stream loadAll(List mimeTypeList); + + Path load(String filename); + + Resource loadAsResource(String filename); + + Resource loadAsResource(String filename, boolean traverse); + + void deleteAll(); + +} diff --git a/src/main/java/com/microfocus/example/service/UserService.java b/src/main/java/com/microfocus/example/service/UserService.java new file mode 100644 index 0000000..17476bb --- /dev/null +++ b/src/main/java/com/microfocus/example/service/UserService.java @@ -0,0 +1,469 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.service; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.*; +import com.microfocus.example.payload.request.MessageRequest; +import com.microfocus.example.payload.request.RegisterUserRequest; +import com.microfocus.example.payload.request.SubscribeUserRequest; +import com.microfocus.example.payload.response.RegisterUserResponse; +import com.microfocus.example.payload.response.SubscribeUserResponse; +import com.microfocus.example.repository.MessageRepository; +import com.microfocus.example.repository.OrderRepository; +import com.microfocus.example.repository.RoleRepository; +import com.microfocus.example.repository.UserRepository; +import com.microfocus.example.utils.EncryptedPasswordUtils; +import com.microfocus.example.utils.UserUtils; +import com.microfocus.example.web.form.*; +import com.microfocus.example.web.form.admin.AdminNewUserForm; +import com.microfocus.example.web.form.admin.AdminPasswordForm; +import com.microfocus.example.web.form.admin.AdminUserForm; +import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.util.*; + +/** + * User Service to hide business logs / database persistence + * @author Kevin A. Lee + */ +@Service +@Transactional +public class UserService { + + private static final Logger log = LoggerFactory.getLogger(UserService.class); + + @Autowired + private UserRepository userRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private MessageRepository messageRepository; + + @Autowired + private OrderRepository orderRepository; + + @Value("${app.data.page-size:25}") + private Integer pageSize; + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Optional findUserById(UUID id) { + return userRepository.findById(id); + } + + public Optional findUserByUsername(String username) { + return userRepository.findUserByUsername(username); + } + + public Optional findUserByEmail(String email) { return userRepository.findUserByEmail(email); } + + public List getAllUsers() { + return (List) userRepository.findAll(); + } + + public List getAllUsers(Integer offset, String keywords) { + if (keywords != null && !keywords.isEmpty()) { + return userRepository.findUsersByKeywords(keywords, offset, pageSize); + } else { + return userRepository.listUsers(offset, pageSize); + } + } + + public User getUserById(UUID id) { + return userRepository.findById(id).get(); + } + + public void deleteUserById(UUID id) { + userRepository.deleteById(id); + } + + public List findUsersByUsername(String username) { + return userRepository.findUsersByUsername(username); + } + + public List findEnabledUsersByUsername(boolean enabled, String username) { + return userRepository.findUsersByEnabledAndUsername(enabled, username); + } + + public boolean userExistsById(UUID id) { + return userRepository.existsById(id); + } + + public User registerUser(RegisterUserForm newUser) throws UsernameTakenException, EmailAddressTakenException { + if (findUserByUsername(newUser.getUsername()).isPresent()) { + throw new UsernameTakenException(newUser.getUsername()); + } + if (findUserByEmail(newUser.getEmail()).isPresent()) { + throw new EmailAddressTakenException(newUser.getEmail()); + } + Set authorities = new HashSet(); + authorities.add(roleRepository.findByName("ROLE_USER").get()); + User utmp = new User(); + utmp.setUsername(newUser.getUsername()); + utmp.setFirstName(newUser.getFirstName()); + utmp.setLastName(newUser.getLastName()); + utmp.setPassword(EncryptedPasswordUtils.encryptPassword(newUser.getPassword())); + utmp.setEmail(newUser.getEmail()); + utmp.setPhone(newUser.getPhone()); + utmp.setEnabled(true); + utmp.setDateCreated(new Date()); + utmp.setAuthorities(authorities); + userRepository.saveAndFlush(utmp); + return utmp; + } + + public RegisterUserResponse registerUser(RegisterUserRequest newUser) throws UsernameTakenException, EmailAddressTakenException { + if (findUserByUsername(newUser.getUsername()).isPresent()) { + throw new UsernameTakenException(newUser.getUsername()); + } + if (findUserByEmail(newUser.getEmail()).isPresent()) { + throw new EmailAddressTakenException(newUser.getEmail()); + } + Set authorities = new HashSet(); + authorities.add(roleRepository.findByName("ROLE_USER").get()); + User utmp = new User(); + utmp.setUsername(newUser.getUsername()); + utmp.setFirstName(newUser.getFirstName()); + utmp.setLastName(newUser.getLastName()); + utmp.setPassword(EncryptedPasswordUtils.encryptPassword(newUser.getPassword())); + utmp.setEmail(newUser.getEmail()); + utmp.setPhone(newUser.getPhone()); + utmp.setEnabled(true); + utmp.setDateCreated(new Date()); + utmp.setAuthorities(authorities); + userRepository.saveAndFlush(utmp); + RegisterUserResponse registeredUser = new RegisterUserResponse(utmp.getUsername(), utmp.getPassword(), + newUser.getFirstName(), utmp.getLastName(), utmp.getEmail(), utmp.getPhone()); + return registeredUser; + } + + public SubscribeUserResponse subscribeUser(SubscribeUserRequest newUser) { + try { + UserUtils.registerUser(null, null, newUser.getEmail()); + } catch (IOException | ParseException e) { + e.printStackTrace(); + } + SubscribeUserResponse subscribedUser = new SubscribeUserResponse(newUser.getId(), null, null, newUser.getEmail()); + return subscribedUser; + } + + public User saveUser(User user) { + return userRepository.save(user); + } + + public User saveUserFromApi(User apiUser) throws InvalidPasswordException, UserNotFoundException { + Optional optionalUser = userRepository.findUserByUsername(apiUser.getUsername()); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + if (!apiUser.getPassword().isEmpty()) { + if (!EncryptedPasswordUtils.matches(apiUser.getPassword(), user.getPassword())) { + throw new InvalidPasswordException("Password is incorrect"); + } + } + user.setFirstName(apiUser.getFirstName()); + user.setLastName(apiUser.getLastName()); + user.setEmail(apiUser.getEmail()); + user.setPhone(apiUser.getPhone()); + user.setAddress(apiUser.getAddress()); + user.setCity(apiUser.getCity()); + user.setState(apiUser.getState()); + user.setZip(apiUser.getZip()); + user.setCountry(apiUser.getCountry()); + return userRepository.saveAndFlush(user); + } else { + throw new UserNotFoundException("Username not found: " + apiUser.getUsername()); + } + } + + public User saveUserFromUserForm(UserForm userForm) throws InvalidPasswordException, UserNotFoundException { + Optional optionalUser = userRepository.findUserByUsername(userForm.getUsername()); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + if (!EncryptedPasswordUtils.matches(userForm.getPassword(), user.getPassword())) { + throw new InvalidPasswordException("Password is incorrect"); + } + user.setFirstName(userForm.getFirstName()); + user.setLastName(userForm.getLastName()); + user.setEmail(userForm.getEmail()); + user.setPhone(userForm.getPhone()); + user.setAddress(user.getAddress()); + user.setCity(user.getCity()); + user.setState(user.getState()); + user.setZip(user.getZip()); + user.setCountry(user.getCountry()); + return userRepository.saveAndFlush(user); + } else { + throw new UserNotFoundException("Username not found: " + userForm.getUsername()); + } + } + + public User saveUserFromAdminUserForm(AdminUserForm adminUserForm) throws UserNotFoundException { + Optional optionalUser = userRepository.findUserByUsername(adminUserForm.getUsername()); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + user.setId(adminUserForm.getId()); + user.setUsername(adminUserForm.getUsername()); + user.setFirstName(adminUserForm.getFirstName()); + user.setLastName(adminUserForm.getLastName()); + user.setEmail(adminUserForm.getEmail()); + user.setPhone(adminUserForm.getPhone()); + user.setAddress(adminUserForm.getAddress()); + user.setCity(adminUserForm.getCity()); + user.setState(adminUserForm.getState()); + user.setZip(adminUserForm.getZip()); + user.setCountry(adminUserForm.getCountry()); + user.setEnabled(adminUserForm.getEnabled()); + return userRepository.saveAndFlush(user); + } else { + throw new UserNotFoundException("Username not found: " + adminUserForm.getUsername()); + } + } + + public User updateUserPasswordFromPasswordForm(UUID id, PasswordForm passwordForm) { + if (passwordForm.getPassword().equals(passwordForm.getConfirmPassword())) { + Optional optionalUser = userRepository.findById(id); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + user.setPassword(EncryptedPasswordUtils.encryptPassword(passwordForm.getPassword())); + return user; + } else { + throw new UserNotFoundException("Username not found: " + passwordForm.getUsername()); + } + } else { + throw new InvalidPasswordException("Password and Confirm Password fields do not match"); + } + } + + public User updateUserPasswordFromAdminPasswordForm(UUID id, AdminPasswordForm adminPasswordForm) { + if (adminPasswordForm.getPassword().equals(adminPasswordForm.getConfirmPassword())) { + Optional optionalUser = userRepository.findById(id); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + user.setPassword(EncryptedPasswordUtils.encryptPassword(adminPasswordForm.getPassword())); + return user; + } else { + throw new UserNotFoundException("Username not found: " + adminPasswordForm.getUsername()); + } + } else { + throw new InvalidPasswordException("Password and Confirm Password fields do not match"); + } + } + + public User addUserFromAdminNewUserForm(AdminNewUserForm adminNewUserForm) { + Set authorities = new HashSet(); + authorities.add(roleRepository.findByName("ROLE_USER").get()); + User utmp = new User(); + utmp.setUsername(adminNewUserForm.getUsername()); + utmp.setFirstName(adminNewUserForm.getFirstName()); + utmp.setLastName(adminNewUserForm.getLastName()); + utmp.setPassword(EncryptedPasswordUtils.encryptPassword(adminNewUserForm.getPassword())); + utmp.setEmail(adminNewUserForm.getEmail()); + utmp.setPhone(adminNewUserForm.getPhone()); + utmp.setAddress(adminNewUserForm.getAddress()); + utmp.setCity(adminNewUserForm.getCity()); + utmp.setState(adminNewUserForm.getState()); + utmp.setZip(adminNewUserForm.getZip()); + utmp.setCountry(adminNewUserForm.getCountry()); + utmp.setEnabled(adminNewUserForm.getEnabled()); + utmp.setEnabled(adminNewUserForm.getEnabled()); + utmp.setDateCreated(new Date()); + utmp.setAuthorities(authorities); + return userRepository.saveAndFlush(utmp); + } + + // + // User Roles + // + + public List getAllRoles() { + return roleRepository.findAll(); + } + + //public List getUserRoles(Integer userId) { + // return roleRepository.findByUserId(userId); + //} + + public boolean roleExistsById(Integer id) { + return roleRepository.existsById(id); + } + + public Optional findRoleById(Integer id) { + return roleRepository.findById(id); + } + + public Authority saveRole(Authority role) { + return roleRepository.save(role); + } + + public void deleteRoleById(Integer id) { + roleRepository.deleteById(id); + } + + // + // User Messages + // + + public List getAllMessages() { + return messageRepository.findAll(); + } + + public long getUserMessageCount(UUID userId) { + return messageRepository.countByUserId(userId); + } + + public long getUserUnreadMessageCount(UUID userId) { + return messageRepository.countUnreadByUserId(userId); + } + + public List getUserMessages(UUID userId) { + return messageRepository.findByUserId(userId); + } + + public boolean messageExistsById(UUID id) { + return messageRepository.existsById(id); + } + + public Optional findMessageById(UUID id) { + return messageRepository.findById(id); + } + + public Message saveMessage(Message message) { + return messageRepository.save(message); + } + + public Message saveMessage(MessageForm message) { + return messageRepository.save(message); + } + + public Message saveMessageFromApi(UUID messageId, MessageRequest message) throws MessageNotFoundException, UserNotFoundException { + Message mtmp = new Message(); + Optional optionalUser = userRepository.findById(message.getUserId()); + if (optionalUser.isPresent()) { + mtmp.setUser(optionalUser.get()); + // are we creating a new message or updating an existing message? + if (messageId == null) { + mtmp.setId(null); + } else { + mtmp.setId(messageId); + // check it exists + if (!messageExistsById(messageId)) + throw new MessageNotFoundException("Message not found with id: " + messageId); + } + mtmp.setText(message.getText()); + if (message.getSentDate() != null) mtmp.setSentDate(message.getSentDate()); + return messageRepository.save(mtmp); + } else { + throw new UserNotFoundException("User not found with id: " + message.getUserId()); + } + } + + public void deleteMessageById(UUID id) { + messageRepository.deleteById(id); + } + + public void markMessageAsReadById(UUID id) { messageRepository.markMessageAsReadById(id); } + + // + // User Orders + // + + public List getAllOrders() { + return orderRepository.findAll(); + } + + public long getUserOrderCount(UUID userId) { + return orderRepository.countByUserId(userId); + } + + public long getUserUnshippedOrderCount(UUID userId) { + return orderRepository.countNotShippedByUserId(userId); + } + + public List getUserOrders(UUID userId) { + return orderRepository.findByUserId(userId); + } + + public boolean orderExistsById(UUID id) { + return orderRepository.existsById(id); + } + + public Optional findOrderById(UUID id) { + return orderRepository.findById(id); + } + + public Order saveOrder(Order order) { + return orderRepository.save(order); + } + + /* + public Message saveOrder(MessageForm message) { + return messageRepository.save(message); + } + + + public Message saveMessageFromApi(UUID messageId, MessageRequest message) throws MessageNotFoundException, UserNotFoundException { + Message mtmp = new Message(); + Optional optionalUser = userRepository.findById(message.getUserId()); + if (optionalUser.isPresent()) { + mtmp.setUser(optionalUser.get()); + // are we creating a new message or updating an existing message? + if (messageId == null) { + mtmp.setId(null); + } else { + mtmp.setId(messageId); + // check it exists + if (!messageExistsById(messageId)) + throw new MessageNotFoundException("Message not found with id: " + messageId); + } + mtmp.setText(message.getText()); + if (message.getSentDate() != null) mtmp.setSentDate(message.getSentDate()); + return messageRepository.save(mtmp); + } else { + throw new UserNotFoundException("User not found with id: " + message.getUserId()); + } + } + + public void deleteMessageById(UUID id) { + messageRepository.deleteById(id); + } + + public void markMessageAsReadById(UUID id) { messageRepository.markMessageAsReadById(id); } + + */ +} diff --git a/src/main/java/com/microfocus/example/utils/AdminUtils.java b/src/main/java/com/microfocus/example/utils/AdminUtils.java new file mode 100644 index 0000000..21979ce --- /dev/null +++ b/src/main/java/com/microfocus/example/utils/AdminUtils.java @@ -0,0 +1,131 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.utils; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Random; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.microfocus.example.exception.BackupException; + +/** + * Administration Utilities + * + * @author Kevin. A. Lee + */ +public class AdminUtils { + + private static final Logger log = LoggerFactory.getLogger(AdminUtils.class); + + public static int startDbBackup(String profile) throws BackupException { + int backupId = 0; + +// Uncomment the following for more secure example: +/* + String profileName = "default"; + switch (profile) { + case "quick": profileName = "quick"; + break; + case "full": profileName = "full"; + break; + default: profileName = "full"; + break; + } + if (profile.matches("^.*[^a-zA-Z0-9 ].*$")) + throw new BackupException("Profile contains non alpha numeric characters, cannot start backup"); +*/ + + String[] backupCommand = { + "cmd.exe", "/K", "dir", "c:\\util\\backup.bat", + "-profile", profile + }; + String[] cleanupCommand = { + "cmd.exe", "/K", "c:\\util\\cleanup.bat" + }; + log.info("Running: " + Arrays.toString(backupCommand)); + // call backup tool API + log.info("Running: " + Arrays.toString(cleanupCommand)); + + // call backup tool API + backupId = getBackupId(); + return backupId; + } + + public static int startDbRestore(int backupId) { + int restoreId = 0; + // Note: database restore not currently supported from website + return restoreId; + } + + public static String getDbStatus(int backupId) { + +// Uncomment the following for more secure example: +/* + if (Boolean.parseBoolean(isLocked(backupId))) { + return "LOCKED"; + } +*/ + + if(Boolean.getBoolean(isLocked(backupId))){ + return"LOCKED"; + } + return isReady(backupId); + } + + public static int getBackupId() { + return genId(); + } + + // + // Private methods + // + + private static int genId() { + +// Uncomment the following for more secure example: +/* + SecureRandom sr = new SecureRandom(); + try { + sr = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException ignored) { + log.error(ignored.getMessage()); + } + return sr.nextInt(Integer.MAX_VALUE); + */ + + Random r=new Random(); + r.setSeed(12345); + return r.nextInt(); + } + + private static String isLocked(int backupId) { + return "FALSE"; + } + + private static String isReady(int backupId) { + return "READY"; + } + +} diff --git a/src/main/java/com/microfocus/example/utils/EncryptedPasswordUtils.java b/src/main/java/com/microfocus/example/utils/EncryptedPasswordUtils.java new file mode 100644 index 0000000..93ca45a --- /dev/null +++ b/src/main/java/com/microfocus/example/utils/EncryptedPasswordUtils.java @@ -0,0 +1,75 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.utils; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +/** + * Encrypted Password Utilities using BCryptPasswordEncoder + * + * @author Kevin A. Lee + */ +@SuppressWarnings("deprecation") +public class EncryptedPasswordUtils { + + private static final byte[] iv = { 22, 33, 11, 44, 55, 99, 66, 77 }; + private static final SecretKey keySpec = new SecretKeySpec(iv, "DES"); + + public static String encryptPassword(String password) { + byte[] encrypted = null; + try { + Cipher desCipher = Cipher.getInstance("DES"); + desCipher.init(Cipher.ENCRYPT_MODE, keySpec); + encrypted = desCipher.doFinal(password.getBytes()); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + + return new String(encrypted); + } + + public static boolean matches(String password1, String password2) { + byte[] encrypted = null; + String encPassword1 = ""; + try { + Cipher desCipher = Cipher.getInstance("DES"); + desCipher.init(Cipher.ENCRYPT_MODE, keySpec); + encrypted = desCipher.doFinal(password1.getBytes()); + encPassword1 = new String(encrypted); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + + return encPassword1.equals(password2); + } + +} diff --git a/src/main/java/com/microfocus/example/utils/JwtUtils.java b/src/main/java/com/microfocus/example/utils/JwtUtils.java new file mode 100644 index 0000000..558fb13 --- /dev/null +++ b/src/main/java/com/microfocus/example/utils/JwtUtils.java @@ -0,0 +1,131 @@ +/* + Insecure Web App (IWA) + + Copyright 2020-2023 Open Text or one of its affiliates. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.utils; + +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.User; +import io.jsonwebtoken.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.time.Instant; +import java.util.Date; + +@Component +public class JwtUtils { + private static final Logger log = LoggerFactory.getLogger(JwtUtils.class); + + @Value("${app.jwt.secret}") + private String jwtSecret; + + @Value("#{new Integer(${app.jwt.expiration-ms:0})}") + private int jwtExpirationMs; + + @Value("#{new Integer(${app.jwt.refresh-ms:0})}") + private int jwtRefreshMs; + + public String generateJwtToken(Authentication authentication) { + + CustomUserDetails userPrincipal = (CustomUserDetails) authentication.getPrincipal(); + log.debug("generateJwtToken for: " + userPrincipal.getUsername()); + + return Jwts.builder() + .setSubject((userPrincipal.getUsername())) + .setHeaderParam("kid","/root/res/keys/secret.key") + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(SignatureAlgorithm.HS256, jwtSecret) + .compact(); + } + + public String generateJwtTokenFromUsername(String username) { + log.debug("JwtUtils::generateJwtTokenFromUsername for: {}", username); + return Jwts.builder() + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) + .signWith(SignatureAlgorithm.HS512, jwtSecret) + .compact(); + } + + public String refreshJwtToken(Authentication authentication) { + CustomUserDetails userPrincipal = (CustomUserDetails) authentication.getPrincipal(); + + return Jwts.builder() + .setSubject((userPrincipal.getUsername())) + .setHeaderParam("kid","/root/res/keys/secret.key") + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtRefreshMs)) + .signWith(SignatureAlgorithm.HS256, jwtSecret) + .compact(); + } + + public Instant getDefaultExpiration() { + return Instant.now().plusMillis(jwtRefreshMs); + } + + public long getExpirationFromJwtToken(String token) { + return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getExpiration().getTime(); + } + + public String getUserNameFromJwtToken(String token) { + return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject(); + } + + public boolean validateJwtToken(String authToken) { + try { + Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + log.error("Invalid JWT signature: {}", e.getMessage()); + } catch (MalformedJwtException e) { + log.error("Invalid JWT token: {}", e.getMessage()); + } catch (ExpiredJwtException e) { + log.error("JWT token is expired: {}", e.getMessage()); + } catch (UnsupportedJwtException e) { + log.error("JWT token is unsupported: {}", e.getMessage()); + } catch (IllegalArgumentException e) { + log.error("JWT claims string is empty: {}", e.getMessage()); + } + + return false; + } + + public String generateAndSetSession(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) { + HttpSession session = request.getSession(false); + CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); + User user = customUserDetails.getUserDetails(); + String jwtToken = generateJwtToken(authentication); + log.debug("Generated jwtToken: " + jwtToken); + session.setAttribute("userId", user.getId()); + session.setAttribute("username", user.getUsername()); + session.setAttribute("authorities", authentication.getAuthorities()); + session.setAttribute("jwtToken", jwtToken); + response.addHeader("kid",jwtToken); + return jwtToken; + } +} diff --git a/src/main/java/com/microfocus/example/utils/UserUtils.java b/src/main/java/com/microfocus/example/utils/UserUtils.java new file mode 100644 index 0000000..30f834a --- /dev/null +++ b/src/main/java/com/microfocus/example/utils/UserUtils.java @@ -0,0 +1,141 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.utils; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.Enumeration; +import java.util.NoSuchElementException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + + +public class UserUtils { + + private static final Logger log = LoggerFactory.getLogger(AdminUtils.class); + + public static final String USER_INFO_FILE = "user_info.json"; + public static final String NEWSLETTER_USER_FILE = "newsletter_registration.json"; + public static final String DEFAULT_ROLE = "guest"; + + public static void writeUser(String username, String password) throws IOException { + JsonFactory jsonFactory = new JsonFactory(); + + File dataFile = new File(getFilePath(USER_INFO_FILE)); + if (dataFile.createNewFile()){ + log.debug("Created: " + getFilePath(USER_INFO_FILE)); + } + + JsonGenerator jGenerator = jsonFactory.createGenerator(dataFile, JsonEncoding.UTF8); + + jGenerator.writeStartObject(); + + jGenerator.writeFieldName("username"); + jGenerator.writeRawValue("\"" + username + "\""); + + jGenerator.writeFieldName("password"); + jGenerator.writeRawValue("\"" + password + "\""); + + jGenerator.writeFieldName("role"); + jGenerator.writeRawValue("\"default\""); + + jGenerator.writeEndObject(); + + jGenerator.close(); + } + + public static void registerUser(String firstName, String lastName, String email) throws IOException, ParseException { + JsonFactory jsonFactory = new JsonFactory(); + + JSONParser jsonParser = new JSONParser(); + JSONArray jsonArray = new JSONArray(); + + File dataFile = new File(getFilePath(NEWSLETTER_USER_FILE)); + if (dataFile.exists()) { + jsonArray = (JSONArray) jsonParser.parse(new FileReader(getFilePath(NEWSLETTER_USER_FILE))); + } else { + dataFile.createNewFile(); + log.debug("Created: " + getFilePath(NEWSLETTER_USER_FILE)); + } + + try (OutputStream fos = new FileOutputStream(dataFile, false)) { + + JsonGenerator jGenerator = jsonFactory.createGenerator(fos, JsonEncoding.UTF8); + jGenerator.writeStartArray(); + + for (Object jsonObject : jsonArray) + { + jGenerator.writeStartObject(); + JSONObject person = (JSONObject) jsonObject; + jGenerator.writeFieldName("firstName"); + jGenerator.writeRawValue("\"" + (String) person.get("firstName") + "\""); + jGenerator.writeFieldName("lastName"); + jGenerator.writeRawValue("\"" + (String) person.get("lastName") + "\""); + jGenerator.writeFieldName("email"); + jGenerator.writeRawValue("\"" + (String) person.get("email") + "\""); + jGenerator.writeFieldName("role"); + jGenerator.writeRawValue("\"" + (String) person.get("role") + "\""); + jGenerator.writeEndObject(); + + } + + // write new user + jGenerator.writeStartObject(); + jGenerator.writeFieldName("firstName"); + jGenerator.writeRawValue("\"" + firstName + "\""); + jGenerator.writeFieldName("lastName"); + jGenerator.writeRawValue("\"" + lastName + "\""); + jGenerator.writeFieldName("email"); + jGenerator.writeRawValue("\"" + email + "\""); + jGenerator.writeFieldName("role"); + jGenerator.writeRawValue("\"" + DEFAULT_ROLE + "\""); + jGenerator.writeEndObject(); + + jGenerator.writeEndArray(); + + jGenerator.close(); + } + + } + + public void logZipContents(String fName) + throws IOException, SecurityException, IllegalStateException, NoSuchElementException { + ZipFile zf = new ZipFile(fName); + @SuppressWarnings("unchecked") + Enumeration e = (Enumeration) zf.entries(); + while (e.hasMoreElements()) { + log.info(e.nextElement().toString()); + } + } + + private static String getFilePath(String relativePath) { + return System.getProperty("user.home") + File.separatorChar + relativePath; + } + +} diff --git a/src/main/java/com/microfocus/example/utils/WebUtils.java b/src/main/java/com/microfocus/example/utils/WebUtils.java new file mode 100644 index 0000000..15e116a --- /dev/null +++ b/src/main/java/com/microfocus/example/utils/WebUtils.java @@ -0,0 +1,68 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.utils; + +import com.microfocus.example.entity.Authority; +import com.microfocus.example.entity.User; +import org.springframework.security.core.Authentication; + +import java.security.Principal; +import java.util.Set; + +/** + * Customer Web Utilities + * + * @author Kevin. A. Lee + */ +public class WebUtils { + + public static String toString(User user) { + StringBuilder sb = new StringBuilder(); + + sb.append("UserName:").append(user.getUsername()); + + Set authorities = user.getAuthorities(); + if (authorities != null && !authorities.isEmpty()) { + sb.append(" ("); + boolean first = true; + for (Authority a : authorities) { + if (first) { + sb.append(a.getName()); + first = false; + } else { + sb.append(", ").append(a.getName()); + } + } + sb.append(")"); + } + return sb.toString(); + } + + public static User getLoggedInUser(Principal principal) { + User loggedInUser = null; + if (principal != null) { + org.springframework.security.core.userdetails.UserDetails user = + (org.springframework.security.core.userdetails.UserDetails) ((Authentication) principal).getPrincipal(); + loggedInUser = User.fromUserDetails(user); + } + return loggedInUser; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/AbstractBaseController.java b/src/main/java/com/microfocus/example/web/controllers/AbstractBaseController.java new file mode 100644 index 0000000..ce42137 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/AbstractBaseController.java @@ -0,0 +1,49 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.utils.WebUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ui.Model; + +import java.security.Principal; +import java.util.Currency; +import java.util.Locale; + +public abstract class AbstractBaseController { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + abstract LocaleConfiguration GetLocaleConfiguration(); + abstract String GetControllerName(); + + final Model setModelDefaults(Model model, Principal principal, String viewName) { + Locale currentLocale = GetLocaleConfiguration().getLocale(); + Currency currency = Currency.getInstance(currentLocale); + model.addAttribute("currencySymbol", currency.getSymbol()); + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("messageCount", "0"); + model.addAttribute("controllerName", GetControllerName()); + model.addAttribute("viewName", viewName); + return model; + } +} diff --git a/src/main/java/com/microfocus/example/web/controllers/CartController.java b/src/main/java/com/microfocus/example/web/controllers/CartController.java new file mode 100644 index 0000000..5f89f27 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/CartController.java @@ -0,0 +1,146 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.UserNotFoundException; +import com.microfocus.example.service.ProductService; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.OrderForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.*; + +/** + * Controller for shopping cart pages + * + * @author Kevin A. Lee + */ +@RequestMapping("/cart") +@Controller +public class CartController extends AbstractBaseController { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final String CONTROLLER_NAME = getClass().getName(); + + @Autowired + private ProductService productService; + + @Autowired + private UserService userService; + + @Autowired + LocaleConfiguration localeConfiguration; + + @Override + LocaleConfiguration GetLocaleConfiguration() { + return localeConfiguration; + } + + @Override + String GetControllerName() { + return CONTROLLER_NAME; + } + + @GetMapping(value = {"", "/"}) + public String index(Model model, Principal principal) { + this.setModelDefaults(model, principal, "index"); + return "cart/index"; + } + + @GetMapping("/checkout") + public String checkout(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + User utmp = optionalUser.get(); + OrderForm orderForm = new OrderForm(); + orderForm.setUser(utmp); + model.addAttribute("orderForm", orderForm); + model.addAttribute("userInfo", WebUtils.toString(user.getUserDetails())); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "checkout"); + return "cart/checkout"; + } + + @PostMapping("/order") + public String cartOrder(@Valid @ModelAttribute("orderForm") OrderForm orderForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (bindingResult.hasErrors()) { + this.setModelDefaults(model, principal, "confirm"); + return "cart/confirm"; + } else { + try { + //userService.saveUserFromUserForm(userForm); + log.info("orderNotes: " + orderForm.getNotes()); + ObjectMapper MAPPER = new JsonMapper(); + try { + Object orderNotes = MAPPER.readValue((String)orderForm.getNotes(), Object.class); + orderForm.setNotes(orderNotes); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Order otmp = productService.newOrderFromOrderForm(orderForm); + redirectAttributes.addFlashAttribute("orderNum", otmp.getOrderNum()); + redirectAttributes.addFlashAttribute("orderId", otmp.getId()); + this.setModelDefaults(model, principal, "confirm"); + return "redirect:/cart/confirm"; + } catch (UserNotFoundException ex) { + log.error("UserNotFoundException saving profile: " + principal.toString()); + //FieldError usernameError = new FieldError("userForm", "username", ex.getMessage()); + //bindingResult.addError(usernameError); + } + } + this.setModelDefaults(model, principal, "confirm"); + return "cart/confirm"; + } + + @GetMapping("/confirm") + public String order(Model model, Principal principal) { + this.setModelDefaults(model, principal, "confirm"); + return "cart/confirm"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/CustomErrorController.java b/src/main/java/com/microfocus/example/web/controllers/CustomErrorController.java new file mode 100644 index 0000000..ff2557d --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/CustomErrorController.java @@ -0,0 +1,57 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletResponse; + +@RestController +public class CustomErrorController implements ErrorController { + + @RequestMapping("/error") + public ModelAndView handleError(HttpServletResponse response) { + ModelAndView modelAndView = new ModelAndView(); + + if(response.getStatus() == HttpStatus.NOT_FOUND.value()) { + modelAndView.setViewName("error/404-not-found"); + } + else if(response.getStatus() == HttpStatus.FORBIDDEN.value()) { + modelAndView.setViewName("error/403-access-denied"); + } + else if(response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value()) { + modelAndView.setViewName("error/500-internal-error"); + } + else { + modelAndView.setViewName("error/default"); + } + + return modelAndView; + } + + @Override + public String getErrorPath() { + return "/error"; + } +} diff --git a/src/main/java/com/microfocus/example/web/controllers/DefaultController.java b/src/main/java/com/microfocus/example/web/controllers/DefaultController.java new file mode 100644 index 0000000..1d2ad47 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/DefaultController.java @@ -0,0 +1,155 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.config.handlers.CustomAuthenticationSuccessHandler; +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.utils.JwtUtils; +import com.microfocus.example.utils.WebUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Scope; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.security.Principal; + +/** + * Default (root) controllers + * + * @author Kevin A. Lee + */ +@SessionAttributes({"currentUser", "currentUserId"}) +@Controller +@Scope("session") +public class DefaultController extends AbstractBaseController{ + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final String CONTROLLER_NAME = getClass().getName(); + + @Value("${app.messages.home}") + private String message = "Hello World"; + + @Autowired + private JwtUtils jwtUtils; + + @Autowired + LocaleConfiguration localeConfiguration; + + @Override + LocaleConfiguration GetLocaleConfiguration() { + return localeConfiguration; + } + + @Override + String GetControllerName() { + return CONTROLLER_NAME; + } + + @GetMapping("/") + public String index(Model model, Principal principal) { + model.addAttribute("message", message); + this.setModelDefaults(model, principal, "index"); + return "index"; + } + + @GetMapping("/login") + public String login(HttpServletRequest request, Model model, Principal principal) { + HttpSession session = request.getSession(false); + String referer = (String) request.getHeader("referer"); + session.setAttribute("loginReferer", referer); + this.setModelDefaults(model, principal, "login"); + return "login"; + } + + @GetMapping("/verify") + public String verify(HttpServletRequest request, Model model, Principal principal) { + return "verify"; + } + + @PostMapping("/verify") + public String verify(HttpServletRequest request, HttpServletResponse response, + @RequestParam("otp") String otp, Model model, Principal principal) { + Authentication authentication = (Authentication) principal; + String jwtToken = jwtUtils.generateAndSetSession(request, response, authentication); + String targetUrl = CustomAuthenticationSuccessHandler.getTargetUrl(request, response, authentication); + return "redirect:"+targetUrl; + } + + @GetMapping("/services") + public String services(Model model, Principal principal) { + this.setModelDefaults(model, principal, "services"); + return "services"; + } + + @GetMapping("/prescriptions") + public String prescriptions(Model model, Principal principal) { + this.setModelDefaults(model, principal, "prescriptions"); + return "prescriptions"; + } + + @GetMapping("/advice") + public String advice(Model model, Principal principal) { + this.setModelDefaults(model, principal, "advice"); + return "advice"; + } + + @GetMapping("/access-denied") + public String accessDenied(Model model, Principal principal) { + if (principal != null) { + log.debug("DefaultController:accessDenied: " + principal.toString()); + CustomUserDetails loggedInUser = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + model.addAttribute("user", WebUtils.toString(loggedInUser.getUserDetails())); + String message = "Sorry " + principal.getName() + " - " // + + "you do not have permission to access this page."; + model.addAttribute("message", message); + } + this.setModelDefaults(model, principal, "403-access-denied"); + return "error/403-access-denied"; + } + + @GetMapping("/backdoor") + public String backdoor(Model model, Principal principal) { + log.debug("Oops! Someone has found the backdoor!"); + this.setModelDefaults(model, principal, "backdoor"); + return "admin/backdoor"; + } + + @GetMapping("/not-yet-implemented") + public String notYetImplemented(Model model, Principal principal) { + this.setModelDefaults(model, principal, "not-implemented"); + return "error/not-implemented"; + } + + @GetMapping("/site-message") + @ResponseBody + public String siteMessage() { + return "This site is currently healthy."; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/ProductController.java b/src/main/java/com/microfocus/example/web/controllers/ProductController.java new file mode 100644 index 0000000..8a03f0b --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/ProductController.java @@ -0,0 +1,181 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.Product; +import com.microfocus.example.exception.ServerErrorException; +import com.microfocus.example.service.ProductService; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.data.repository.query.Param; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.ResourceUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Principal; +import java.util.*; + +/** + * Controller for product pages + * + * @author Kevin A. Lee + */ +@RequestMapping("/products") +@Controller +public class ProductController extends AbstractBaseController { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final String CONTROLLER_NAME = getClass().getName(); + + @Value("${app.data.page-size:25}") + private Integer defaultPageSize; + + @Autowired + ProductService productService; + + @Autowired + LocaleConfiguration localeConfiguration; + + @Override + LocaleConfiguration GetLocaleConfiguration() { + return localeConfiguration; + } + + @Override + String GetControllerName() { + return CONTROLLER_NAME; + } + + @GetMapping("/xss") + @ResponseBody + public ResponseEntity getKeywordsContent(@Param("keywords") String keywords) { + + String retContent = "Product search using: " + keywords; + + return ResponseEntity.ok().body(retContent); + } + + @GetMapping("/firstaid") + public String firstaid(Model model, @Param("keywords") String keywords, @Param("limit") Integer limit, Principal principal) { + log.debug("Searching for products using keywords: " + ((keywords == null || keywords.isEmpty()) ? "none" : keywords)); + productService.setPageSize((limit == null ? defaultPageSize : limit)); + List products = productService.getAllActiveProducts(0, keywords); + model.addAttribute("keywords", keywords); + model.addAttribute("products", products); + model.addAttribute("productCount", products.size()); + model.addAttribute("productTotal", productService.count()); + this.setModelDefaults(model, principal, "index"); + return "products/firstaid"; + } + + @GetMapping(value = {"", "/"}) + public String index(Model model, @Param("keywords") String keywords, @Param("limit") Integer limit, Principal principal) { + log.debug("Searching for products using keywords: " + ((keywords == null || keywords.isEmpty()) ? "none" : keywords)); + productService.setPageSize((limit == null ? defaultPageSize : limit)); + try { + List products = productService.getAllActiveProducts(0, keywords); + model.addAttribute("keywords", keywords); + model.addAttribute("products", products); + model.addAttribute("productCount", products.size()); + model.addAttribute("productTotal", productService.count()); + this.setModelDefaults(model, principal, "index"); + } catch (Exception ex) { + throw new ServerErrorException(ExceptionUtils.getStackTrace(ex)); + } + return "products/index"; + } + + @GetMapping("/{id}") + public String viewProduct(@PathVariable("id") UUID productId, Model model, Principal principal) { + Optional optionalProduct = productService.findProductById(productId); + if (optionalProduct.isPresent()) { + model.addAttribute("product", optionalProduct.get()); + } else { + model.addAttribute("message", "Internal error accessing product!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "products/not-found"; + } + this.setModelDefaults(model, principal, "view"); + return "products/view"; + } + + @GetMapping("/{id}/download/{fileName:.+}") + public ResponseEntity downloadFile(@PathVariable(value = "id") UUID productId, + @PathVariable String fileName, HttpServletRequest request) { + Resource resource; + File dataDir; + try { + dataDir = ResourceUtils.getFile("classpath:data"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return ResponseEntity.notFound().build(); + } + + log.debug("Using data directory: " + dataDir.getAbsolutePath()); + String fileBasePath = dataDir.getAbsolutePath() + File.separatorChar + productId.toString() + File.separatorChar; + Path path = Paths.get(fileBasePath + fileName); + try { + resource = new UrlResource(path.toUri()); + } catch (MalformedURLException e) { + e.printStackTrace(); + return ResponseEntity.notFound().build(); + } + + // Try to determine file's content type + String contentType = null; + try { + contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath()); + } catch (IOException ex) { + log.debug("Could not determine file type."); + } + + // Fallback to the default content type if type could not be determined + if (contentType == null) { + contentType = "application/octet-stream"; + } + + return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType)) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") + .body(resource); + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/UserController.java b/src/main/java/com/microfocus/example/web/controllers/UserController.java new file mode 100644 index 0000000..9928214 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/UserController.java @@ -0,0 +1,698 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020-2022 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.CustomUserDetails; +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.*; +import com.microfocus.example.service.StorageService; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.*; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.data.repository.query.Param; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.validation.Valid; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URLConnection; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Controller for user pages + * + * @author Kevin A. Lee + */ +@RequestMapping("/user") +@Controller +@SessionAttributes("user") +public class UserController extends AbstractBaseController { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final String CONTROLLER_NAME = getClass().getName(); + + @Value("${AUTHENTICATION_ERROR;Invalid authentication credentials were supplied.") + private String AUTHENTICATION_ERROR; + + @Value("${USER_NOT_FOUND_ERROR:A user to be changed was not found.}") + private String USER_NOT_FOUND_ERROR; + + @Value("${USERNAME_TAKEN_ERROR:A username was used that is already taken.") + private String USERNAME_TAKEN_ERROR; + + @Value(("${EMAIL_ADDRESS_TAKEN_ERROR:An email address was used that is already taken.}")) + private String EMAIL_ADDRESS_TAKEN_ERROR; + + @Autowired + private UserService userService; + + @Autowired + private StorageService storageService; + + @Value("${app.messages.home}") + private final String message = "Hello World"; + + @Autowired + private SessionRegistry sessionRegistry; + + @Autowired + LocaleConfiguration localeConfiguration; + + private String thRCECMD = ""; + + @Override + LocaleConfiguration GetLocaleConfiguration() { + return localeConfiguration; + } + + @Override + String GetControllerName() { + return CONTROLLER_NAME; + } + + @GetMapping(value = {"", "/"}) + public String userHome(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + UserForm userForm = new UserForm(optionalUser.get()); + model.addAttribute("username", userForm.getUsername()); + model.addAttribute("fullname", userForm.getFirstName() + " " + userForm.getLastName()); + model.addAttribute("userInfo", WebUtils.toString(user.getUserDetails())); + model.addAttribute("unreadMessageCount", userService.getUserUnreadMessageCount(user.getId())); + model.addAttribute("unshippedOrderCount", userService.getUserUnshippedOrderCount(user.getId())); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "home"); + return "user/home"; + } + + @GetMapping(value = {"/profile"}) + public String userProfile(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + UserForm userForm = new UserForm(optionalUser.get()); + model.addAttribute("userForm", userForm); + model.addAttribute("userInfo", WebUtils.toString(user.getUserDetails())); + model.addAttribute("unreadMessageCount", userService.getUserUnreadMessageCount(user.getId())); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "profile"); + return "user/profile"; + } + + @GetMapping("/editProfile") + public String userEditProfile(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + UserForm userForm = new UserForm(optionalUser.get()); + model.addAttribute("userForm", userForm); + model.addAttribute("userInfo", WebUtils.toString(user.getUserDetails())); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "edit-profile"); + return "user/edit-profile"; + } + + @GetMapping("/changePassword") + public String userChangePassword(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + PasswordForm passwordForm = new PasswordForm(optionalUser.get()); + model.addAttribute("passwordForm", passwordForm); + model.addAttribute("userInfo", WebUtils.toString(user.getUserDetails())); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "change-password"); + return "user/change-password"; + } + + // + // Messages + // + + @GetMapping("/messages") + public String userMessages(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + List messages = userService.getUserMessages(user.getId()); + model.addAttribute("messages", messages); + model.addAttribute("unreadMessageCount", userService.getUserUnreadMessageCount(user.getId())); + model.addAttribute("totalMessageCount", messages.size()); + this.setModelDefaults(model, principal, "index"); + return "user/messages/index"; + } + + /* + @GetMapping("/unread-message-count") + @ResponseBody + public String getUserMessageCount(Model model, Principal principal) { + UUID loggedInUserId; + if (principal != null) { + CustomUserDetails loggedInUser = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + loggedInUserId = loggedInUser.getId(); + long userMessageCount = userService.getUserUnreadMessageCount(loggedInUserId); + return Long.toString(userMessageCount); + } else { + return "0"; + } + }*/ + + @GetMapping("/messages/{id}") + public String viewMessage(@PathVariable("id") UUID messageId, + Model model, Principal principal) { + UUID loggedInUserId; + if (principal != null) { + CustomUserDetails loggedInUser = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + loggedInUserId = loggedInUser.getId(); + } else { + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + Optional optionalMessage = userService.findMessageById(messageId); + if (optionalMessage.isPresent()) { + // does user have permission to read this message? + UUID messageUserId = optionalMessage.get().getUser().getId(); + if (!messageUserId.equals(loggedInUserId)) { + log.debug("User id: " + loggedInUserId + " trying to access message for: " + messageUserId); + this.setModelDefaults(model, principal, "access-denied"); + return "user/messages/access-denied"; + } + MessageForm messageForm = new MessageForm(optionalMessage.get()); + model.addAttribute("messageForm", messageForm); + // mark messages as read + userService.markMessageAsReadById(messageId); + } else { + model.addAttribute("message", "Internal error accessing message!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/messages/not-found"; + } + this.setModelDefaults(model, principal, "view"); + return "user/messages/view"; + } + + // + // Orders + // + + @GetMapping("/orders") + public String userOrders(Model model, Principal principal) { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + List orders = userService.getUserOrders(user.getId()); + model.addAttribute("orders", orders); + model.addAttribute("unshippedOrderCount", userService.getUserUnshippedOrderCount(user.getId())); + model.addAttribute("totalOrderCount", orders.size()); + this.setModelDefaults(model, principal, "index"); + return "user/orders/index"; + } + + @GetMapping("/unshipped-order-count") + @ResponseBody + public String getUnshippedOrderCount(Model model, Principal principal) { + if (principal != null) { + CustomUserDetails loggedInUser = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + long unshippedOrderCount = userService.getUserUnshippedOrderCount(loggedInUser.getId()); + return Long.toString(unshippedOrderCount); + } else { + return "0"; + } + } + + @GetMapping("/orders/{id}") + public String viewOrder(@PathVariable("id") UUID orderId, + Model model, Principal principal) { + UUID loggedInUserId; + if (principal != null) { + CustomUserDetails loggedInUser = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + loggedInUserId = loggedInUser.getId(); + } else { + this.setModelDefaults(model, principal, "not-found"); + return "user/not-found"; + } + Optional optionalOrder = userService.findOrderById(orderId); + if (optionalOrder.isPresent()) { + // does user have permission to view this order? + UUID orderUserId = optionalOrder.get().getUser().getId(); + if (!orderUserId.equals(loggedInUserId)) { + log.debug("User id: " + loggedInUserId + " trying to access order for: " + orderUserId); + this.setModelDefaults(model, principal, "access-denied"); + return "user/orders/access-denied"; + } + OrderForm orderForm = new OrderForm(optionalOrder.get()); + model.addAttribute("orderForm", orderForm); + } else { + model.addAttribute("message", "Internal error accessing order!"); + model.addAttribute("alertClass", "alert-danger"); + this.setModelDefaults(model, principal, "not-found"); + return "user/orders/not-found"; + } + this.setModelDefaults(model, principal, "view"); + return "user/orders/view"; + } + + // + // + // + + @PostMapping("/saveProfile") + public String userSaveProfile(@Valid @ModelAttribute("userForm") UserForm userForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (bindingResult.hasErrors()) { + this.setModelDefaults(model, principal, "edit-profile"); + return "user/edit-profile"; + } else { + try { + userService.saveUserFromUserForm(userForm); + redirectAttributes.addFlashAttribute("message", "Profile updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + this.setModelDefaults(model, principal, "profile"); + return "redirect:/user/profile"; + } catch (InvalidPasswordException ex) { + log.error(AUTHENTICATION_ERROR); + FieldError passwordError = new FieldError("userForm", "password", ex.getMessage()); + bindingResult.addError(passwordError); + } catch (UserNotFoundException ex) { + log.error(USER_NOT_FOUND_ERROR); + FieldError usernameError = new FieldError("userForm", "username", ex.getMessage()); + bindingResult.addError(usernameError); + } + } + this.setModelDefaults(model, principal, "profile"); + return "user/profile"; + } + + @PostMapping("/savePassword") + public String userSavePassword(@Valid @ModelAttribute("passwordForm") PasswordForm passwordForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (bindingResult.hasErrors()) { + this.setModelDefaults(model, principal, "change-password"); + return "user/change-password"; + } else { + try { + CustomUserDetails user = (CustomUserDetails) ((Authentication) principal).getPrincipal(); + Optional optionalUser = userService.findUserById(user.getId()); + if (optionalUser.isPresent()) { + userService.updateUserPasswordFromPasswordForm(user.getId(), passwordForm); + } + redirectAttributes.addFlashAttribute("message", "Password updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/logout"; + } catch (InvalidPasswordException ex) { + log.error(AUTHENTICATION_ERROR); + FieldError passwordError = new FieldError("passwordForm", "password", ex.getMessage()); + bindingResult.addError(passwordError); + } catch (UserNotFoundException ex) { + log.error(USER_NOT_FOUND_ERROR); + FieldError usernameError = new FieldError("passwordForm", "username", ex.getMessage()); + bindingResult.addError(usernameError); + } + } + this.setModelDefaults(model, principal, "home"); + return "user/home"; + } + + @PostMapping("/messages/delete/{id}") + public String userDeleteMessage(@PathVariable("id") UUID messageId, + Model model, Principal principal) { + userService.deleteMessageById(messageId); + model.addAttribute("message", "Successfully deleted message!"); + model.addAttribute("alertClass", "alert-success"); + return "redirect:/user/messages/"; + } + + @GetMapping("/register") + public String registerUser(Model model, Principal principal) { + RegisterUserForm registerUserForm = new RegisterUserForm(); + model.addAttribute("registerUserForm", registerUserForm); + this.setModelDefaults(model, principal, "register"); + return "user/register"; + } + + @PostMapping("/register") + public String registerUser(@Valid @ModelAttribute("registerUserForm") RegisterUserForm registerUserForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (bindingResult.hasErrors()) { + this.setModelDefaults(model, principal, "register"); + return "user/register"; + } else { + try { + User utmp = userService.registerUser(registerUserForm); + return "redirect:/login?registerSuccess"; + } catch (UsernameTakenException ex) { + log.error(USERNAME_TAKEN_ERROR); + FieldError usernameError = new FieldError("registerUserForm", "username", ex.getMessage()); + bindingResult.addError(usernameError); + } catch (EmailAddressTakenException ex) { + log.error(EMAIL_ADDRESS_TAKEN_ERROR); + FieldError emailError = new FieldError("registerUserForm", "email", ex.getMessage()); + bindingResult.addError(emailError); + } + } + this.setModelDefaults(model, principal, "register"); + return "user/register"; + } + + // + // File uploads + // + + @GetMapping("/uploadFile") + public String listUploadedFiles(@Valid @ModelAttribute("uploadForm") UploadForm uploadForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) throws IOException { + + model.addAttribute("files", storageService.loadAll().map( + path -> MvcUriComponentsBuilder.fromMethodName(UserController.class, + "serveFile", path.getFileName().toString()).build().toUri().toString()) + .collect(Collectors.toList())); + + return "user/upload-file"; + } + + @GetMapping("/files/{filename:.+}") + @ResponseBody + public ResponseEntity serveFile(@PathVariable String filename) { + + Resource file = storageService.loadAsResource(filename); + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + file.getFilename() + "\"").body(file); + } + + @PostMapping("/files/upload") + public String handleFileUpload(@RequestParam("file") MultipartFile file, + RedirectAttributes redirectAttributes) { + + storageService.store(file); + redirectAttributes.addFlashAttribute("message", + "You successfully uploaded " + file.getOriginalFilename() + "!"); + + return "redirect:/user/upload-file"; + } + + @GetMapping("/upload-xml-file") + public String listUploadedXMLFiles(@Valid @ModelAttribute("uploadForm") UploadForm uploadForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) throws IOException { + + JSONArray filesJsonAry = new JSONArray(); + + List mimeTypeList = new ArrayList<>(); + mimeTypeList.add("text/xml"); + mimeTypeList.add("application/xml"); + + Stream filePaths = storageService.loadAll(mimeTypeList); + filesJsonAry.putAll(filePaths.map(path -> { + JSONObject fileJsonObj = new JSONObject(); + String url = MvcUriComponentsBuilder.fromMethodName(UserController.class, + "serveXMLFile", path.getFileName().toString()).build().toUri().toString(); + fileJsonObj.put("name", path.getFileName()); + fileJsonObj.put("url", url); + fileJsonObj.put("content", getXMLFileContent(path.toString())); + return fileJsonObj; + }).collect(Collectors.toList())); + + model.addAttribute("files", filesJsonAry.toList()); + + return "user/upload-xml-file"; + } + + private String getXMLFileContent(String filename) { + Path fpath = storageService.load(filename); + + String xmlContent = ""; + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + try { + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(fpath.toFile()); + try (ByteArrayOutputStream bytesOutStream = new ByteArrayOutputStream()) { + writeXml(doc, bytesOutStream); + xmlContent = bytesOutStream.toString(); + } catch (IOException | TransformerException e) { + e.printStackTrace(); + } + } catch (ParserConfigurationException | IOException | SAXException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return xmlContent; + + } + + @GetMapping("/files/xml/{filename:.+}") + @ResponseBody + public ResponseEntity serveXMLFile(@PathVariable String filename) { + + Resource file = storageService.loadAsResource(filename); + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + file.getFilename() + "\"").body(file); + } + + @PostMapping("/files/upload-xml") + public String handleXMLFileUpload(@RequestParam("file") MultipartFile file, + RedirectAttributes redirectAttributes) { + + storageService.store(file); + redirectAttributes.addFlashAttribute("message", + "You successfully uploaded " + file.getOriginalFilename() + "!"); + + return "redirect:/user/upload-xml-file"; + } + + // write doc to output stream + private static void writeXml(Document doc, + OutputStream output) + throws TransformerException { + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(output); + + transformer.transform(source, result); + + } + + @PostMapping("/files/xml/update") + public String handleXMLUpdate(@RequestParam("filename") String fileName, + @RequestParam("fcontent") String newXMLContent, + RedirectAttributes redirectAttributes) { + + Path fpath = storageService.load(fileName); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + try { + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new InputSource(new StringReader(newXMLContent))); + Path temp = Files.createTempFile("iwa", ".xml"); + try (FileOutputStream outStream = new FileOutputStream(temp.toString())) { + writeXml(doc, outStream); + } catch (IOException | TransformerException e) { + e.printStackTrace(); + } + + storageService.store(temp, fpath.toString()); + Files.delete(temp); + } catch (ParserConfigurationException | IOException | SAXException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + redirectAttributes.addFlashAttribute("message", + "Successfully updated " + fileName + "!"); + + return "redirect:/user/upload-xml-file"; + } + + @ExceptionHandler(StorageFileNotFoundException.class) + public ResponseEntity handleStorageFileNotFound(StorageFileNotFoundException exc) { + return ResponseEntity.notFound().build(); + } + + @GetMapping("/ssrf") + public String ssrfExploit(Model model, @Param("url") String url) { + + if (Objects.isNull(url) || url.isEmpty()) + return "user/ssrf"; + + + URL urlLoc; + try { + urlLoc = new URL(url); + URLConnection connection = urlLoc.openConnection(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + String body = reader.lines().collect(Collectors.joining()); + model.addAttribute("urlcontent", body); + model.addAttribute("url", url); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return "user/ssrf"; + } + + @GetMapping("/command-shell") + public String getCommandShell(Model model) { + + String cmdWrapper = ""; + if (Objects.nonNull(this.thRCECMD) && this.thRCECMD.length() > 2) { + cmdWrapper = String.format("T (java.lang.Runtime).getRuntime().exec('%s')", this.thRCECMD); + } + model.addAttribute("shellcmd", cmdWrapper); + model.addAttribute("usercmd", this.thRCECMD); + return "user/command-shell"; + } + + @PostMapping("/command-shell") + public String executeCommandShell(@RequestParam("cmdshell") String cmd, + RedirectAttributes redirectAttributes) { + + this.thRCECMD = cmd; + redirectAttributes.addFlashAttribute("message", + "You successfully executed " + cmd + "!"); + return "redirect:/user/command-shell"; + } + + @GetMapping("/download-file") + public String unverifiedFileAccessIndex(Model model) { + model.addAttribute("file", ""); + return "user/download-file"; + } + + @GetMapping("/files/download/unverified") + public ResponseEntity serveUnverifiedFile(@Param("file") String file) { + + if (Objects.isNull(file) || file.isEmpty()) { + return ResponseEntity.badRequest().build(); + } + + Resource rfile = storageService.loadAsResource(file, true); + return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + rfile.getFilename() + "\"").body(rfile); + } + + @GetMapping("/log") + public String writeLog(Model model, @Param("val") String val) { + int intVal = -1; + String strLog = ""; + try { + intVal = Integer.parseInt(val); + strLog = "Input value is: "+intVal; + log.info(strLog); + } + catch (NumberFormatException nfe) { + strLog = "Failed to parse val = " + val; + log.info("Failed to parse val = " + val); + } + + model.addAttribute("val", val); + model.addAttribute("intval", intVal); + model.addAttribute("logwritten", strLog); + + return "user/log"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminDefaultController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminDefaultController.java new file mode 100644 index 0000000..fc9df92 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminDefaultController.java @@ -0,0 +1,120 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.BackupException; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.AdminUtils; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.admin.BackupForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import java.security.Principal; +import java.util.List; + +/** + * Controller for administrative pages + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin") +@Controller +public class AdminDefaultController { + + private static final Logger log = LoggerFactory.getLogger(AdminDefaultController.class); + + @Autowired + private UserService userService; + + @GetMapping(value = {"", "/"}) + public String index(Model model, Principal principal) { + this.setModelDefaults(model, principal, "Admin", "index"); + return "admin/dashboard"; + } + + @GetMapping("/backup") + public String databaseBackup(Model model, Principal principal) { + BackupForm backupForm = new BackupForm(); + int backupId = AdminUtils.getBackupId(); + backupForm.setId(backupId); + backupForm.setStatus(AdminUtils.getDbStatus(backupId)); + model.addAttribute("backupForm", backupForm); + this.setModelDefaults(model, principal, "Admin", "backup"); + return "admin/backup"; + } + + @GetMapping("/diagnostics") + public String siteDiagnostics(Model model, Principal principal) { + this.setModelDefaults(model, principal, "Admin", "diagnostics"); + return "admin/diagnostics"; + } + + @PostMapping("/runDbBackup") + public String runDbBackup(@Valid @ModelAttribute("backupForm") BackupForm backupForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (bindingResult.hasErrors()) { + return "admin/backup"; + } else { + log.debug("Backup profile: " + backupForm.getProfile()); + int backUpId = 0; + try { + backUpId = AdminUtils.startDbBackup(backupForm.getProfile()); + } catch (BackupException ignored) { + log.error(ignored.getMessage()); + } + log.debug("Backup id: " + backUpId); + redirectAttributes.addFlashAttribute("message", "Database backup started successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + this.setModelDefaults(model, principal, "Admin", "index"); + return "redirect:/admin"; + } + } + + @PostMapping("/dumpUsers") + public String dumpUsers(HttpServletRequest request, Model model, Principal principal, + @RequestParam(value = "usernames", required = false) String usernames, + @RequestParam(value = "status", required = false) boolean enabled) { + List users = userService.findEnabledUsersByUsername(enabled, usernames); + model.addAttribute("users", users); + this.setModelDefaults(model, principal, "Admin", "users"); + return "admin/users"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("messageCount", "0"); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminMessageController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminMessageController.java new file mode 100644 index 0000000..e1e6fb8 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminMessageController.java @@ -0,0 +1,90 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.MessageForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Controller for administration of messages + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin/messages") +@Controller +public class AdminMessageController { + + private static final Logger log = LoggerFactory.getLogger(AdminMessageController.class); + + @Autowired + private UserService userService; + + @GetMapping(value = {"", "/"}) + public String adminMessages(Model model, Principal principal) { + List messages = userService.getAllMessages(); + model.addAttribute("messages", messages); + this.setModelDefaults(model, principal, "Admin", "messages"); + return "admin/messages/index"; + } + + @GetMapping("/{id}") + public String viewMessage(@PathVariable("id") UUID messageId, + Model model, Principal principal) { + Optional optionalMessage = userService.findMessageById(messageId); + if (optionalMessage.isPresent()) { + MessageForm messageForm = new MessageForm(optionalMessage.get()); + model.addAttribute("messageForm", messageForm); + } else { + model.addAttribute("message", "Internal error accessing message!"); + model.addAttribute("alertClass", "alert-danger"); + return "message/not-found"; + } + return "/admin/messages/view"; + } + + @PostMapping("/delete/{id}") + public String userDeleteMessage(@PathVariable("id") UUID messageId, + Model model, Principal principal) { + userService.deleteMessageById(messageId); + model.addAttribute("message", "Successfully deleted message!"); + model.addAttribute("alertClass", "alert-success"); + return "redirect:/admin/messages/"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("messageCount", "0"); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminOrderController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminOrderController.java new file mode 100644 index 0000000..ab5f239 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminOrderController.java @@ -0,0 +1,161 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.Order; +import com.microfocus.example.exception.OrderNotFoundException; +import com.microfocus.example.service.ProductService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.admin.AdminOrderForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.*; + +/** + * Controller for administration of orders + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin/orders") +@Controller +public class AdminOrderController { + + private static final Logger log = LoggerFactory.getLogger(AdminOrderController.class); + + @Autowired + private ProductService productService; + + @Autowired + LocaleConfiguration localeConfiguration; + + @GetMapping(value = {"", "/"}) + public String listOrders(Model model, Principal principal) { + List orders = productService.getAllOrders(); + model.addAttribute("orders", orders); + this.setModelDefaults(model, principal, "Admin", "orders"); + return "admin/orders/index"; + } + + @GetMapping("/{id}") + public String viewOrder(@PathVariable("id") UUID orderId, + Model model, Principal principal) { + Optional optionalOrder = productService.findOrderById(orderId); + if (optionalOrder.isPresent()) { + AdminOrderForm adminOrderForm = new AdminOrderForm(optionalOrder.get()); + model.addAttribute("adminOrderForm", adminOrderForm); + } else { + model.addAttribute("message", "Internal error accessing order!"); + model.addAttribute("alertClass", "alert-danger"); + return "order/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "viewOrder"); + return "admin/orders/view"; + } + + @GetMapping("/{id}/edit") + public String editOrder(@PathVariable("id") UUID orderId, + Model model, Principal principal) { + Optional optionalOrder = productService.findOrderById(orderId); + if (optionalOrder.isPresent()) { + AdminOrderForm adminOrderForm = new AdminOrderForm(optionalOrder.get()); + model.addAttribute("adminOrderForm", adminOrderForm); + } else { + model.addAttribute("message", "Internal error accessing order!"); + model.addAttribute("alertClass", "alert-danger"); + return "order/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "editOrder"); + return "admin/orders/edit"; + } + + @PostMapping("/{id}/save") + public String saveOrder(@Valid @ModelAttribute("adminOrderForm") AdminOrderForm adminOrderForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + try { + productService.saveOrderFromAdminOrderForm(adminOrderForm); + redirectAttributes.addFlashAttribute("message", "Order updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/orders/" + adminOrderForm.getId(); + } catch (OrderNotFoundException ex) { + FieldError orderIdError = new FieldError("adminOrderForm", "id", ex.getMessage()); + bindingResult.addError(orderIdError); + } + } + this.setModelDefaults(model, principal, "Admin", "saveOrder"); + return "admin/orders/edit"; + } + + @GetMapping("/{id}/delete") + public String deleteOrder(@PathVariable("id") UUID orderId, + Model model, Principal principal) { + Optional optionalOrder = productService.findOrderById(orderId); + if (optionalOrder.isPresent()) { + AdminOrderForm adminOrderForm = new AdminOrderForm(optionalOrder.get()); + model.addAttribute("adminOrderForm", adminOrderForm); + } else { + model.addAttribute("message", "Internal error accessing order!"); + model.addAttribute("alertClass", "alert-danger"); + return "order/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "deleteOrder"); + return "admin/orders/delete"; + } + + @PostMapping("/{id}/delete") + public String deleteOrder(@PathVariable("id") UUID orderId, + @RequestParam(value = "action", required = true) String action, + Model model, RedirectAttributes redirectAttributes, + Principal principal) { + if (action.equals("delete")) { + productService.deleteOrderById(orderId); + redirectAttributes.addAttribute("message", "Order deleted successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + } else { + redirectAttributes.addAttribute("message", "Order deletion cancelled."); + redirectAttributes.addFlashAttribute("alertClass", "alert-info"); + } + return "redirect:/admin/orders/"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + Locale currentLocale = localeConfiguration.getLocale(); + Currency currency = Currency.getInstance(currentLocale); + model.addAttribute("currencySymbol", currency.getSymbol()); + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminProductController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminProductController.java new file mode 100644 index 0000000..5148203 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminProductController.java @@ -0,0 +1,186 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.Product; +import com.microfocus.example.exception.ProductNotFoundException; +import com.microfocus.example.service.ProductService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.admin.AdminNewProductForm; +import com.microfocus.example.web.form.admin.AdminProductForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.*; + +/** + * Controller for administration of products + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin/products") +@Controller +public class AdminProductController { + + private static final Logger log = LoggerFactory.getLogger(AdminProductController.class); + + @Autowired + private ProductService productService; + + @Autowired + LocaleConfiguration localeConfiguration; + + @GetMapping(value = {"", "/"}) + public String listProducts(Model model, Principal principal) { + List products = productService.getAllProducts(); + model.addAttribute("products", products); + this.setModelDefaults(model, principal, "Admin", "products"); + return "admin/products/index"; + } + + @GetMapping("/{id}") + public String viewProduct(@PathVariable("id") UUID productId, + Model model, Principal principal) { + Optional optionalProduct = productService.findProductById(productId); + if (optionalProduct.isPresent()) { + AdminProductForm adminProductForm = new AdminProductForm(optionalProduct.get()); + model.addAttribute("adminProductForm", adminProductForm); + } else { + model.addAttribute("message", "Internal error accessing product!"); + model.addAttribute("alertClass", "alert-danger"); + return "product/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "productView"); + return "admin/products/view"; + } + + @GetMapping("/{id}/edit") + public String productEdit(@PathVariable("id") UUID productId, + Model model, Principal principal) { + Optional optionalProduct = productService.findProductById(productId); + if (optionalProduct.isPresent()) { + AdminProductForm adminProductForm = new AdminProductForm(optionalProduct.get()); + model.addAttribute("adminProductForm", adminProductForm); + } else { + model.addAttribute("message", "Internal error accessing product!"); + model.addAttribute("alertClass", "alert-danger"); + return "product/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "productEdit"); + return "admin/products/edit"; + } + + @PostMapping("/{id}/save") + public String productSave(@Valid @ModelAttribute("adminProductForm") AdminProductForm adminProductForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + try { + productService.saveProductFromAdminProductForm(adminProductForm); + redirectAttributes.addFlashAttribute("message", "Product updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/products/" + adminProductForm.getId(); + } catch (ProductNotFoundException ex) { + FieldError productnameError = new FieldError("adminProductForm", "name", ex.getMessage()); + bindingResult.addError(productnameError); + } + } + this.setModelDefaults(model, principal, "Admin", "productEdit"); + return "admin/products/edit"; + } + + @GetMapping("/{id}/delete") + public String productDelete(@PathVariable("id") UUID productId, + Model model, Principal principal) { + Optional optionalProduct = productService.findProductById(productId); + if (optionalProduct.isPresent()) { + AdminProductForm adminProductForm = new AdminProductForm(optionalProduct.get()); + model.addAttribute("adminProductForm", adminProductForm); + } else { + model.addAttribute("message", "Internal error accessing product!"); + model.addAttribute("alertClass", "alert-danger"); + return "product/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "productDelete"); + return "admin/products/delete"; + } + + @PostMapping("/{id}/delete") + public String productDelete(@PathVariable("id") UUID productId, + @RequestParam(value = "action", required = true) String action, + Model model, RedirectAttributes redirectAttributes, + Principal principal) { + if (action.equals("delete")) { + productService.deleteProductById(productId); + redirectAttributes.addAttribute("message", "Product deleted successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + } else { + redirectAttributes.addAttribute("message", "Product deletion cancelled."); + redirectAttributes.addFlashAttribute("alertClass", "alert-info"); + } + return "redirect:/admin/products/"; + } + + @GetMapping("/add") + public String productAdd(Model model, Principal principal) { + AdminNewProductForm adminNewProductForm = new AdminNewProductForm(); + model.addAttribute("adminNewProductForm", adminNewProductForm); + this.setModelDefaults(model, principal, "Admin", "productAdd"); + return "admin/products/add"; + } + + @PostMapping("/addSave") + public String productAddSave(@Valid @ModelAttribute("adminNewProductForm") AdminNewProductForm adminNewProductForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + Product p = productService.newProductFormAdminNewProductForm(adminNewProductForm); + redirectAttributes.addFlashAttribute("message", "Product '" + adminNewProductForm.getCode() + "' added successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/products/" + p.getId(); + } + this.setModelDefaults(model, principal, "Admin", "productAddSave"); + return "admin/products/add"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + Locale currentLocale = localeConfiguration.getLocale(); + Currency currency = Currency.getInstance(currentLocale); + model.addAttribute("currencySymbol", currency.getSymbol()); + model.addAttribute("product", WebUtils.getLoggedInUser(principal)); + model.addAttribute("messageCount", "0"); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminReviewController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminReviewController.java new file mode 100644 index 0000000..7593b73 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminReviewController.java @@ -0,0 +1,161 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.config.LocaleConfiguration; +import com.microfocus.example.entity.Review; +import com.microfocus.example.exception.ReviewNotFoundException; +import com.microfocus.example.service.ProductService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.admin.AdminReviewForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.*; + +/** + * Controller for administration of product reviews + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin/reviews") +@Controller +public class AdminReviewController { + + private static final Logger log = LoggerFactory.getLogger(AdminReviewController.class); + + @Autowired + private ProductService productService; + + @Autowired + LocaleConfiguration localeConfiguration; + + @GetMapping(value = {"", "/"}) + public String listReviews(Model model, Principal principal) { + List reviews = productService.getReviews(); + model.addAttribute("reviews", reviews); + this.setModelDefaults(model, principal, "Admin", "reviews"); + return "admin/reviews/index"; + } + + @GetMapping("/{id}") + public String viewRevier(@PathVariable("id") UUID reviewId, + Model model, Principal principal) { + Optional optionalReview = productService.findReviewById(reviewId); + if (optionalReview.isPresent()) { + AdminReviewForm adminReviewForm = new AdminReviewForm(optionalReview.get()); + model.addAttribute("adminReviewForm", adminReviewForm); + } else { + model.addAttribute("message", "Internal error accessing review!"); + model.addAttribute("alertClass", "alert-danger"); + return "review/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "viewReview"); + return "admin/reviews/view"; + } + + @GetMapping("/{id}/edit") + public String editReview(@PathVariable("id") UUID reviewId, + Model model, Principal principal) { + Optional optionalReview = productService.findReviewById(reviewId); + if (optionalReview.isPresent()) { + AdminReviewForm adminReviewForm = new AdminReviewForm(optionalReview.get()); + model.addAttribute("adminReviewForm", adminReviewForm); + } else { + model.addAttribute("message", "Internal error accessing review!"); + model.addAttribute("alertClass", "alert-danger"); + return "review/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "editReview"); + return "admin/reviews/edit"; + } + + @PostMapping("/{id}/save") + public String saveReview(@Valid @ModelAttribute("adminReviewForm") AdminReviewForm adminReviewForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + try { + productService.saveReviewFromAdminReviewForm(adminReviewForm); + redirectAttributes.addFlashAttribute("message", "Review updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/reviews/" + adminReviewForm.getId(); + } catch (ReviewNotFoundException ex) { + FieldError reviewIdError = new FieldError("adminReviewForm", "id", ex.getMessage()); + bindingResult.addError(reviewIdError); + } + } + this.setModelDefaults(model, principal, "Admin", "saveReview"); + return "admin/reviews/edit"; + } + + @GetMapping("/{id}/delete") + public String deleteReview(@PathVariable("id") UUID reviewId, + Model model, Principal principal) { + Optional optionalReview = productService.findReviewById(reviewId); + if (optionalReview.isPresent()) { + AdminReviewForm adminReviewForm = new AdminReviewForm(optionalReview.get()); + model.addAttribute("adminReviewForm", adminReviewForm); + } else { + model.addAttribute("message", "Internal error accessing review!"); + model.addAttribute("alertClass", "alert-danger"); + return "review/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "deleteReview"); + return "admin/reviews/delete"; + } + + @PostMapping("/{id}/delete") + public String deleteReview(@PathVariable("id") UUID reviewId, + @RequestParam(value = "action", required = true) String action, + Model model, RedirectAttributes redirectAttributes, + Principal principal) { + if (action.equals("delete")) { + productService.deleteReviewById(reviewId); + redirectAttributes.addAttribute("message", "Review deleted successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + } else { + redirectAttributes.addAttribute("message", "Review deletion cancelled."); + redirectAttributes.addFlashAttribute("alertClass", "alert-info"); + } + return "redirect:/admin/reviews/"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + Locale currentLocale = localeConfiguration.getLocale(); + Currency currency = Currency.getInstance(currentLocale); + model.addAttribute("currencySymbol", currency.getSymbol()); + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } + +} diff --git a/src/main/java/com/microfocus/example/web/controllers/admin/AdminUserController.java b/src/main/java/com/microfocus/example/web/controllers/admin/AdminUserController.java new file mode 100644 index 0000000..a6b1f02 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/controllers/admin/AdminUserController.java @@ -0,0 +1,229 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.controllers.admin; + +import com.microfocus.example.entity.User; +import com.microfocus.example.exception.InvalidPasswordException; +import com.microfocus.example.exception.UserNotFoundException; +import com.microfocus.example.service.UserService; +import com.microfocus.example.utils.WebUtils; +import com.microfocus.example.web.form.admin.AdminNewUserForm; +import com.microfocus.example.web.form.admin.AdminPasswordForm; +import com.microfocus.example.web.form.admin.AdminUserForm; +import com.microfocus.example.web.form.UserForm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Controller for administration of users + * + * @author Kevin A. Lee + */ +@RequestMapping("/admin/users") +@Controller +public class AdminUserController { + + private static final Logger log = LoggerFactory.getLogger(AdminUserController.class); + + @Value("${AUTHENTICATION_ERROR;Invalid authentication credentials were supplied.") + private String AUTHENTICATION_ERROR; + + @Autowired + private UserService userService; + + @GetMapping(value = {"", "/"}) + public String listUsers(Model model, Principal principal) { + List users = userService.getAllUsers(); + model.addAttribute("users", users); + this.setModelDefaults(model, principal, "Admin", "users"); + return "admin/users/index"; + } + + @GetMapping("/{id}") + public String viewUser(@PathVariable("id") UUID userId, + Model model, Principal principal) { + Optional optionalUser = userService.findUserById(userId); + if (optionalUser.isPresent()) { + UserForm userForm = new UserForm(optionalUser.get()); + model.addAttribute("userForm", userForm); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "userView"); + return "admin/users/view"; + } + + @GetMapping("/{id}/edit") + public String userEditProfile(@PathVariable("id") UUID userId, + Model model, Principal principal) { + Optional optionalUser = userService.findUserById(userId); + if (optionalUser.isPresent()) { + AdminUserForm adminUserForm = new AdminUserForm(optionalUser.get()); + model.addAttribute("adminUserForm", adminUserForm); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "userEdit"); + return "admin/users/edit"; + } + + @PostMapping("/{id}/save") + public String userSaveProfile(@Valid @ModelAttribute("adminUserForm") AdminUserForm adminUserForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + try { + userService.saveUserFromAdminUserForm(adminUserForm); + redirectAttributes.addFlashAttribute("message", "User updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/users/" + adminUserForm.getId(); + } catch (UserNotFoundException ex) { + FieldError usernameError = new FieldError("adminUserForm", "username", ex.getMessage()); + bindingResult.addError(usernameError); + } + } + this.setModelDefaults(model, principal, "Admin", "userEdit"); + return "admin/users/edit"; + } + + @GetMapping("/{id}/changePassword") + public String userChangePassword(@PathVariable("id") UUID userId, + Model model, Principal principal) { + Optional optionalUser = userService.findUserById(userId); + if (optionalUser.isPresent()) { + AdminPasswordForm adminPasswordForm = new AdminPasswordForm(optionalUser.get()); + model.addAttribute("adminPasswordForm", adminPasswordForm); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "userChangePassword"); + return "admin/users/change-password"; + } + + @PostMapping("/{id}/savePassword") + public String userSavePassword(@PathVariable("id") UUID userId, + @Valid @ModelAttribute("adminPasswordForm") AdminPasswordForm adminPasswordForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + try { + userService.updateUserPasswordFromAdminPasswordForm(userId, adminPasswordForm); + redirectAttributes.addFlashAttribute("message", "User password updated successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/users/" + userId; + } catch (InvalidPasswordException ex) { + log.error(AUTHENTICATION_ERROR); + FieldError passwordError = new FieldError("adminPasswordForm", "password", ex.getMessage()); + bindingResult.addError(passwordError); + } catch (UserNotFoundException ex) { + FieldError usernameError = new FieldError("adminPasswordForm", "username", ex.getMessage()); + bindingResult.addError(usernameError); + } + } + this.setModelDefaults(model, principal, "Admin", "userEdit"); + return "/admin/users/change-password"; + } + + @GetMapping("/{id}/delete") + public String userDelete(@PathVariable("id") UUID userId, + Model model, Principal principal) { + Optional optionalUser = userService.findUserById(userId); + if (optionalUser.isPresent()) { + AdminUserForm adminUserForm = new AdminUserForm(optionalUser.get()); + model.addAttribute("adminUserForm", adminUserForm); + } else { + model.addAttribute("message", "Internal error accessing user!"); + model.addAttribute("alertClass", "alert-danger"); + return "user/not-found"; + } + this.setModelDefaults(model, principal, "Admin", "userDelete"); + return "admin/users/delete"; + } + + @PostMapping("/{id}/delete") + public String userDelete(@PathVariable("id") UUID userId, + @RequestParam(value = "action", required = true) String action, + Model model, RedirectAttributes redirectAttributes, + Principal principal) { + if (action.equals("delete")) { + userService.deleteUserById(userId); + redirectAttributes.addAttribute("message", "User deleted successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + } else { + redirectAttributes.addAttribute("message", "User deletion cancelled."); + redirectAttributes.addFlashAttribute("alertClass", "alert-info"); + } + return "redirect:/admin/users/"; + } + + @GetMapping("/add") + public String userAdd(Model model, Principal principal) { + AdminNewUserForm adminNewUserForm = new AdminNewUserForm(); + model.addAttribute("adminNewUserForm", adminNewUserForm); + this.setModelDefaults(model, principal, "Admin", "userAdd"); + return "admin/users/add"; + } + + @PostMapping("/addSave") + public String userAddSave(@Valid @ModelAttribute("adminNewUserForm") AdminNewUserForm adminNewUserForm, + BindingResult bindingResult, Model model, + RedirectAttributes redirectAttributes, + Principal principal) { + if (!bindingResult.hasErrors()) { + User u = userService.addUserFromAdminNewUserForm(adminNewUserForm); + redirectAttributes.addFlashAttribute("message", "User " + adminNewUserForm.getUsername() + " added successfully."); + redirectAttributes.addFlashAttribute("alertClass", "alert-success"); + return "redirect:/admin/users/" + u.getId(); + } + this.setModelDefaults(model, principal, "Admin", "userAddSave"); + return "admin/users/add"; + } + + private Model setModelDefaults(Model model, Principal principal, String controllerName, String actionName) { + model.addAttribute("user", WebUtils.getLoggedInUser(principal)); + model.addAttribute("messageCount", "0"); + model.addAttribute("controllerName", controllerName); + model.addAttribute("actionName", actionName); + return model; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/MessageForm.java b/src/main/java/com/microfocus/example/web/form/MessageForm.java new file mode 100644 index 0000000..f42cd34 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/MessageForm.java @@ -0,0 +1,124 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.User; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating message + * + * @author Kevin A. Lee + */ +public class MessageForm { + + private UUID id; + + private User user; + + @NotEmpty(message = "{message.text.notEmpty}") + @Size(min = 40, message = "{message.text.invalidLength}") + private String text; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date sentDate; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date readDate; + + private Boolean read; + + public MessageForm() { + } + + public MessageForm(Message message) { + this.id = message.getId(); + this.user = message.getUser(); + this.text = message.getText(); + this.sentDate = message.getSentDate(); + this.readDate = message.getReadDate(); + this.read = message.getRead(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Date getSentDate() { + return sentDate; + } + + public void setSentDate(Date sentDate) { + this.sentDate = sentDate; + } + + public Date getReadDate() { + return readDate; + } + + public void setReadDate(Date readDate) { + this.readDate = readDate; + } + + public Boolean getRead() { + return read; + } + + public void setRead(Boolean read) { + this.read = read; + } + + @Override + public String toString() { + return "MessageForm(" + id + " : " + text.substring(0,40) + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/OrderForm.java b/src/main/java/com/microfocus/example/web/form/OrderForm.java new file mode 100644 index 0000000..9312cec --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/OrderForm.java @@ -0,0 +1,151 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.microfocus.example.entity.Message; +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating order + * + * @author Kevin A. Lee + */ +public class OrderForm { + + private UUID id; + + private User user; + + private String orderNum; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date orderDate; + + private float amount; + + private String cart; + + private Boolean shipped; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date shippedDate; + + private Object notes; + + public OrderForm() { + } + + public OrderForm(Order order) { + this.id = order.getId(); + this.user = order.getUser(); + this.orderNum = order.getOrderNum(); + this.orderDate = order.getOrderDate(); + this.amount = order.getAmount(); + this.cart = order.getCart(); + this.shipped = order.getShipped(); + this.shippedDate = order.getShippedDate(); + this.setNotes(order.getNotes()); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getOrderNum() { + return orderNum; + } + + public void setOrderNum(String orderNum) { + this.orderNum = orderNum; + } + + public Date getOrderDate() { + return orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public float getAmount() { + return amount; + } + + public void setAmount(float amount) { + this.amount = amount; + } + + public String getCart() { + return this.cart; + } + + public void setCart(String cart) { + this.cart = cart; + } + + public Boolean getShipped() { + return shipped; + } + + public void setShipped(Boolean shipped) { + this.shipped = shipped; + } + + public Date getShippedDate() { + return shippedDate; + } + + public void setShippedDate(Date shippedDate) { + this.shippedDate = shippedDate; + } + + public Object getNotes() { + return notes; + } + + public void setNotes(Object notes) { + this.notes = notes; + } + + @Override + public String toString() { + return "OrderForm(" + id + " : " + orderNum + " for: " + user.getUsername() + " amount : " + amount + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/PasswordForm.java b/src/main/java/com/microfocus/example/web/form/PasswordForm.java new file mode 100644 index 0000000..776bc50 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/PasswordForm.java @@ -0,0 +1,87 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import javax.validation.constraints.NotEmpty; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import com.microfocus.example.entity.User; +import com.microfocus.example.web.validation.ValidPassword; + +/** + * Form backing entity/DTO for changing password + * @author Kevin A. Lee + */ +public class PasswordForm { + + @Bean("UserFormPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @NotEmpty + private String username; + + @ValidPassword + private String password; + + @ValidPassword + private String confirmPassword; + + public PasswordForm() { + } + + public PasswordForm(User user) { + this.username = user.getUsername(); + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + @Override + public String toString() { + return "PasswordForm{" + + ", username='" + username + '\'' + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/ProductForm.java b/src/main/java/com/microfocus/example/web/form/ProductForm.java new file mode 100644 index 0000000..c0995dc --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/ProductForm.java @@ -0,0 +1,203 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.microfocus.example.entity.Product; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating product + * + * @author Kevin A. Lee + */ +public class ProductForm { + + private UUID id; + + @NotEmpty(message = "{product.code.notEmpty}") + @Size(min = 6, max = 40, message = "{product.code.invalidLength}") + private String code; + + @NotEmpty(message = "{product.name.notEmpty}") + @Size(min = 6, max = 40, message = "{product.name.invalidLength}") + private String name; + + @NotEmpty(message = "{product.summary.notEmpty}") + @Size(min = 10, message = "{product.summary.invalidLength}") + private String summary; + + @NotEmpty(message = "{product.description.notEmpty}") + @Size(min = 40, message = "{product.description.invalidLength}") + private String description; + + private String image; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float price; + + private Boolean onSale; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float salePrice; + + private Boolean inStock; + + @Min(value = 0, message = "{product.timeToStock.invalidValue}") + @Max(value = 365, message = "{product.timeToStock.invalidValue}") + private int timeToStock; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + private Boolean available; + + public ProductForm() { + } + + public ProductForm(Product product) { + this.id = product.getId(); + this.code = product.getCode(); + this.name = product.getName(); + this.summary = product.getSummary(); + this.description = product.getDescription(); + this.image = product.getImage(); + this.price = product.getPrice(); + this.onSale = product.getOnSale(); + this.salePrice = product.getSalePrice(); + this.inStock = product.getInStock(); + this.timeToStock = product.getTimeToStock(); + this.rating = product.getRating(); + this.available = product.getAvailable(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Boolean getOnSale() { + return onSale; + } + + public void setOnSale(Boolean onSale) { + this.onSale = onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public void setSalePrice(float salePrice) { + this.salePrice = salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public void setInStock(Boolean inStock) { + this.inStock = inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public void setTimeToStock(int timeToStock) { + this.timeToStock = timeToStock; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + + @Override + public String toString() { + return "ProductForm(" + id + " : " + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/RegisterUserForm.java b/src/main/java/com/microfocus/example/web/form/RegisterUserForm.java new file mode 100644 index 0000000..1d9f064 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/RegisterUserForm.java @@ -0,0 +1,224 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.microfocus.example.entity.User; +import com.microfocus.example.web.validation.ValidPassword; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.UUID; + +/** + * Form backing entity/DTO for register a new user + * + * @author Kevin A. Lee + */ +public class RegisterUserForm { + + @Bean("RegisterUserFormPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 2, max = 10, message = "{user.username.invalidLength}") + private String username; + + @ValidPassword + private String password; + + @ValidPassword + private String confirmPassword; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^$|[0-9]{10})", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + private Boolean enabled; + + public RegisterUserForm() { + this.username = ""; + } + + public RegisterUserForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.password = user.getPassword(); + this.confirmPassword = user.getConfirmPassword(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip = user.getZip(); + this.country = user.getCountry(); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "RegisterUserForm{" + + "id=" + id + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/UploadForm.java b/src/main/java/com/microfocus/example/web/form/UploadForm.java new file mode 100644 index 0000000..24e1fc1 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/UploadForm.java @@ -0,0 +1,86 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.microfocus.example.entity.User; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import java.util.UUID; + +/** + * Form backing entity/DTO for uploading a file + * + * @author Kevin A. Lee + */ +public class UploadForm { + + + private UUID id; + private String username; + private String file; + + + public UploadForm() { + } + + public UploadForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + @Override + public String toString() { + return "UploadForm{" + + "userid=" + id + + ", username='" + username + '\'' + + ", file =" + file + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/UserForm.java b/src/main/java/com/microfocus/example/web/form/UserForm.java new file mode 100644 index 0000000..2226b00 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/UserForm.java @@ -0,0 +1,218 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form; + +import com.microfocus.example.entity.User; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating user profile + * + * @author Kevin A. Lee + */ +public class UserForm { + + @Bean("UserFormPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 2, max = 10, message = "{user.username.invalidLength}") + private String username; + + @NotEmpty(message = "{user.password.notEmpty}") + private String password; + + private String confirmPassword; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.lastName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.lastName.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + @Column(unique = true) + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^$|[0-9]{10})", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + private Boolean enabled; + + public UserForm() { + } + + public UserForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.password = user.getPassword(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip = user.getZip(); + this.country = user.getCountry(); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "UserForm{" + + "id=" + id + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminNewProductForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminNewProductForm.java new file mode 100644 index 0000000..6de38b5 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminNewProductForm.java @@ -0,0 +1,205 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.Product; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating product + * + * @author Kevin A. Lee + */ +public class AdminNewProductForm { + + private UUID id; + + @NotEmpty(message = "{product.code.notEmpty}") + @Size(min = 6, max = 40, message = "{product.code.invalidLength}") + private String code; + + @NotEmpty(message = "{product.name.notEmpty}") + @Size(min = 6, max = 40, message = "{product.name.invalidLength}") + private String name; + + @NotEmpty(message = "{product.summary.notEmpty}") + @Size(min = 10, message = "{product.summary.invalidLength}") + private String summary; + + @NotEmpty(message = "{product.description.notEmpty}") + @Size(min = 40, message = "{product.description.invalidLength}") + private String description; + + private String image; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float price; + + private Boolean onSale; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float salePrice; + + private Boolean inStock; + + @Min(value = 0, message = "{product.timeToStock.invalidValue}") + @Max(value = 365, message = "{product.timeToStock.invalidValue}") + private int timeToStock; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + private Boolean available; + + public AdminNewProductForm() { + } + + public AdminNewProductForm(Product product) { + this.id = product.getId(); + this.code = product.getCode(); + this.name = product.getName(); + this.summary = product.getSummary(); + this.description = product.getDescription(); + this.image = product.getImage(); + this.price = product.getPrice(); + this.onSale = product.getOnSale(); + this.salePrice = product.getSalePrice(); + this.inStock = product.getInStock(); + this.timeToStock = product.getTimeToStock(); + this.rating = product.getRating(); + this.available = product.getAvailable(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Boolean getOnSale() { + return onSale; + } + + public void setOnSale(Boolean onSale) { + this.onSale = onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public void setSalePrice(float salePrice) { + this.salePrice = salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public void setInStock(Boolean inStock) { + this.inStock = inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public void setTimeToStock(int timeToStock) { + this.timeToStock = timeToStock; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + + @Override + public String toString() { + return "ProductForm(" + id + " : " + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminNewUserForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminNewUserForm.java new file mode 100644 index 0000000..c2a5935 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminNewUserForm.java @@ -0,0 +1,220 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.User; +import com.microfocus.example.web.validation.ValidPassword; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.UUID; + +/** + * Form backing entity/DTO for adding a new user + * + * @author Kevin A. Lee + */ +public class AdminNewUserForm { + + @Bean("AdminNewUserFormPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 2, max = 10, message = "{user.username.invalidLength}") + private String username; + + @ValidPassword + private String password; + + @ValidPassword + private String confirmPassword; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^$|[0-9]{10})", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + private Boolean enabled; + + public AdminNewUserForm() { + } + + public AdminNewUserForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.password = user.getPassword(); + this.confirmPassword = user.getConfirmPassword(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip = user.getZip(); + this.country = user.getCountry(); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "AdminNewUserForm{" + + "id=" + id + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminOrderForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminOrderForm.java new file mode 100644 index 0000000..a8d2583 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminOrderForm.java @@ -0,0 +1,148 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.Order; +import com.microfocus.example.entity.User; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating order + * + * @author Kevin A. Lee + */ +public class AdminOrderForm { + + private UUID id; + + private User user; + + private String orderNum; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date orderDate; + + private float amount; + + private String cart; + + private Boolean shipped; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date shippedDate; + + private Object notes; + + public AdminOrderForm() { + } + + public AdminOrderForm(Order order) { + this.id = order.getId(); + this.user = order.getUser(); + this.orderNum = order.getOrderNum(); + this.orderDate = order.getOrderDate(); + this.amount = order.getAmount(); + this.cart = order.getCart(); + this.shipped = order.getShipped(); + this.shippedDate = order.getShippedDate(); + this.notes = order.getNotes(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public String getOrderNum() { + return orderNum; + } + + public void setOrderNum(String orderNum) { + this.orderNum = orderNum; + } + + public Date getOrderDate() { + return orderDate; + } + + public void setOrderDate(Date orderDate) { + this.orderDate = orderDate; + } + + public float getAmount() { + return amount; + } + + public void setAmount(float amount) { + this.amount = amount; + } + + public String getCart() { + return this.cart; + } + + public void setCart(String cart) { + this.cart = cart; + } + + public Boolean getShipped() { + return shipped; + } + + public void setShipped(Boolean shipped) { + this.shipped = shipped; + } + + public Date getShippedDate() { + return shippedDate; + } + + public void setShippedDate(Date shippedDate) { + this.shippedDate = shippedDate; + } + + public Object getNotes() { + return notes; + } + + public void setNotes(Object notes) { + this.notes = notes; + } + + @Override + public String toString() { + return "AdminOrderForm(" + id + " : " + orderNum + " for: " + user.getUsername() + " amount : " + amount + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminPasswordForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminPasswordForm.java new file mode 100644 index 0000000..07e1f48 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminPasswordForm.java @@ -0,0 +1,101 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.User; +import com.microfocus.example.web.validation.ValidPassword; +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import java.util.UUID; + +/** + * Form backing entity/DTO for changing password + * @author Kevin A. Lee + */ +public class AdminPasswordForm { + + @Bean("AdminPasswordFormPasswordEncoder") + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private UUID id; + + @NotEmpty + private String username; + + @ValidPassword + private String password; + + @ValidPassword + private String confirmPassword; + + public AdminPasswordForm() { + } + + public AdminPasswordForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.password = user.getPassword(); + this.confirmPassword = user.getConfirmPassword(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + @Override + public String toString() { + return "AdminPasswordForm{" + + ", username='" + username + '\'' + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminProductForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminProductForm.java new file mode 100644 index 0000000..4959c25 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminProductForm.java @@ -0,0 +1,205 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.Product; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating product + * + * @author Kevin A. Lee + */ +public class AdminProductForm { + + private UUID id; + + @NotEmpty(message = "{product.code.notEmpty}") + @Size(min = 6, max = 40, message = "{product.code.invalidLength}") + private String code; + + @NotEmpty(message = "{product.name.notEmpty}") + @Size(min = 6, max = 40, message = "{product.name.invalidLength}") + private String name; + + @NotEmpty(message = "{product.summary.notEmpty}") + @Size(min = 10, message = "{product.summary.invalidLength}") + private String summary; + + @NotEmpty(message = "{product.description.notEmpty}") + @Size(min = 40, message = "{product.description.invalidLength}") + private String description; + + private String image; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float price; + + private Boolean onSale; + + @Min(value = 0, message = "{product.price.invalidValue}") + private float salePrice; + + private Boolean inStock; + + @Min(value = 0, message = "{product.timeToStock.invalidValue}") + @Max(value = 365, message = "{product.timeToStock.invalidValue}") + private int timeToStock; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + private Boolean available; + + public AdminProductForm() { + } + + public AdminProductForm(Product product) { + this.id = product.getId(); + this.code = product.getCode(); + this.name = product.getName(); + this.summary = product.getSummary(); + this.description = product.getDescription(); + this.image = product.getImage(); + this.price = product.getPrice(); + this.onSale = product.getOnSale(); + this.salePrice = product.getSalePrice(); + this.inStock = product.getInStock(); + this.timeToStock = product.getTimeToStock(); + this.rating = product.getRating(); + this.available = product.getAvailable(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Boolean getOnSale() { + return onSale; + } + + public void setOnSale(Boolean onSale) { + this.onSale = onSale; + } + + public float getSalePrice() { + return salePrice; + } + + public void setSalePrice(float salePrice) { + this.salePrice = salePrice; + } + + public Boolean getInStock() { + return inStock; + } + + public void setInStock(Boolean inStock) { + this.inStock = inStock; + } + + public int getTimeToStock() { + return timeToStock; + } + + public void setTimeToStock(int timeToStock) { + this.timeToStock = timeToStock; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getAvailable() { + return available; + } + + public void setAvailable(Boolean available) { + this.available = available; + } + + @Override + public String toString() { + return "ProductForm(" + id + " : " + name + " : SRP : " + price + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminReviewForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminReviewForm.java new file mode 100644 index 0000000..2750c90 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminReviewForm.java @@ -0,0 +1,130 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2021 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.Product; +import com.microfocus.example.entity.Review; +import com.microfocus.example.entity.User; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import java.util.Date; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating review + * + * @author Kevin A. Lee + */ +public class AdminReviewForm { + + private UUID id; + + private Product product; + + private User user; + + @DateTimeFormat(pattern = "MM-dd-yyyy") + private Date reviewDate; + + private String comment; + + @Min(value = 1, message = "{product.rating.invalidValue}") + @Max(value = 5, message = "{product.rating.invalidValue}") + private int rating; + + private Boolean visible; + + public AdminReviewForm() { + } + + public AdminReviewForm(Review review) { + this.id = review.getId(); + this.product = review.getProduct(); + this.user = review.getUser(); + this.reviewDate = review.getReviewDate(); + this.comment = review.getComment(); + this.rating = review.getRating(); + this.visible = review.getVisible(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public Product getProduct() { + return this.product; + } + + public void setProduct(Product product) { + this.product = product; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Date getReviewDate() { + return reviewDate; + } + + public void setReviewDate(Date reviewDate) { + this.reviewDate = reviewDate; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public Boolean getVisible() { + return visible; + } + + public void setVisible(Boolean visible) { + this.visible = visible; + } + + @Override + public String toString() { + return "AdminReviewForm(" + id + " of: " + product.getName() + " by: " + user.getUsername() + " on : " + reviewDate + ")"; + } + +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/AdminUserForm.java b/src/main/java/com/microfocus/example/web/form/admin/AdminUserForm.java new file mode 100644 index 0000000..5dd59af --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/AdminUserForm.java @@ -0,0 +1,188 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +import com.microfocus.example.entity.User; + +import javax.persistence.Column; +import javax.validation.constraints.*; +import java.util.UUID; + +/** + * Form backing entity/DTO for updating user profile + * + * @author Kevin A. Lee + */ +public class AdminUserForm { + + private UUID id; + + @NotEmpty(message = "{user.username.notEmpty}") + @Size(min = 2, max = 10, message = "{user.username.invalidLength}") + private String username; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String firstName; + + @NotEmpty(message = "{user.firstName.notEmpty}") + @Size(min = 2, max = 40, message = "{user.firstName.invalidLength}") + private String lastName; + + @NotEmpty(message = "{user.email.notEmpty}") + @Email(message = "{user.email.invalidFormat") + private String email; + + @NotEmpty(message = "{user.phone.notEmpty}") + @Pattern(regexp = "(^$|[0-9]{10})", message = "{user.phone.invalidFormat}") + @Column(unique = true) + private String phone; + + private String address; + private String city; + private String state; + private String zip; + private String country; + + private Boolean enabled; + + public AdminUserForm() { + } + + public AdminUserForm(User user) { + this.id = user.getId(); + this.username = user.getUsername(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.phone = user.getPhone(); + this.address = user.getAddress(); + this.city = user.getCity(); + this.state = user.getState(); + this.zip = user.getZip(); + this.country = user.getCountry(); + this.enabled = user.getEnabled(); + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "AdminUserForm{" + + "id=" + id + + ", username='" + username + '\'' + + ", email =" + email + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/form/admin/BackupForm.java b/src/main/java/com/microfocus/example/web/form/admin/BackupForm.java new file mode 100644 index 0000000..1a24b1b --- /dev/null +++ b/src/main/java/com/microfocus/example/web/form/admin/BackupForm.java @@ -0,0 +1,77 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.form.admin; + +/** + * Form backing entity/DTO for database backup + * @author Kevin A. Lee + */ +public class BackupForm { + + private Integer id; + + private String status; + + private String profile; + + private String refreshInterval; + + public BackupForm() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getProfile() { + return profile; + } + + public void setProfile(String profile) { + this.profile = profile; + } + + public String getRefreshInterval() { + return refreshInterval; + } + + public void setRefreshInterval(String refreshInterval) { + this.refreshInterval = refreshInterval; + } + + @Override + public String toString() { + return "BackupForm{" + + ", id='" + id + '\'' + + '}'; + } +} diff --git a/src/main/java/com/microfocus/example/web/validation/PasswordConstraintValidator.java b/src/main/java/com/microfocus/example/web/validation/PasswordConstraintValidator.java new file mode 100644 index 0000000..59bc022 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/validation/PasswordConstraintValidator.java @@ -0,0 +1,136 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.validation; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.apache.commons.io.FileUtils; +import org.passay.CharacterRule; +import org.passay.DictionaryRule; +import org.passay.EnglishCharacterData; +import org.passay.LengthRule; +import org.passay.PasswordData; +import org.passay.PasswordValidator; +import org.passay.RuleResult; +import org.passay.WhitespaceRule; +import org.passay.dictionary.WordListDictionary; +import org.passay.dictionary.WordLists; +import org.passay.dictionary.sort.ArraysSort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +/** + * Custom Password Validator (using org.passay) + * @author Kevin A. Lee + */ +public class PasswordConstraintValidator implements ConstraintValidator { + + private static final Logger log = LoggerFactory.getLogger(PasswordConstraintValidator.class); + + private DictionaryRule dictionaryRule; + + @Value("${app.invalidPasswordList}") + private String invalidPasswordList = "/invalid-password-list.txt"; + + @Override + public void initialize(ValidPassword constraintAnnotation) { + FileReader[] fr = new FileReader[0]; + FileReader frPassList = null; + try { + //String filename = System.getProperty("com.microfocus.example.passwordList"); + //File dictionaryFile = new File(filename); + //String invalidPasswordList = FileUtils.readFileToString(dictionaryFile); + File invalidPasswordFile = new File(this.getClass().getResource(invalidPasswordList).getFile()); + frPassList = new FileReader(invalidPasswordFile); + fr = new FileReader[] { frPassList }; + dictionaryRule = new DictionaryRule( + new WordListDictionary(WordLists.createFromReader( + // Reader around the word list file + fr, + // True for case sensitivity, false otherwise + false, + // Dictionaries must be sorted + new ArraysSort() + ))); + } catch (IOException e) { + throw new RuntimeException("could not load word list", e); + } finally { + if (frPassList != null) { + try { + frPassList.close(); + } catch (IOException ignored) { + log.error(ignored.getMessage()); + } + } + } + } + + @Override + public boolean isValid(String password, ConstraintValidatorContext context) { + if (password.isEmpty()) { + return false; + } + PasswordValidator validator = new PasswordValidator(Arrays.asList( + + // at least 8 characters + new LengthRule(8, 30), + + // at least one upper-case character + new CharacterRule(EnglishCharacterData.UpperCase, 1), + + // at least one lower-case character + new CharacterRule(EnglishCharacterData.LowerCase, 1), + + // at least one digit character + new CharacterRule(EnglishCharacterData.Digit, 1), + + // at least one symbol (special character) + new CharacterRule(EnglishCharacterData.Special, 1), + + // no whitespace + new WhitespaceRule(), + + // no common passwords + dictionaryRule + )); + + RuleResult result = validator.validate(new PasswordData(password)); + + if (result.isValid()) { + return true; + } + + List messages = validator.getMessages(result); + String messageTemplate = messages.stream().collect(Collectors.joining(",")); + context.buildConstraintViolationWithTemplate(messageTemplate) + .addConstraintViolation() + .disableDefaultConstraintViolation(); + return false; + } +} diff --git a/src/main/java/com/microfocus/example/web/validation/ValidPassword.java b/src/main/java/com/microfocus/example/web/validation/ValidPassword.java new file mode 100644 index 0000000..aca03d4 --- /dev/null +++ b/src/main/java/com/microfocus/example/web/validation/ValidPassword.java @@ -0,0 +1,46 @@ +/* + Insecure Web App (IWA) + + Copyright (C) 2020 Micro Focus or one of its affiliates + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package com.microfocus.example.web.validation; + +import javax.validation.Payload; +import javax.validation.Constraint; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Interface for custom Password Validator + * @author Kevin A. Lee + */ +@Documented +@Constraint(validatedBy = PasswordConstraintValidator.class) +@Target({ TYPE, FIELD, ANNOTATION_TYPE }) +@Retention(RUNTIME) +public @interface ValidPassword { + + String message() default "Invalid Password"; + Class[] groups() default {}; + Class[] payload() default {}; + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..0830a28 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,78 @@ +server: + port: 8888 + error: + include-message: always + include-binding-errors: always + include-stacktrace: on_trace_param + include-exception: true + whitelabel: + enabled: false + +spring: + profiles: + active: dev + main: + allow-bean-definition-overriding: true + banner-mode: "off" + thymeleaf: + cache: false + check-template: true + check-template-location: true + enabled: true + encoding: UTF-8 + # excluded-view-names: + mode: HTML + prefix: classpath:/templates/ + suffix: .html + # template-resolver-order: + # view-names: + servlet: + content-type: text/html + datasource: + # h2 database + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:iwa_dev + username: sa + password: password + initialization-mode: always + jpa: + show-sql: true + hibernate: + ddl-auto: none + properties: + hiberate: + format_sql: true + # h2 database dialect + dialect: org.hibernate.dialect.H2Dialect + mvc: +# favicon: +# enabled: false + throw-exception-if-no-handler-found: true + jackson: + serialization: + WRITE_DATES_AS_TIMESTAMPS: false + +logging: + level: + root: WARN + com.microfocus: DEBUG + org.hibernate.SQL: DEBUG + org.hibernate.type.descriptor.sql.BasicBinder: TRACE + org.springframework.web: INFO + org.springframework.security: INFO + +app: + name: IWA Pharmacy Direct + version: 1.0 + currency: GBP + invalidPasswordList: "/invalid-password-list.txt" + data: + page-size: 25 + messages: + home: Welcome to our site! + jwt: + secret: eLgvVuw3qIHLNGNH/XNASMiV1RQlVgNakeDsLu7svKQP7jtu2+35wwu/rfwBAQi88wCiuCU66NS66BEySj6jQ4tLOJy4Jn5USbjBk98vMYnT+3me2lYqTUg/tdEg2HHMzhnu+MOXdq5LK1iwr0/uTob/meDy2cIT3ljF+8L7ZuxnwYYW0BTQsM+0nDTYNg4Ik/ChORRBjDyNpplzGvJlqvY5g2RkCyIP0WRe1A== + expiration-ms: 86400000 + refresh-ms: 86400000 + + diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..992bc29 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,74 @@ +server: + port: 8080 + error: + include-message: always + include-binding-errors: always + include-stacktrace: on_trace_param + include-exception: true + whitelabel: + enabled: false + +spring: + profiles: + active: test + main: + allow-bean-definition-overriding: true + banner-mode: "off" + thymeleaf: + cache: false + check-template: true + check-template-location: true + enabled: true + encoding: UTF-8 + # excluded-view-names: + mode: HTML + prefix: classpath:/templates/ + suffix: .html + # template-resolver-order: + # view-names: + servlet: + content-type: text/html + datasource: + # h2 database + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:iwa_test + username: sa + password: password + initialization-mode: always + jpa: + show-sql: true + hibernate: + ddl-auto: none + properties: + hiberate: + format_sql: true + # h2 database dialect + dialect: org.hibernate.dialect.H2Dialect + mvc: + favicon: + enabled: false + throw-exception-if-no-handler-found: true + jackson: + serialization: + WRITE_DATES_AS_TIMESTAMPS: false + +logging: + level: + root: WARN + com.microfocus: DEBUG + org.springframework.web: INFO + org.springframework.security: INFO + +app: + name: IWA Pharmacy Direct + version: 1.0 + currency: GBP + invalidPasswordList: "/invalid-password-list.txt" + data: + page-size: 25 + messages: + home: Welcome to our site! + jwt: + secret: eLgvVuw3qIHLNGNH/XNASMiV1RQlVgNakeDsLu7svKQP7jtu2+35wwu/rfwBAQi88wCiuCU66NS66BEySj6jQ4tLOJy4Jn5USbjBk98vMYnT+3me2lYqTUg/tdEg2HHMzhnu+MOXdq5LK1iwr0/uTob/meDy2cIT3ljF+8L7ZuxnwYYW0BTQsM+0nDTYNg4Ik/ChORRBjDyNpplzGvJlqvY5g2RkCyIP0WRe1A== + expiration-ms: 86400000 + refresh-ms: 86400000 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..4988148 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,97 @@ +server: + port: 8080 + error: + include-message: always + include-binding-errors: always + include-stacktrace: on_trace_param + include-exception: false + whitelabel: + enabled: false + servlet: + context-path: / + +spring: + profiles: + active: prod + main: + allow-bean-definition-overriding: true + banner-mode: "off" + thymeleaf: + cache: false + check-template: true + check-template-location: true + enabled: true + encoding: UTF-8 + # excluded-view-names: + mode: HTML + prefix: classpath:/templates/ + suffix: .html + # template-resolver-order: + # view-names: + servlet: + content-type: text/html + datasource: + # h2 database + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:iwa + username: sa + password: password + initialization-mode: always + jpa: + show-sql: false + hibernate: + ddl-auto: none + properties: + hiberate: + # h2 database dialect + dialect: org.hibernate.dialect.H2Dialect + mvc: +# favicon: +# enabled: false + throw-exception-if-no-handler-found: true + jackson: + serialization: + WRITE_DATES_AS_TIMESTAMPS: false + +# resources: +# add-mappings: false + + +springdoc: + api-docs: + path: /v3/api-docs + swagger-ui: +# url: /v3/api-docs + path: /swagger-ui.html + display-request-duration: true + + # groups-order: DESC + disable-swagger-default-url: true + paths-to-match: /api/** +# group-configs: +# - group: application +# paths-to-match: /api/** +# - group: actuator +# paths-to-match: /actuator/** + + +logging: + level: + root: WARN + com.microfocus: INFO + org.springframework.web: INFO + org.springframework.security: INFO + +app: + name: IWA Pharmacy Direct + version: 1.0 + currency: GBP + invalidPasswordList: "/invalid-password-list.txt" + data: + page-size: 25 + messages: + home: Welcome to our site! + jwt: + secret: eLgvVuw3qIHLNGNH/XNASMiV1RQlVgNakeDsLu7svKQP7jtu2+35wwu/rfwBAQi88wCiuCU66NS66BEySj6jQ4tLOJy4Jn5USbjBk98vMYnT+3me2lYqTUg/tdEg2HHMzhnu+MOXdq5LK1iwr0/uTob/meDy2cIT3ljF+8L7ZuxnwYYW0BTQsM+0nDTYNg4Ik/ChORRBjDyNpplzGvJlqvY5g2RkCyIP0WRe1A== + expiration-ms: 86400000 + refresh-ms: 86400000 diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..22ae366 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,130 @@ +INSERT INTO authorities (name, id) +VALUES ('ROLE_ADMIN', '05970e74-c82b-4e21-b100-f8184d6e3454'); +INSERT INTO authorities (name, id) +VALUES ('ROLE_USER', '6bdd6188-d659-4390-8d37-8f090d2ed69a'); +INSERT INTO authorities (name, id) +values ('ROLE_API', 'dfc1d81b-4a7e-4248-80f7-8445ee5cb68e'); +INSERT INTO users (id, username, password, first_name, last_name, email, phone, address, city, state, zip, country, date_created, gender, enabled) +VALUES ('e18c8bcc-935d-444d-a194-3a32a3b35a49', 'admin', '$2a$10$YFhTnHpCL.Z0Ev0j1CbEUub7sIWmN7Qd5RmnU8g5ekuoapV7Zdx32', + 'Admin', 'User', 'admin@localhost.com', '0123456789', '', '', '', '', 'United Kingdom', CURDATE(), 'Male', 1); +INSERT INTO users (id, username, password, first_name, last_name, email, phone, address, city, state, zip, country, date_created, gender, enabled) +VALUES ('32e7db01-86bc-4687-9ecb-d79b265ac14f', 'user1', '$2a$10$YFhTnHpCL.Z0Ev0j1CbEUub7sIWmN7Qd5RmnU8g5ekuoapV7Zdx32', + 'Sam', 'Shopper', 'user1@localhost.com', '0123456789', '1 Somewhere Street', 'London', 'Greater London', 'SW1', 'United Kingdom', CURDATE(), 'Female', 1); +INSERT INTO users (id, username, password, first_name, last_name, email, phone, address, city, state, zip, country, date_created, gender, enabled) +VALUES ('db4cfab1-ff1d-4bca-a662-394771841383', 'user2', '$2a$10$YFhTnHpCL.Z0Ev0j1CbEUub7sIWmN7Qd5RmnU8g5ekuoapV7Zdx32', + 'Sarah', 'Shopper', 'user2@localhost.com', '0123456789', '1 Somewhere Street', 'London', 'Greater London', 'SW1', 'United Kingdom', CURDATE(), 'Male', 1); +INSERT INTO users (id, username, password, first_name, last_name, email, phone, address, city, state, zip, country, date_created, gender, enabled) +VALUES ('92a82f45-7a03-42f3-80f8-ce4e9892409d', 'api', '$2a$10$YFhTnHpCL.Z0Ev0j1CbEUub7sIWmN7Qd5RmnU8g5ekuoapV7Zdx32', + 'Api', 'User', 'api@localhost.com', '0123456789', '1 Somewhere Street', 'London', 'Greater London', 'SW1', 'United Kingdom', CURDATE(), 'Female', 1); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('05970e74-c82b-4e21-b100-f8184d6e3454', 'e18c8bcc-935d-444d-a194-3a32a3b35a49'); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('6bdd6188-d659-4390-8d37-8f090d2ed69a', 'e18c8bcc-935d-444d-a194-3a32a3b35a49'); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('dfc1d81b-4a7e-4248-80f7-8445ee5cb68e', 'e18c8bcc-935d-444d-a194-3a32a3b35a49'); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('6bdd6188-d659-4390-8d37-8f090d2ed69a', '32e7db01-86bc-4687-9ecb-d79b265ac14f'); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('6bdd6188-d659-4390-8d37-8f090d2ed69a', 'db4cfab1-ff1d-4bca-a662-394771841383'); +INSERT INTO user_authorities (authority_id, user_id) +VALUES ('dfc1d81b-4a7e-4248-80f7-8445ee5cb68e', '92a82f45-7a03-42f3-80f8-ce4e9892409d'); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('eec467c8-5de9-4c7c-8541-7b31614d31a0', 'SWA234-A568-00010', 'Solodox 750', 4, + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pharetra enim erat, sed tempor mauris viverra in. Donec ante diam, rhoncus dapibus efficitur ut, sagittis a elit. Integer non ante felis. Curabitur nec lectus ut velit bibendum euismod. Nulla mattis convallis neque ac euismod. Ut vel mattis lorem, nec tempus nibh. Vivamus tincidunt enim a risus placerat viverra. Curabitur diam sapien, posuere dignissim accumsan sed, tempus sit amet diam. Aliquam tincidunt vitae quam non rutrum. Nunc id sollicitudin neque, at posuere metus. Sed interdum ex erat, et ornare purus bibendum id. Suspendisse sagittis est dui. Donec vestibulum elit at arcu feugiat porttitor.', + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pharetra enim erat, sed tempor mauris viverra in. Donec ante diam, rhoncus dapibus efficitur ut, sagittis a elit. Integer non ante felis. Curabitur nec lectus ut velit bibendum euismod. Nulla mattis convallis neque ac euismod. Ut vel mattis lorem, nec tempus nibh. Vivamus tincidunt enim a risus placerat viverra. Curabitur diam sapien, posuere dignissim accumsan sed, tempus sit amet diam. Aliquam tincidunt vitae quam non rutrum. Nunc id sollicitudin neque, at posuere metus. Sed interdum ex erat, et ornare purus bibendum id. Suspendisse sagittis est dui. Donec vestibulum elit at arcu feugiat porttitor.', + 'generic-product-4.jpg', + 12.95, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, on_sale, sale_price, in_stock, time_to_stock, available) +VALUES ('74b87e87-0d77-422c-baaa-622498a84328', 'SWA534-F528-00115', 'Alphadex Plus', 5, + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet quam eget neque vestibulum tincidunt vitae vitae augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer rhoncus varius sem non luctus. Etiam tincidunt et leo non tempus. Etiam imperdiet elit arcu, a fermentum arcu commodo vel. Fusce vel consequat erat. Curabitur non lacus velit. Donec dignissim velit et sollicitudin pulvinar.', + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet quam eget neque vestibulum tincidunt vitae vitae augue. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer rhoncus varius sem non luctus. Etiam tincidunt et leo non tempus. Etiam imperdiet elit arcu, a fermentum arcu commodo vel. Fusce vel consequat erat. Curabitur non lacus velit. Donec dignissim velit et sollicitudin pulvinar.', + 'generic-product-1.jpg', + 14.95, 1, 9.95, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('6bbbeb10-6709-4163-a790-f691b09d6aca', 'SWA179-G243-00101', 'Dontax', 3, + 'Aenean sit amet pulvinar mauris. Suspendisse eu ligula malesuada, condimentum tortor rutrum, rutrum dui. Sed vehicula augue sit amet placerat bibendum. Maecenas ac odio libero. Donec mi neque, convallis ut nulla quis, malesuada convallis velit. Aenean a augue blandit, viverra massa nec, laoreet quam. In lacinia eros quis lacus dictum pharetra.', + 'Aenean sit amet pulvinar mauris. Suspendisse eu ligula malesuada, condimentum tortor rutrum, rutrum dui. Sed vehicula augue sit amet placerat bibendum. Maecenas ac odio libero. Donec mi neque, convallis ut nulla quis, malesuada convallis velit. Aenean a augue blandit, viverra massa nec, laoreet quam. In lacinia eros quis lacus dictum pharetra.', + 'generic-product-2.jpg', + 8.50, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, on_sale, sale_price, in_stock, time_to_stock, available) +VALUES ('b6a2c319-1d14-424b-9a60-ec3ba97d21e7', 'SWA201-D342-00132', 'Tranix Life', 5, + 'Curabitur imperdiet lacus nec lacus feugiat varius. Integer hendrerit erat orci, eget varius urna varius ac. Nulla fringilla, felis eget cursus imperdiet, odio eros tincidunt est, non blandit enim ante nec magna. Suspendisse in justo maximus nisi molestie bibendum. Fusce consequat accumsan nulla, vel pharetra nulla consequat sit amet.', + 'Curabitur imperdiet lacus nec lacus feugiat varius. Integer hendrerit erat orci, eget varius urna varius ac. Nulla fringilla, felis eget cursus imperdiet, odio eros tincidunt est, non blandit enim ante nec magna. Suspendisse in justo maximus nisi molestie bibendum. Fusce consequat accumsan nulla, vel pharetra nulla consequat sit amet.', + 'generic-product-3.jpg', + 7.95, 1, 4.95, 1, 14, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('96018e5d-f34b-4e92-955c-d077809344ab', 'SWA312-F432-00134', 'Salex Two', 5, + 'In porta viverra condimentum. Morbi nibh magna, suscipit sit amet urna sed, euismod consectetur eros. Donec egestas, elit ut commodo fringilla, sem quam suscipit lectus, id tempus enim sem quis risus. Curabitur eleifend bibendum magna, vel iaculis elit varius et. Sed mollis dolor quis metus lacinia posuere. Phasellus odio mi, tempus quis dui et, consectetur iaculis odio. Quisque fringilla viverra eleifend. Cras dignissim euismod tortor, eget congue turpis fringilla sit amet. Aenean sed semper dolor, sed ultrices felis.', + 'In porta viverra condimentum. Morbi nibh magna, suscipit sit amet urna sed, euismod consectetur eros. Donec egestas, elit ut commodo fringilla, sem quam suscipit lectus, id tempus enim sem quis risus. Curabitur eleifend bibendum magna, vel iaculis elit varius et. Sed mollis dolor quis metus lacinia posuere. Phasellus odio mi, tempus quis dui et, consectetur iaculis odio. Quisque fringilla viverra eleifend. Cras dignissim euismod tortor, eget congue turpis fringilla sit amet. Aenean sed semper dolor, sed ultrices felis.', + 'generic-product-5.jpg', + 11.95, 0, 14, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, on_sale, sale_price, in_stock, time_to_stock, available) +VALUES ('b85c1e4b-3ab8-4d15-b884-24db5e246058', 'SWA654-F106-00412', 'Betala Lite', 5, + 'Sed bibendum metus vitae suscipit mattis. Mauris turpis purus, sodales a egestas vel, tincidunt ac ipsum. Donec in sapien et quam varius dignissim. Phasellus eros sem, facilisis quis vehicula sed, ornare eget odio. Nam tincidunt urna mauris, id tincidunt risus posuere ac. Integer vel est vel enim convallis blandit sed sed urna. Nam dapibus erat nunc, id euismod diam pulvinar id. Fusce a felis justo.', + 'Sed bibendum metus vitae suscipit mattis. Mauris turpis purus, sodales a egestas vel, tincidunt ac ipsum. Donec in sapien et quam varius dignissim. Phasellus eros sem, facilisis quis vehicula sed, ornare eget odio. Nam tincidunt urna mauris, id tincidunt risus posuere ac. Integer vel est vel enim convallis blandit sed sed urna. Nam dapibus erat nunc, id euismod diam pulvinar id. Fusce a felis justo.', + 'generic-product-4.jpg', + 11.95, 1, 9.95, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('6709d692-4b37-459b-ba40-3bcc3186ca09', 'SWA254-A971-00213', 'Stimlab Mitre', 5, + 'Phasellus malesuada pulvinar justo, ac eleifend magna lacinia eget. Proin vulputate nec odio at volutpat. Duis non suscipit arcu. Nam et arcu vehicula, sollicitudin eros non, scelerisque diam. Phasellus sagittis pretium tristique. Vestibulum sit amet lectus nisl. Aliquam aliquet dolor sit amet neque placerat, vel varius metus molestie. Fusce sed ipsum blandit, efficitur est vitae, scelerisque enim. Integer porttitor est et dictum blandit. Quisque gravida tempus orci nec finibus.', + 'Phasellus malesuada pulvinar justo, ac eleifend magna lacinia eget. Proin vulputate nec odio at volutpat. Duis non suscipit arcu. Nam et arcu vehicula, sollicitudin eros non, scelerisque diam. Phasellus sagittis pretium tristique. Vestibulum sit amet lectus nisl. Aliquam aliquet dolor sit amet neque placerat, vel varius metus molestie. Fusce sed ipsum blandit, efficitur est vitae, scelerisque enim. Integer porttitor est et dictum blandit. Quisque gravida tempus orci nec finibus.', + 'generic-product-6.jpg', + 12.95, 0, 7, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('ba802760-b33e-4352-acfa-0a10859b519a', 'SWA754-B418-00315', 'Alphadex Lite', 2, + 'Nam bibendum porta metus. Aliquam viverra pulvinar velit et condimentum. Pellentesque quis purus libero. Fusce hendrerit tortor sed nulla lobortis commodo. Donec ultrices mi et sollicitudin aliquam. Phasellus rhoncus commodo odio quis faucibus. Nullam interdum mi non egestas pellentesque. Duis nec porta leo, eu placerat tellus.', + 'Nam bibendum porta metus. Aliquam viverra pulvinar velit et condimentum. Pellentesque quis purus libero. Fusce hendrerit tortor sed nulla lobortis commodo. Donec ultrices mi et sollicitudin aliquam. Phasellus rhoncus commodo odio quis faucibus. Nullam interdum mi non egestas pellentesque. Duis nec porta leo, eu placerat tellus.', + 'generic-product-7.jpg', 9.95, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('339311c3-8325-464a-8ca6-4b78716f00d0', 'SWA432-E901-00126', 'Villacore 2000', 1, + 'Aliquam erat volutpat. Ut gravida scelerisque purus a sagittis. Nullam pellentesque arcu sed risus dignissim scelerisque. Maecenas vel elit pretium, ultrices augue ac, interdum libero. Suspendisse potenti. In felis metus, mattis quis lorem congue, condimentum volutpat felis. Nullam mauris mi, bibendum in ultrices sed, blandit congue ipsum.', + 'Aliquam erat volutpat. Ut gravida scelerisque purus a sagittis. Nullam pellentesque arcu sed risus dignissim scelerisque. Maecenas vel elit pretium, ultrices augue ac, interdum libero. Suspendisse potenti. In felis metus, mattis quis lorem congue, condimentum volutpat felis. Nullam mauris mi, bibendum in ultrices sed, blandit congue ipsum.', + 'generic-product-8.jpg', + 19.95, 1, 30, 1); +INSERT INTO products (id, code, name, rating, summary, description, image, price, in_stock, time_to_stock, available) +VALUES ('0bf8ccfc-89e8-4662-b940-ca7d267dcb99', 'SWA723-A375-00412', 'Kanlab Blue', 5, + 'Proin eget nisl non sapien gravida pellentesque. Cras tincidunt tortor posuere, laoreet sapien nec, tincidunt nunc. Integer vehicula, erat ut pretium porta, velit leo dignissim odio, eu ultricies urna nulla a dui. Proin et dapibus turpis, et tincidunt augue. In mattis luctus elit, in vehicula erat pretium sed. Suspendisse ullamcorper mollis dolor eu tristique.', + 'Proin eget nisl non sapien gravida pellentesque. Cras tincidunt tortor posuere, laoreet sapien nec, tincidunt nunc. Integer vehicula, erat ut pretium porta, velit leo dignissim odio, eu ultricies urna nulla a dui. Proin et dapibus turpis, et tincidunt augue. In mattis luctus elit, in vehicula erat pretium sed. Suspendisse ullamcorper mollis dolor eu tristique.', + 'generic-product-9.jpg', + 9.95, 0, 7, 1); +INSERT INTO messages (id, user_id, text, read) +values ('0fddab02-5ea0-4fb0-ae7d-c0b83679b9d4', 'e18c8bcc-935d-444d-a194-3a32a3b35a49', 'This is an example message', + 0); +INSERT INTO messages (id, user_id, text, read) +values ('2b87dbbe-4337-4f2a-b378-00277a49b82d', 'e18c8bcc-935d-444d-a194-3a32a3b35a49', 'Test message - please ignore!', + 0); +INSERT INTO messages (id, user_id, text, read) +values ('6914e47d-2f0a-4deb-a712-12e7801e13e8', '32e7db01-86bc-4687-9ecb-d79b265ac14f', + 'Welcome to JWA. This is an example message that you can read', 0); +INSERT INTO messages (id, user_id, text, read) +values ('755c10aa-fe8c-490b-82fa-8f418e39f596', '32e7db01-86bc-4687-9ecb-d79b265ac14f', 'Test message - please ignore!', + 0); +INSERT INTO messages (id, user_id, text, read) +values ('ec52da53-7809-4333-bfe5-84233f93be82', '92a82f45-7a03-42f3-80f8-ce4e9892409d', + 'Welcome to JWA. This is an example message that you can read', 0); +INSERT INTO messages (id, user_id, text, read) +values ('6aa03b01-4288-4e6e-bff6-dfe0f462cf68', '92a82f45-7a03-42f3-80f8-ce4e9892409d', 'Test message - please ignore!', + 0); + +INSERT INTO orders (id, user_id, order_date, order_num, amount, shipped, cart, credit_card) +VALUES ('c9b31f33-17a4-4fcd-927e-c14cdee32201', '32e7db01-86bc-4687-9ecb-d79b265ac14f', CURDATE()-5, 'OID-P400-0001', 100.0, 0, + '[{"id":"6bbbeb10-6709-4163-a790-f691b09d6aca","name":"Dontax","price":8.50,"quantity":4}]', '4375 2134 2183 3846'); +INSERT INTO orders (id, user_id, order_date, order_num, amount, shipped, cart, credit_card) +VALUES ('c94cbf6d-9baa-4a02-8eea-b13ceb43474d', '32e7db01-86bc-4687-9ecb-d79b265ac14f', CURDATE()-10, 'OID-P400-0001', 25.0, 0, + '[{"id":"6bbbeb10-6709-4163-a790-f691b09d6aca","name":"Dontax","price":8.50,"quantity":1}]', '4375 2134 2183 3847'); +INSERT INTO orders (id, user_id, order_date, order_num, amount, shipped, shipped_date, cart, credit_card) +VALUES ('81550b4f-c660-41ec-a6f3-076b611add9b', '32e7db01-86bc-4687-9ecb-d79b265ac14f', CURDATE()-20, 'OID-P401-0009', 50.0, 1, CURDATE(), + '[{"id":"6bbbeb10-6709-4163-a790-f691b09d6aca","name":"Dontax","price":8.50,"quantity":2}]', '4375 2134 2183 3848'); +INSERT INTO orders (id, user_id, order_date, order_num, amount, shipped, cart, credit_card) +VALUES ('db4cfab1-ff1d-4bca-a662-394771841383', 'db4cfab1-ff1d-4bca-a662-394771841383', CURDATE()-10, 'OID-G320-0051', 25.0, 0, + '[{"id":"6bbbeb10-6709-4163-a790-f691b09d6aca","name":"Dontax","price":8.50,"quantity":1}]', '4375 2134 2183 3849'); + +INSERT INTO reviews (id, product_id, user_id, review_date, comment, rating, visible) +VALUES ('822f734a-3d13-4ebc-bff6-9c36d29866a6', 'eec467c8-5de9-4c7c-8541-7b31614d31a0', '32e7db01-86bc-4687-9ecb-d79b265ac14f', + CURDATE()-10, 'This is an example review of Solodox 750. It is very good.', 5, 1); +INSERT INTO reviews (id, product_id, user_id, review_date, comment, rating, visible) +VALUES ('5f3936db-0a41-4026-8a66-1b9b0c21e203', '74b87e87-0d77-422c-baaa-622498a84328', '32e7db01-86bc-4687-9ecb-d79b265ac14f', + CURDATE()-5, 'Arrived on time and works well but the instructions are very limited and not explained well.', 4, 1); +INSERT INTO reviews (id, product_id, user_id, review_date, comment, rating, visible) +VALUES ('920292c5-0c9c-46a5-aacb-d8011ae6608a', 'eec467c8-5de9-4c7c-8541-7b31614d31a0', 'db4cfab1-ff1d-4bca-a662-394771841383', + CURDATE()-2, 'This is another review of Solodox 750. It does not work as described and not worth the money.', 3, 1); diff --git a/src/main/resources/invalid-password-list.txt b/src/main/resources/invalid-password-list.txt new file mode 100644 index 0000000..7128378 --- /dev/null +++ b/src/main/resources/invalid-password-list.txt @@ -0,0 +1,5 @@ +qwerty123! +azerty12! +12345678! +password123 +password123! diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000..b279fe3 --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,52 @@ +# Spring Security +AbstractUserDetailsAuthenticationProvider.badCredentials=Incorrect username or password. + +# User +user.username.notEmpty=Please provide a username. +user.username.invalidLength=Username should be between 4 and 10 characters. +user.username.invalidFormat=Username should be between 4 and 10 characters and only contain characters and digits. +user.firstName.notEmpty=Please provide a first name. +user.firstName.invalidLength=First name should be between 2 and 40 characters. +user.lastName.notEmpty=Please provide a last name. +user.lastName.invalidLength=Last name should be between 2 and 40 characters. +user.password.notEmpty=Please provide a password. +user.email.notEmpty=Please provide an email address. +user.email.invalidFormat=Please provide a valid email address. +user.phone.notEmpty=Please provide a phone number. +user.phone.invalidFormat=Phone number should contain only digits (0-9) and be between 7 and 10 digits long. + +# Role + +# Products +product.code.notEmpty=Please provide a product code. +product.code.invalidLength=Product code should be between 6 and 40 characters. +product.name.notEmpty=Please provide a product name. +product.name.invalidLength=Product name should be between 6 and 40 characters. +product.summary.notEmpty=Please provide a product summary. +product.summary.invalidLength=Product summary should be at least 10 characters. +product.description.notEmpty=Please provide a product description. +product.description.invalidLength=Product description should be at least 40 characters. +product.price.invalidValue=Please provide a valid price. +product.timeToStock.invalidValue=Product time to stock should be between 0 and 365 days. +product.rating.invalidValue=Product rating should between 1 and 5. + +# Messages +message.text.notEmpty=Please provide some message text. +message.text.invalidLength=Message text should be at least 40 characters. + +# Passwords +TOO_LONG=Password must not have more than %2$s characters. +TOO_SHORT=Password must not contain less than %2$s characters. +INSUFFICIENT_UPPERCASE=Password must contain an uppercase characters. +INSUFFICIENT_LOWERCASE=Password must contain a lowercase characters. +INSUFFICIENT_DIGIT=Password must contain a numeric character. +INSUFFICIENT_SPECIAL=Password must contain a special character. +ILLEGAL_WORD=Password contains a illegal word or sequence of characters + +# Errors +AUTHENTICATION_ERROR=Invalid authentication credentials were supplied. +USER_NOT_FOUND_ERROR=A user to be changed was not found. +USERNAME_TAKEN_ERROR=A username was used that is already taken. +EMAIL_ADDRESS_TAKEN_ERROR=An email address was used that is already taken. + +currency.symbol=GBP diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..b1ebfd6 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,135 @@ +--alter table users drop constraint UKuser_username if exists; +--alter table user_authorities drop constraint FKuser_authority_authority_id if exists; +--alter table user_authorities drop constraint FKuser_authority_user_id if exists; +--alter table messages drop constraint FKmessage_user_id if exists; +--alter table orders drop constraint FKorders_user_id if exists; +--alter table reviews drop constraint FKproducts_product_id if exists; +--alter table reviews drop constraint FKproducts_user_id if exists; +--alter table refresh_tokens constraint FK_refresh_tokens_user_id if exists; + +drop table authorities if exists cascade; +drop table users if exists cascade; +drop table user_authorities if exists cascade; +drop table verifications if exists cascade; +drop table products if exists cascade; +drop table messages if exists cascade; +drop table orders if exists cascade; +drop table reviews if exists cascade; +drop table refresh_tokens if exists cascade; +drop sequence if exists hibernate_sequence; +create sequence hibernate_sequence start with 1 increment by 1; + +create table authorities +( + id UUID not null, + name varchar(255), + primary key (id) +); +create table users +( + id UUID not null, + username varchar(255) not null, + password varchar(255), + date_created timestamp, + first_name varchar(255) not null, + last_name varchar(255) not null, + email varchar(255) not null, + phone varchar(255) not null, + address varchar(255) default null, + city varchar(255) default null, + state varchar(255) default null, + zip varchar(255) default null, + country varchar(255) default null, + gender varchar(255) not null, + enabled bit(1) not null, + primary key (id) +); +create table user_authorities +( + user_id UUID not null, + authority_id UUID not null, + primary key (user_id, authority_id) +); +create table verifications +( + phone varchar(255) not null, + request_id varchar(255) not null, + expiry_date datetime default null, + primary key (phone) +); +create table products +( + id UUID not null, + code varchar(255) not null, + name varchar(255) not null, + summary clob not null, + description clob not null, + image varchar(255), + price float not null, + on_sale bit(1) default 0 not null, + sale_price float default 0.0 not null, + in_stock bit(1) default 1 not null, + time_to_stock integer default 0 not null, + rating integer default 1 not null, + available bit(1) default 1 not null, + primary key (id) +); +create table messages +( + id UUID not null, + user_id UUID not null, + text clob default null, + sent_date datetime default NOW(), + read_date datetime default null, + read bit(1) not null, + primary key (id) +); +create table orders +( + id UUID not null, + user_id UUID not null, + order_num varchar(255) not null, + order_date datetime default NOW(), + amount float not null, + cart clob default null, + shipped bit(1) not null, + shipped_date datetime default null, + order_notes clob default null, + credit_card clob default null, + primary key (id) +); +create table reviews +( + id UUID not null, + product_id UUID not null, + user_id UUID not null, + review_date datetime default NOW(), + comment clob default null, + rating integer default 1 not null, + visible bit(1) not null, + primary key (id) +); +create table refresh_tokens +( + id UUID not null, + user_id UUID not null, + expiry_date datetime null, + primary key (id) +); + +alter table users + add constraint UKuser_username unique (username); +alter table user_authorities + add constraint FKuser_authority_authority_id foreign key (authority_id) references authorities on delete cascade; +alter table user_authorities + add constraint FKuser_authority_user_id foreign key (user_id) references users on delete cascade; +alter table messages + add constraint FKmessage_user_id foreign key (user_id) references users (id) on delete cascade; +alter table orders + add constraint FKorders_user_id foreign key (user_id) references users (id) on delete cascade; +alter table reviews + add constraint FKproducts_product_id foreign key (product_id) references products (id) on delete cascade; +alter table reviews + add constraint FKproducts_user_id foreign key (user_id) references users (id) on delete cascade; +alter table refresh_tokens + add constraint FK_refresh_tokens_user_id foreign key (user_id) references users (id) on delete cascade; diff --git a/src/main/resources/site-message.txt b/src/main/resources/site-message.txt new file mode 100644 index 0000000..8830930 --- /dev/null +++ b/src/main/resources/site-message.txt @@ -0,0 +1 @@ +This site is healthy with no issues. diff --git a/src/main/resources/static/README.md b/src/main/resources/static/README.md new file mode 100644 index 0000000..d1f6aff --- /dev/null +++ b/src/main/resources/static/README.md @@ -0,0 +1,16 @@ +# IWA (Insecure Web App) + +## Overview + +_IWA (Insecure Web App)_ is an example Java/Spring Web Application for use in **DevSecOps** scenarios and demonstrations. +The source code includes some examples of insecure code - which can be found using static and dynamic application +security testing tools such as [Fortify SCA](https://www.microfocus.com/en-us/products/static-code-analysis-sas), +[Fortify On Demand](https://www.microfocus.com/en-us/products/application-security-testing) +and [Fortify WebInspect](https://www.microfocus.com/en-us/products/webinspect-dynamic-analysis-dast). + +One of the main aims of this project is to illustrate how security can be embedded early and continuously in +the development lifecycle - so a number of "integrations" to common build and pipeline tools is provided. + +Please note: the application should not be used in a live or production environment! + +![Screenshot](media/screenshot.png) \ No newline at end of file diff --git a/src/main/resources/static/css/app.css b/src/main/resources/static/css/app.css new file mode 100644 index 0000000..1a81b2c --- /dev/null +++ b/src/main/resources/static/css/app.css @@ -0,0 +1,1839 @@ +/* Base */ + +header { + +} +body { + min-height: 100vh; + line-height: 1.7; + color: #8c92a0; + font-weight: 300; + font-size: 16px; +} + +footer { + +} + +::-moz-selection { + background: #000; + color: #fff; +} + +::selection { + background: #000; + color: #fff; +} + +a { + -webkit-transition: .3s all ease; + -o-transition: .3s all ease; + transition: .3s all ease; +} + +a:hover { + text-decoration: none; +} + +.text-black { + color: #000; +} + +.site-wrap:before { + -webkit-transition: .3s all ease-in-out; + -o-transition: .3s all ease-in-out; + transition: .3s all ease-in-out; + background: rgba(0, 0, 0, 0.6); + content: ""; + position: absolute; + z-index: 2000; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + visibility: hidden; +} + +.offcanvas-menu .site-wrap { + position: absolute; + height: 100%; + width: 100%; + z-index: 2; + overflow: hidden; +} + +.offcanvas-menu .site-wrap:before { + opacity: 1; + visibility: visible; +} + +.btn { + -webkit-transition: .3s all ease-in-out; + -o-transition: .3s all ease-in-out; + transition: .3s all ease-in-out; + text-transform: uppercase; + border-width: 2px; +} + +.btn:hover, .btn:active, .btn:focus { + outline: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn.btn-primary:hover { + background: transparent; + color: #51eaea; +} + +.btn-white { + color: #fff; +} + +.btn.btn-black { + background: #000; + color: #000; +} + +.btn.btn-black:hover, .btn.btn-black:active, .btn.btn-black:focus { + background-color: #51eaea; + color: #000; +} + +.btn-white:hover, .btn-white:active, .btn-white:focus { + color: #fff !important; +} + +.form-control { + height: 43px; +} + +.form-control:active, .form-control:focus { + border-color: #51eaea; +} + +.form-control:hover, .form-control:active, .form-control:focus { + -webkit-box-shadow: none !important; + box-shadow: none !important; +} + +.site-section { + padding: 1.0em 0; +} + +.site-section-sm { + padding: 1.0em 0; +} + +@media (min-width: 768px) { + .site-section { + padding: 2.5em 0; + } +} + +.site-section.site-section-sm { + padding: 2em 0; +} + +.site-section-heading { + font-size: 30px; + color: #25262a; + position: relative; +} + +.site-section-heading:before { + display: none; + content: ""; + left: 0%; + top: 0; + position: absolute; + width: 40px; + height: 2px; + background: #51eaea; +} + +.site-section-heading.text-center:before { + display: none; + content: ""; + left: 50%; + top: 0; + -webkit-transform: translateX(-50%); + -ms-transform: translateX(-50%); + transform: translateX(-50%); + position: absolute; + width: 40px; + height: 2px; + background: #51eaea; +} + +.border-top { + border-top: 1px solid #edf0f5 !important; +} + +#site-header .col,#site-header .col-auto{padding-right:5px;padding-left:5px} +#site-header-top{height:46px} +#site-header-logo-left{float:left} +#site-header-logo-right{display:none} +#site-header-cart{position:relative} +#site-header-cart-qty{position:absolute;top:0;right:12px;font-size:11px;line-height:14px;background:#212529;border-radius:50%;color:#fff;height:14px;width:14px;text-align:center} + +#site-header-cart a{font-size:1.5rem;text-decoration:none} +#site-header-cart a .badge{color:#fff} +#site-header-bottom a{display:block;line-height:36px} +#site-header-bottom a:hover{text-decoration:none} +#site-header_nav{clear:both} +#site-header .collapsing{position:initial;height:initial;overflow:initial;transition:initial} +@media screen and (prefers-reduced-motion:reduce) { +#site-header .collapsing{transition:initial} +} +@media (min-width: 576px) { +#site-header .row{margin-right:-15px;margin-left:-15px} +#site-header .col,#site-header .col-auto{padding-right:15px;padding-left:15px} +#site-header-logo-right{float:left;display:block;margin-left:4px} +} + +@media (min-width: 992px) { +#site-header-menu-toggler{display:none!important} +#site-header-nav a{display:inline-block;margin-right:20px} +#site-header-nav{clear:none;display:block!important;float:left} +} + +.site-footer { + font-size: 12px; + background-color: #3c3d41; + padding: 20px 40px; + color: rgba(255,255,255,1.00); + left: 0; + right: 0; +} +.site-footer .footer-brand, .site-footer .footer-nav, .site-footer .footer-social, .site-footer .footer-ns { padding:10px 25px; } +.site-footer .footer-nav, .site-footer .footer-social, .site-footer .footer-ns { border-color: transparent; } +.site-footer .footer-brand h2 { margin:0px 0px 10px; } +.site-footer .footer-brand p { font-size:12px; color:rgba(255,255,255,0.70); } + +.site-footer .footer-nav ul.pages { list-style:none; padding:0px; } +.site-footer .footer-nav ul.pages li { padding:5px 0px;} +.site-footer .footer-nav ul.pages a { color:rgba(255,255,255,1.00); font-weight:bold; text-transform:uppercase; } +.site-footer .footer-nav ul.pages a:hover { color:rgba(255,255,255,0.80); text-decoration:none; } +.site-footer .footer-nav h4 { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 3px; + margin-bottom:10px; +} + +.site-footer .footer-nav ul.list { list-style:none; padding:0px; } +.site-footer .footer-nav ul.list li { padding:5px 0px;} +.site-footer .footer-nav ul.list a { color:rgba(255,255,255,0.80); } +.site-footer .footer-nav ul.list a:hover { color:rgba(255,255,255,0.60); text-decoration:none; } + +.site-footer .footer-social ul { list-style:none; padding:0px; } +.site-footer .footer-social h4 { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 3px; +} +.site-footer .footer-social li { padding:5px 4px;} +.site-footer .footer-social a { color:rgba(255,255,255,1.00);} +.site-footer .footer-social a:hover { color:rgba(255,255,255,0.80); text-decoration:none; } + +.site-footer .footer-ns h4 { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 3px; + margin-bottom:10px; +} +.site-footer .footer-ns p { font-size:12px; color:rgba(255,255,255,0.70); } + +.site-logo a { + text-transform: uppercase; + letter-spacing: .2em; + font-size: 22px; + color: #fff !important; + font-weight: 900; +} + +.site-logo a:hover { + text-decoration: none; +} + +.icons-btn { + display: inline-block; + text-align: center; +} + +.icons-btn:hover { + color: #EEEEEE; +} + +.icons-btn span { + display: block; + height: 40px; + width: 40px; + line-height: 40px; +} + +.my-account-text { + font-size: 12px !important; + vertical-align: text-top; +} + +@media (max-width: 991.98px) { + .icons-btn span { + width: 24px; + } +} + +.site-menu-toggle { + display: block; + text-align: center; + font-size: 28px; + height: 40px; + width: 40px; + line-height: 40px; +} + +.site-menu-toggle > span { + top: 5px; + position: relative; +} + +.site-navbar { + margin-bottom: 0px; + z-index: 1999; + position: relative; + top: 0; + width: 100%; + background-color: #3c3d41; +} + +.site-navbar.position-relative { + position: relative; + background: #f9f9f9 !important; +} + +.site-navbar.transparent { + background: transparent; +} + +.site-navbar .site-navbar-top { + border-bottom: 1px solid #f3f3f4; + padding-top: 20px; + padding-bottom: 20px; + margin-bottom: 0px; +} + +@media (min-width: 768px) { + .site-navbar .site-navbar-top { + padding-top: 40px; + padding-bottom: 40px; + } +} + +.site-navbar .site-search-icon a span { + display: inline-block; + margin-right: 10px; +} + +.site-navbar .site-search-icon a:hover { + text-decoration: none; +} + +.site-navbar a { + color: #fff; +} + +.site-navbar a:hover { + color:rgba(255,255,255,0.80); +} + +.site-navbar .icon { + font-size: 20px; +} + +.site-navbar .site-top-icons a { + color: #fff; +} + +.site-navbar .site-top-icons a:hover { + color:rgba(255,255,255,0.80); +} + +.site-navbar .site-top-icons ul, .site-navbar .site-top-icons ul li { + padding: 0; + margin: 0; + list-style: none; +} + +.site-navbar .site-top-icons ul li { + display: inline-block; +} + +.site-navbar .site-top-icons ul li a { + /*margin-right: 10px;*/ +} + +.site-navbar .site-top-icons ul li a.site-cart { + display: block; + position: relative; +} + +.site-navbar .site-top-icons ul li a.site-cart .count { + position: absolute; + top: 0; + right: 0; + margin-right: -15px; + margin-top: -20px; + font-size: 13px; + width: 24px; + height: 24px; + line-height: 24px; + border-radius: 50%; + display: block; + text-align: center; + background: #51eaea; + color: #fff; + -webkit-transition: .2s all ease-in-out; + -o-transition: .2s all ease-in-out; + transition: .2s all ease-in-out; +} + +.site-navbar .site-top-icons ul li a:hover .count { + -webkit-box-shadow: 0 3px 10px -4px rgba(0, 0, 0, 0.3) !important; + box-shadow: 0 3px 10px -4px rgba(0, 0, 0, 0.3) !important; + margin-top: -22px; +} + +.site-navbar .site-top-icons ul li:last-child a { + padding-right: 0; +} + +.site-navbar .site-top-icons .has-children { + position: relative; +} + +.site-navbar .site-top-icons .has-children > a { + position: relative; + padding-right: 20px; +} + +.site-navbar .site-top-icons .has-children > a::before { + position: absolute; + content: "\e313"; + font-size: 16px; + top: 50%; + right: 0; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + font-family: 'icomoon'; +} + +.site-navbar .site-top-icons .has-children .dropdown { + visibility: hidden; + opacity: 0; + top: 100%; + position: absolute; + text-align: left; + border-top: 2px solid #8c92a0; + -webkit-box-shadow: 0 0px 4px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0 0px 4px 0px rgba(0, 0, 0, 0.05); + border-left: 1px solid #5c626e; + border-right: 1px solid #5c626e; + border-bottom: 1px solid #5c626e; + padding: 0px 0; + margin-top: 20px; + margin-left: 0px; + background: #3c3d41; + -webkit-transition: 0.2s 0s; + -o-transition: 0.2s 0s; + transition: 0.2s 0s; +} + +.site-navbar .site-top-icons .has-children .dropdown a { + font-size: 16px; + text-transform: none; + letter-spacing: normal; + -webkit-transition: 0s all; + -o-transition: 0s all; + transition: 0s all; +} + +.site-navbar .site-top-icons .has-children .dropdown .active > a { + color: #51eaea !important; +} + +.site-navbar .site-top-icons .has-children .dropdown > li { + list-style: none; + padding: 0; + margin: 0; + min-width: 200px; +} + +.site-navbar .site-top-icons .has-children .dropdown > li > a { + padding: 9px 20px; + display: block; +} + +.site-navbar .site-top-icons .has-children .dropdown > li > a:hover { + background: #f4f5f9; + color: #25262a; +} + +.site-navbar .site-top-icons .has-children .dropdown > li.has-children > a:before { + content: "\e315"; + right: 20px; +} + +.site-navbar .site-top-icons .has-children .dropdown > li.has-children > .dropdown, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children > ul { + left: 100%; + top: 0; +} + +.site-navbar .site-top-icons .has-children .dropdown > li.has-children:hover > a, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children:active > a, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children:focus > a { + background: #f4f5f9; + color: #25262a; +} + +.site-navbar .site-top-icons .has-children a { + /*text-transform: uppercase;*/ +} + +.site-navbar .site-top-icons .has-children:hover > a, .site-navbar .site-navigation .site-menu .has-children:focus > a, .site-navbar .site-navigation .site-menu .has-children:active > a { + color: #fff; +} + +.site-navbar .site-top-icons .has-children:hover, .site-navbar .site-navigation .site-menu .has-children:focus, .site-navbar .site-navigation .site-menu .has-children:active { + cursor: pointer; +} + +.site-navbar .site-top-icons .has-children:hover > .dropdown, .site-navbar .site-navigation .site-menu .has-children:focus > .dropdown, .site-navbar .site-navigation .site-menu .has-children:active > .dropdown { + -webkit-transition-delay: 0s; + -o-transition-delay: 0s; + transition-delay: 0s; + margin-top: 0px; + visibility: visible; + opacity: 1; +} + + + +.site-navbar .site-navigation.border-bottom { + border-bottom: 1px solid #f3f3f4 !important; +} + +.site-navbar .site-navigation .site-menu { + margin-left: 0; + padding-left: 0; + margin-bottom: 0; +} + +.site-navbar .site-navigation .site-menu .active > a { + color: #000; + position: relative; +} + +.site-navbar .site-navigation .site-menu .active > a:before { + content: ""; + position: absolute; + left: 10px; + right: 10px; + height: 2px; + background: #000; + bottom: 0; +} + +.site-navbar .site-navigation .site-menu a { + text-decoration: none !important; + font-size: 15px; + display: inline-block; +} + +.site-navbar .site-navigation .site-menu > li { + display: inline-block; + padding: 10px 5px; +} + +.site-navbar .site-navigation .site-menu > li > a { + padding: 10px 10px; + text-transform: uppercase; + letter-spacing: .05em; + color: #fff; + font-size: 14px; + text-decoration: none !important; +} + +.site-navbar .site-navigation .site-menu > li > a:hover { + color: #eee; +} + +.site-navbar .site-navigation .site-menu .has-children { + position: relative; +} + +.site-navbar .site-navigation .site-menu .has-children > a { + position: relative; + padding-right: 20px; +} + +.site-navbar .site-navigation .site-menu .has-children > a::before { + position: absolute; + content: "\e313"; + font-size: 16px; + top: 50%; + right: 0; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + font-family: 'icomoon'; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown { + visibility: hidden; + opacity: 0; + top: 100%; + position: absolute; + text-align: left; + border-top: 2px solid #8c92a0; + -webkit-box-shadow: 0 0px 4px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0 0px 4px 0px rgba(0, 0, 0, 0.05); + border-left: 1px solid #5c626e; + border-right: 1px solid #5c626e; + border-bottom: 1px solid #5c626e; + padding: 0px 0; + margin-top: 20px; + margin-left: 0px; + background: #3c3d41; + -webkit-transition: 0.2s 0s; + -o-transition: 0.2s 0s; + transition: 0.2s 0s; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown a { + font-size: 16px; + text-transform: none; + letter-spacing: normal; + -webkit-transition: 0s all; + -o-transition: 0s all; + transition: 0s all; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown .active > a { + color: #51eaea !important; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li { + list-style: none; + padding: 0; + margin: 0; + min-width: 200px; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li > a { + padding: 9px 20px; + display: block; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li > a:hover { + background: #f4f5f9; + color: #25262a; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children > a:before { + content: "\e315"; + right: 20px; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children > .dropdown, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children > ul { + left: 100%; + top: 0; +} + +.site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children:hover > a, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children:active > a, .site-navbar .site-navigation .site-menu .has-children .dropdown > li.has-children:focus > a { + background: #f4f5f9; + color: #25262a; +} + +.site-navbar .site-navigation .site-menu .has-children a { + text-transform: uppercase; +} + +.site-navbar .site-navigation .site-menu .has-children:hover > a, .site-navbar .site-navigation .site-menu .has-children:focus > a, .site-navbar .site-navigation .site-menu .has-children:active > a { + color: #fff; +} + +.site-navbar .site-navigation .site-menu .has-children:hover, .site-navbar .site-navigation .site-menu .has-children:focus, .site-navbar .site-navigation .site-menu .has-children:active { + cursor: pointer; +} + +.site-navbar .site-navigation .site-menu .has-children:hover > .dropdown, .site-navbar .site-navigation .site-menu .has-children:focus > .dropdown, .site-navbar .site-navigation .site-menu .has-children:active > .dropdown { + -webkit-transition-delay: 0s; + -o-transition-delay: 0s; + transition-delay: 0s; + margin-top: 0px; + visibility: visible; + opacity: 1; +} + +.site-navbar .secondary-nav .site-navigation .site-menu > li > a { + text-transform: none; +} + +.site-mobile-menu { + width: 300px; + position: fixed; + right: 0; + z-index: 2000; + padding-top: 20px; + background: #fff; + height: calc(100vh); + -webkit-transform: translateX(110%); + -ms-transform: translateX(110%); + transform: translateX(110%); + -webkit-box-shadow: -10px 0 20px -10px rgba(0, 0, 0, 0.1); + box-shadow: -10px 0 20px -10px rgba(0, 0, 0, 0.1); + -webkit-transition: .3s all ease-in-out; + -o-transition: .3s all ease-in-out; + transition: .3s all ease-in-out; +} + +.offcanvas-menu .site-mobile-menu { + -webkit-transform: translateX(0%); + -ms-transform: translateX(0%); + transform: translateX(0%); +} + +.site-mobile-menu .site-mobile-menu-header { + width: 100%; + float: left; + margin-bottom: 20px; + padding-left: 20px; + padding-right: 20px; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-close { + float: right; + margin-top: 8px; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-close span { + font-size: 40px; + display: inline-block; + padding-left: 10px; + padding-right: 10px; + line-height: 1; + cursor: pointer; + -webkit-transition: .3s all ease; + -o-transition: .3s all ease; + transition: .3s all ease; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-close span:hover { + color: #25262a; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-logo { + float: left; + margin-top: 10px; + margin-left: 20px; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-logo a { + display: inline-block; + text-transform: uppercase; + color: #000; + letter-spacing: .2em; + font-size: 22px; + font-weight: 900; +} + +.site-mobile-menu .site-mobile-menu-header .site-mobile-menu-logo a:hover { + text-decoration: none; +} + +.site-mobile-menu .site-mobile-menu-body { + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + position: relative; + padding: 20px; + height: calc(100vh - 52px); + padding-bottom: 150px; +} + +.site-mobile-menu .site-nav-wrap { + padding: 0; + margin: 0; + list-style: none; + position: relative; +} + +.site-mobile-menu .site-nav-wrap a { + padding: 10px 20px; + display: block; + position: relative; + color: #212529; +} + +.site-mobile-menu .site-nav-wrap a:hover { + color: #51eaea; +} + +.site-mobile-menu .site-nav-wrap li { + position: relative; + display: block; +} + +.site-mobile-menu .site-nav-wrap li.active > a { + color: #51eaea; +} + +.site-mobile-menu .site-nav-wrap .arrow-collapse { + position: absolute; + right: 0px; + top: 10px; + z-index: 20; + width: 36px; + height: 36px; + text-align: center; + cursor: pointer; + border-radius: 50%; +} + +.site-mobile-menu .site-nav-wrap .arrow-collapse:hover { + background: #f8f9fa; +} + +.site-mobile-menu .site-nav-wrap .arrow-collapse:before { + font-size: 18px; + z-index: 20; + font-family: "icomoon"; + content: "\e313"; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%) rotate(-180deg); + -ms-transform: translate(-50%, -50%) rotate(-180deg); + transform: translate(-50%, -50%) rotate(-180deg); + -webkit-transition: .3s all ease; + -o-transition: .3s all ease; + transition: .3s all ease; +} + +.site-mobile-menu .site-nav-wrap .arrow-collapse.collapsed:before { + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +.site-mobile-menu .site-nav-wrap > li { + display: block; + position: relative; + float: left; + width: 100%; +} + +.site-mobile-menu .site-nav-wrap > li > a { + padding-left: 20px; + font-size: 20px; +} + +.site-mobile-menu .site-nav-wrap > li > ul { + padding: 0; + margin: 0; + list-style: none; +} + +.site-mobile-menu .site-nav-wrap > li > ul > li { + display: block; +} + +.site-mobile-menu .site-nav-wrap > li > ul > li > a { + padding-left: 40px; + font-size: 16px; +} + +.site-mobile-menu .site-nav-wrap > li > ul > li > ul { + padding: 0; + margin: 0; +} + +.site-mobile-menu .site-nav-wrap > li > ul > li > ul > li { + display: block; +} + +.site-mobile-menu .site-nav-wrap > li > ul > li > ul > li > a { + font-size: 16px; + padding-left: 60px; +} + +/* Blocks */ +.site-blocks-cover { + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + position: relative; +} + +.site-blocks-cover, .site-blocks-cover > .container > .row { + min-height: 700px; + //height: calc(100vh); +} + +.site-blocks-cover.inner-page, .site-blocks-cover.inner-page .row { + min-height: 400px; + height: 700px; +} + +.site-blocks-cover h2 { + font-size: 14px; + font-weight: normal; + color: #000; + letter-spacing: .2em; + text-transform: uppercase; +} + +@media (max-width: 991.98px) { + .site-blocks-cover .featured-hero-product { + margin-top: 10%; + position: absolute; + } +} + +.site-blocks-cover h1 { + font-size: 50px; + font-weight: 900; + color: #fff; + margin-bottom: 30px; + text-transform: uppercase; +} + +@media (min-width: 768px) { + .site-blocks-cover h1 { + font-size: 70px; + } +} + +.site-blocks-cover p { + color: #fff; + font-size: 20px; + line-height: 35px; +} + +.site-blocks-cover .intro-text { + font-size: 16px; + color: #fff; + line-height: 1.5; +} + +.site-blocks-cover.inner-page .site-block-cover-content { + margin-top: 20%; + text-align: center; +} + +@media (min-width: 768px) { + .site-blocks-cover.inner-page .site-block-cover-content { + margin-top: 30%; + } +} + +.site-blocks-cover .main-title { + color: #000; + text-shadow: 2px 2px #cccccc; +} +.site-blocks-cover .sub-title { + color: #000; + text-shadow: 2px 2px #cccccc; +} + +@media (max-width: 991.98px) { + .site-blocks-cover .img-1 { + max-width: 400px; + margin-left: auto; + margin-right: auto; + } +} + +.site-blocks-cover .btn { + padding: 20px 30px; +} + +.site-blocks-1 { + border-bottom: 1px solid #edf0f5; +} + +.site-blocks-1 .divider { + position: relative; +} + +.site-blocks-1 .divider:after { + content: ""; + position: absolute; + height: 100%; + width: 1px; + right: 10px; + background: #edf0f5; +} + +.site-blocks-1 .divider:last-child:after { + display: none; +} + +.site-blocks-1 .icon span { + position: relative; + color: #6c757d; + top: -10px; + font-size: 50px; + display: inline-block; +} + +.site-blocks-1 .text h2 { + color: #25262a; + letter-spacing: .05em; + font-size: 18px; +} + +.site-blocks-1 .text p:last-child { + margin-bottom: 0; +} + +.site-blocks-2 .block-2-item { + display: block; + position: relative; +} + +.site-blocks-2 .block-2-item:before { + z-index: 1; + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: -moz-linear-gradient(top, transparent 0%, transparent 18%, rgba(0, 0, 0, 0.8) 99%, rgba(0, 0, 0, 0.8) 100%); + background: -webkit-linear-gradient(top, transparent 0%, transparent 18%, rgba(0, 0, 0, 0.8) 99%, rgba(0, 0, 0, 0.8) 100%); + background: -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(18%, transparent), color-stop(99%, rgba(0, 0, 0, 0.8)), to(rgba(0, 0, 0, 0.8))); + background: -o-linear-gradient(top, transparent 0%, transparent 18%, rgba(0, 0, 0, 0.8) 99%, rgba(0, 0, 0, 0.8) 100%); + background: linear-gradient(to bottom, transparent 0%, transparent 18%, rgba(0, 0, 0, 0.8) 99%, rgba(0, 0, 0, 0.8) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#cc000000', GradientType=0); +} + +.site-blocks-2 .block-2-item .image { + position: relative; + margin-bottom: 0; + overflow: hidden; +} + +.site-blocks-2 .block-2-item .image img { + margin-bottom: 0; + -webkit-transition: .3s all ease-in-out; + -o-transition: .3s all ease-in-out; + transition: .3s all ease-in-out; +} + +.site-blocks-2 .block-2-item .text { + z-index: 2; + bottom: 0; + padding-left: 20px; + position: absolute; + width: 100%; +} + +.site-blocks-2 .block-2-item .text > span, .site-blocks-2 .block-2-item .text h3 { + color: #fff; +} + +.site-blocks-2 .block-2-item .text > span { + font-size: 12px; + letter-spacing: .1em; + font-weight: 900; +} + +.site-blocks-2 .block-2-item .text h3 { + font-size: 40px; +} + +.site-blocks-2 .block-2-item:hover .image img { + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); +} + +.block-3 .no-direction .owl-nav { + display: none; +} + +.block-3 .owl-stage { + padding-top: 40px; + padding-bottom: 40px; +} + +.block-3 .owl-nav .owl-prev, .block-3 .owl-nav .owl-next { + position: absolute; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + font-size: 2rem; +} + +.block-3 .owl-nav .owl-prev { + left: 0; +} + +.block-3 .owl-nav .owl-next { + right: 0; +} + +.block-3 .owl-dots { + text-align: center; +} + +.block-3 .owl-dots .owl-dot { + display: inline-block; +} + +.block-3 .owl-dots .owl-dot > span { + width: 10px; + height: 10px; + border-radius: 50%; + display: inline-block; + margin: 5px; + background: #ccc; +} + +.block-3 .owl-dots .owl-dot.active > span { + background: #51eaea; +} + +.block-3 .product { + border-right: none !important; + border-left: none !important; +} + +.block-3 .product .item { + border: 1px solid #eee; +} + +.block-4 { + -webkit-box-shadow: 0 0 30px -10px rgba(0, 0, 0, 0.1); + box-shadow: 0 0 30px -10px rgba(0, 0, 0, 0.1); + background: #fff; +} + +.block-4 .block-4-text h3 { + font-size: 20px; + margin-bottom: 0; +} + +.block-4 .block-4-text h3 a { + text-decoration: none; +} + +.block-5 ul, .block-5 ul li { + list-style: none; + padding: 0; + margin: 0; + line-height: 1.5; +} + +.block-5 ul li { + padding-left: 30px; + position: relative; + margin-bottom: 15px; + color: #25262a; +} + +.block-5 ul li:before { + top: 0; + font-family: "icomoon"; + content: ""; + position: absolute; + left: 0; + font-size: 20px; + line-height: 1; + color: #51eaea; +} + +.block-5 ul li.address:before { + content: "\e8b4"; +} + +.block-5 ul li.email:before { + content: "\f0e0"; +} + +.block-5 ul li.phone:before { + content: "\f095"; +} + +.block-6 { + display: block; +} + +.block-6 img { + display: block; +} + +.block-6 h3 { + font-size: 18px; +} + +.block-6 p { + color: #737b8a; +} + +.block-7 .form-group { + position: relative; +} + +.block-7 .form-control { + padding-right: 96px; +} + +.block-7 .btn { + position: absolute; + width: 80px; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + right: 3px; +} + +.block-8 .post-meta { + color: #c4c7ce; +} + +.block-8 .block-8-sep { + margin-left: 10px; + margin-right: 10px; +} + +.site-blocks-table { + overflow: auto; +} + +.site-blocks-table .product-thumbnail { + width: 200px; +} + +.site-blocks-table thead th { + padding: 30px; + text-align: center; + border-width: 1px !important; + vertical-align: middle; + color: #212529; + font-size: 18px; + font-weight: normal; +} + +.site-blocks-table td { + padding: 20px; + text-align: center; + vertical-align: middle; + color: #212529; +} + +.site-blocks-table tbody tr:first-child td { + border-top: 1px solid #51eaea !important; +} + +.site-block-order-table th { + border-top: none !important; + border-bottom-width: 1px !important; +} + +.site-block-order-table td, .site-block-order-table th { + font-weight: normal; + color: #000; +} + +.site-block-top-search { + position: relative; +} + +.site-block-top-search .icon { + position: absolute; + left: 0; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +.site-block-top-search input { + padding-left: 40px; + -webkit-transition: .3s all ease-in-out; + -o-transition: .3s all ease-in-out; + transition: .3s all ease-in-out; +} + +.site-block-top-search input:focus, .site-block-top-search input:active { + padding-left: 25px; +} + +.site-block-27 ul, .site-block-27 ul li { + padding: 0; + margin: 0; +} + +.site-block-27 ul li { + display: inline-block; + margin-bottom: 4px; +} + +.site-block-27 ul li a, .site-block-27 ul li span { + text-align: center; + display: inline-block; + width: 40px; + height: 40px; + line-height: 40px; + border-radius: 50%; + border: 1px solid #eee; + color: #000; +} + +.site-block-27 ul li.active a, .site-block-27 ul li.active span { + background: #343a40; + color: #fff; + border: 1px solid transparent; +} + +#slider-range { + height: 8px; +} + +#slider-range .ui-slider-handle { + width: 16px; + height: 16px; + border-radius: 50%; + border: none !important; + background: #51eaea; +} + +#slider-range .ui-slider-handle:focus, #slider-range .ui-slider-handle:active { + outline: none; +} + +#slider-range .ui-slider-range { + background-color: #51eaea; +} + +.color-item .color { + width: 14px; + height: 14px; +} + +.block-16 figure { + position: relative; +} + +.block-16 figure .play-button { + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + font-size: 40px; + width: 90px; + height: 90px; + background: #fff; + display: block; + border-radius: 50%; + border: none; +} + +.block-16 figure .play-button:hover { + opacity: 1; +} + +.block-16 figure .play-button > span { + position: absolute; + left: 55%; + top: 50%; + -webkit-transform: translate(-50%, -45%); + -ms-transform: translate(-50%, -45%); + transform: translate(-50%, -45%); +} + +.block-38 .block-38-header .block-38-heading { + color: #000; + margin: 0; + font-weight: 300; +} + +.block-38 .block-38-header .block-38-subheading { + color: #b3b3b3; + margin: 0 0 20px 0; + text-transform: uppercase; + font-size: 15px; + letter-spacing: .1em; +} + +.block-38 .block-38-header img { + width: 120px; + border-radius: 50%; + margin-bottom: 20px; +} + +.sign-in { + width: 100px; +} +.bag { + position: relative; +} + +.bag .number { + position: absolute; + top: 0; + width: 20px; + height: 20px; + border-radius: 50%; + line-height: 20px; + color: #000; + font-size: 12px; + background: #51eaea; + right: -5px; +} + +.search-wrap { + position: absolute; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + color: #fff; + background-color: #3c3d41; + z-index: 999; + opacity: 0; + visibility: hidden; + -webkit-transition: .5s all ease; + -o-transition: .5s all ease; + transition: .5s all ease; +} + +.search-wrap .container { + position: relative; + height: 100%; +} + +.search-wrap.active { + opacity: 1; + visibility: visible; +} + +.search-wrap .form-control { + position: absolute; + top: 50%; + width: 100%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + border: none; + z-index: 3; + font-size: 20px; +} + +@media (max-width: 991.98px) { + .search-wrap .form-control { + font-size: 20px; + } +} + +.search-wrap .search-close { + z-index: 4; + position: absolute; + right: 20px; + top: 50%; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +.search-wrap .search-close span { + font-size: 30px; + color: #000; + vertical-align: middle; +} + +.title-section h2 { + padding-left: 30px; + border-left: 4px solid #51eaea; + display: inline-block; + color: #000; +} + +.title-section.text-center h2 { + border-left: none; + position: relative; + padding-left: 0px; + padding-bottom: 20px; +} + +.title-section.text-center h2:after { + display: none; + position: absolute; + bottom: 0; + left: 50%; + -webkit-transform: translateX(-50%); + -ms-transform: translateX(-50%); + transform: translateX(-50%); + width: 70px; + height: 4px; + background: #51eaea; + content: ""; +} + +.product-item { + overflow: hidden; + height: 100%; + position: relative; +} + +.product-item .product-category { + position: absolute; + display: inline-block; + padding: 10px 30px; + background: #fff; + color: #000; + z-index: 8; + top: 60%; + left: 50%; + font-size: 20px; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +.product-item .product-category span { + color: #ced4da; + font-size: 12px; +} + +.product-item .product-category:hover { + background: #000; + color: #fff; +} + +.product-item .product-category:hover span { + color: rgba(255, 255, 255, 0.5); +} + +.product-item.full-height { + height: 100% !important; +} + +@media (max-width: 991.98px) { + .product-item.full-height { + height: 300px !important; + margin-bottom: 30px; + } +} + +.product-item.bg-gray { + background: #f9f9f9; +} + +.product-item.sm-height { + height: 300px; + padding: 40px; +} + +.product-item.md-height { + height: 700px; + padding: 40px; +} + +.product-item img { + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%) scale(0.7); + -ms-transform: translate(-50%, -50%) scale(0.7); + transform: translate(-50%, -50%) scale(0.7); + position: absolute; +} + +.item-entry .product-item { + margin-bottom: 30px; +} + +.item-entry .item-title { + font-size: 18px; + font-weight: normal; + margin-bottom: 0; +} + +.item-entry .item-title a { + color: gray; +} + +.item-entry .item-title a:hover { + color: #000; +} + +.item-entry .item-price { + color: #000; + font-weight: normal; +} + +.item-entry .item-price del { + color: #b3b3b3; +} + +.custom-border-bottom { + border-bottom: 1px solid #f9f9f9; +} + +.custom-border-top { + border-top: 1px solid #f9f9f9; +} + +.height-auto { + height: auto !important; +} + +.featured-hero-product { + text-align: center; +} + +.featured-hero-product h1, .featured-hero-product h4 { + color: #000; +} + +.featured-hero-product h1 { + font-size: 4rem; + letter-spacing: 1rem; +} + +@media (max-width: 991.98px) { + .featured-hero-product h1 { + font-size: 2rem; + } +} + +.featured-hero-product h4 { + font-size: 1rem; +} + +.featured-hero-product .price { + font-size: 2rem; + color: #000; +} + +@media (max-width: 991.98px) { + .featured-hero-product .price { + font-size: 1.5rem; + } +} + +.featured-hero-product .price del { + margin-left: 10px; + color: #6c757d; +} + +.border { + border: 1px solid #eee !important; +} + +.border a { + display: block; + padding: 30px; +} + +.item h3 a { + color: #000; +} + +.item .price { + color: #000; +} + +.tag { + position: absolute; + background: #e86b00; + color: #fff; + letter-spacing: .2em; + padding: 7px 20px; + font-size: .8rem; + font-weight: 900; + top: 0; + left: 0; + text-transform: uppercase; + z-index: 2; + -webkit-transform: rotate(-90deg) translateX(-100%) translateY(-70%); + -ms-transform: rotate(-90deg) translateX(-100%) translateY(-70%); + transform: rotate(-90deg) translateX(-100%) translateY(-70%); +} + +.banner-wrap { + background: #74d12b; + border-radius: 4px; +} + +.banner-wrap a { + padding: 30px; + display: block; + text-align: center; +} + +.banner-wrap a h5 { + font-size: 2rem; + font-style: italic; + margin-bottom: 10px; +} + +.banner-wrap a h5, .banner-wrap a p { + color: #fff; +} + +.banner-wrap a p { + color: #fff; + font-size: 1.4rem; +} + +.banner-wrap a p strong { + font-size: .9rem; + display: block; +} + +.banner-1 { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + padding: 20px; + display: block; + min-height: 300px; + border-radius: 4px; + -webkit-box-shadow: 0 10px 30px -15px rgba(0, 0, 0, 0.2); + box-shadow: 0 10px 30px -15px rgba(0, 0, 0, 0.2); + -webkit-transition: .3s all ease; + -o-transition: .3s all ease; + transition: .3s all ease; + position: relative; + top: 0; +} + +.banner-1:hover { + top: -10px; + -webkit-box-shadow: 0 10px 30px -15px rgba(0, 0, 0, 0.4); + box-shadow: 0 10px 30px -15px rgba(0, 0, 0, 0.4); +} + +.banner-1 .banner-1-inner { + width: 50%; +} + +.banner-1 .banner-1-inner h2 { + color: #000; + text-transform: uppercase; + font-weight: 900; +} + +.banner-1 .banner-1-inner p { + color: rgba(0, 0, 0, 0.5); + font-size: 1.2rem; +} + +.testimony blockquote p { + color: #000; + font-family: "Crimson Text", serif; + font-size: 1.3rem; + line-height: 1.5; + font-style: italic; +} + +.bg-image { + background-size: cover; + position: relative; +} + +.bg-image:after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(81, 234, 234, 0.9); + z-index: 1; +} + +.bg-image > .container { + position: relative; + z-index: 2; +} + +.section-overlap { + margin-top: -150px; +} + +.custom-pill .nav-item { + margin-right: 4px; +} + +.custom-pill .nav-item a { + background: #f4f4f4; + color: #000 !important; +} + +.custom-table th { + color: #000; + font-weight: normal; +} + +.custom-table td { + color: #000; +} + +.breadcrumb { + list-style: none; + font-size: x-large; +} + +.form-login { + width: 100%; + max-width: 330px; + padding: 15px; + margin: 0 auto; +} +.form-login .checkbox { + font-weight: 400; +} +.form-login .form-control { + position: relative; + box-sizing: border-box; + height: auto; + padding: 10px; + font-size: 16px; +} +.form-login .form-control:focus { + z-index: 2; +} +.form-login input[type="username"] { + margin-bottom: -1px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.form-login input[type="password"] { + margin-bottom: 10px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.modal { + z-index: 10000000 !important; +} + diff --git a/src/main/resources/static/css/icomoon/fonts/icomoon.eot b/src/main/resources/static/css/icomoon/fonts/icomoon.eot new file mode 100644 index 0000000..352a9b2 Binary files /dev/null and b/src/main/resources/static/css/icomoon/fonts/icomoon.eot differ diff --git a/src/main/resources/static/css/icomoon/fonts/icomoon.svg b/src/main/resources/static/css/icomoon/fonts/icomoon.svg new file mode 100644 index 0000000..bf75889 --- /dev/null +++ b/src/main/resources/static/css/icomoon/fonts/icomoon.svg @@ -0,0 +1,1530 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/static/css/icomoon/fonts/icomoon.ttf b/src/main/resources/static/css/icomoon/fonts/icomoon.ttf new file mode 100644 index 0000000..c3d4be0 Binary files /dev/null and b/src/main/resources/static/css/icomoon/fonts/icomoon.ttf differ diff --git a/src/main/resources/static/css/icomoon/fonts/icomoon.woff b/src/main/resources/static/css/icomoon/fonts/icomoon.woff new file mode 100644 index 0000000..ba6edd9 Binary files /dev/null and b/src/main/resources/static/css/icomoon/fonts/icomoon.woff differ diff --git a/src/main/resources/static/css/icomoon/style.css b/src/main/resources/static/css/icomoon/style.css new file mode 100644 index 0000000..05a9423 --- /dev/null +++ b/src/main/resources/static/css/icomoon/style.css @@ -0,0 +1,4919 @@ +@font-face { + font-family: 'icomoon'; + src: url('fonts/icomoon.eot?10si43'); + src: url('fonts/icomoon.eot?10si43#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?10si43') format('truetype'), + url('fonts/icomoon.woff?10si43') format('woff'), + url('fonts/icomoon.svg?10si43#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-asterisk:before { + content: "\f069"; +} +.icon-plus:before { + content: "\f067"; +} +.icon-question:before { + content: "\f128"; +} +.icon-minus:before { + content: "\f068"; +} +.icon-glass:before { + content: "\f000"; +} +.icon-music:before { + content: "\f001"; +} +.icon-search:before { + content: "\f002"; +} +.icon-envelope-o:before { + content: "\f003"; +} +.icon-heart:before { + content: "\f004"; +} +.icon-star:before { + content: "\f005"; +} +.icon-star-o:before { + content: "\f006"; +} +.icon-user:before { + content: "\f007"; +} +.icon-film:before { + content: "\f008"; +} +.icon-th-large:before { + content: "\f009"; +} +.icon-th:before { + content: "\f00a"; +} +.icon-th-list:before { + content: "\f00b"; +} +.icon-check:before { + content: "\f00c"; +} +.icon-close:before { + content: "\f00d"; +} +.icon-remove:before { + content: "\f00d"; +} +.icon-times:before { + content: "\f00d"; +} +.icon-search-plus:before { + content: "\f00e"; +} +.icon-search-minus:before { + content: "\f010"; +} +.icon-power-off:before { + content: "\f011"; +} +.icon-signal:before { + content: "\f012"; +} +.icon-cog:before { + content: "\f013"; +} +.icon-gear:before { + content: "\f013"; +} +.icon-trash-o:before { + content: "\f014"; +} +.icon-home:before { + content: "\f015"; +} +.icon-file-o:before { + content: "\f016"; +} +.icon-clock-o:before { + content: "\f017"; +} +.icon-road:before { + content: "\f018"; +} +.icon-download:before { + content: "\f019"; +} +.icon-arrow-circle-o-down:before { + content: "\f01a"; +} +.icon-arrow-circle-o-up:before { + content: "\f01b"; +} +.icon-inbox:before { + content: "\f01c"; +} +.icon-play-circle-o:before { + content: "\f01d"; +} +.icon-repeat:before { + content: "\f01e"; +} +.icon-rotate-right:before { + content: "\f01e"; +} +.icon-refresh:before { + content: "\f021"; +} +.icon-list-alt:before { + content: "\f022"; +} +.icon-lock:before { + content: "\f023"; +} +.icon-flag:before { + content: "\f024"; +} +.icon-headphones:before { + content: "\f025"; +} +.icon-volume-off:before { + content: "\f026"; +} +.icon-volume-down:before { + content: "\f027"; +} +.icon-volume-up:before { + content: "\f028"; +} +.icon-qrcode:before { + content: "\f029"; +} +.icon-barcode:before { + content: "\f02a"; +} +.icon-tag:before { + content: "\f02b"; +} +.icon-tags:before { + content: "\f02c"; +} +.icon-book:before { + content: "\f02d"; +} +.icon-bookmark:before { + content: "\f02e"; +} +.icon-print:before { + content: "\f02f"; +} +.icon-camera:before { + content: "\f030"; +} +.icon-font:before { + content: "\f031"; +} +.icon-bold:before { + content: "\f032"; +} +.icon-italic:before { + content: "\f033"; +} +.icon-text-height:before { + content: "\f034"; +} +.icon-text-width:before { + content: "\f035"; +} +.icon-align-left:before { + content: "\f036"; +} +.icon-align-center:before { + content: "\f037"; +} +.icon-align-right:before { + content: "\f038"; +} +.icon-align-justify:before { + content: "\f039"; +} +.icon-list:before { + content: "\f03a"; +} +.icon-dedent:before { + content: "\f03b"; +} +.icon-outdent:before { + content: "\f03b"; +} +.icon-indent:before { + content: "\f03c"; +} +.icon-video-camera:before { + content: "\f03d"; +} +.icon-image:before { + content: "\f03e"; +} +.icon-photo:before { + content: "\f03e"; +} +.icon-picture-o:before { + content: "\f03e"; +} +.icon-pencil:before { + content: "\f040"; +} +.icon-map-marker:before { + content: "\f041"; +} +.icon-adjust:before { + content: "\f042"; +} +.icon-tint:before { + content: "\f043"; +} +.icon-edit:before { + content: "\f044"; +} +.icon-pencil-square-o:before { + content: "\f044"; +} +.icon-share-square-o:before { + content: "\f045"; +} +.icon-check-square-o:before { + content: "\f046"; +} +.icon-arrows:before { + content: "\f047"; +} +.icon-step-backward:before { + content: "\f048"; +} +.icon-fast-backward:before { + content: "\f049"; +} +.icon-backward:before { + content: "\f04a"; +} +.icon-play:before { + content: "\f04b"; +} +.icon-pause:before { + content: "\f04c"; +} +.icon-stop:before { + content: "\f04d"; +} +.icon-forward:before { + content: "\f04e"; +} +.icon-fast-forward:before { + content: "\f050"; +} +.icon-step-forward:before { + content: "\f051"; +} +.icon-eject:before { + content: "\f052"; +} +.icon-chevron-left:before { + content: "\f053"; +} +.icon-chevron-right:before { + content: "\f054"; +} +.icon-plus-circle:before { + content: "\f055"; +} +.icon-minus-circle:before { + content: "\f056"; +} +.icon-times-circle:before { + content: "\f057"; +} +.icon-check-circle:before { + content: "\f058"; +} +.icon-question-circle:before { + content: "\f059"; +} +.icon-info-circle:before { + content: "\f05a"; +} +.icon-crosshairs:before { + content: "\f05b"; +} +.icon-times-circle-o:before { + content: "\f05c"; +} +.icon-check-circle-o:before { + content: "\f05d"; +} +.icon-ban:before { + content: "\f05e"; +} +.icon-arrow-left:before { + content: "\f060"; +} +.icon-arrow-right:before { + content: "\f061"; +} +.icon-arrow-up:before { + content: "\f062"; +} +.icon-arrow-down:before { + content: "\f063"; +} +.icon-mail-forward:before { + content: "\f064"; +} +.icon-share:before { + content: "\f064"; +} +.icon-expand:before { + content: "\f065"; +} +.icon-compress:before { + content: "\f066"; +} +.icon-exclamation-circle:before { + content: "\f06a"; +} +.icon-gift:before { + content: "\f06b"; +} +.icon-leaf:before { + content: "\f06c"; +} +.icon-fire:before { + content: "\f06d"; +} +.icon-eye:before { + content: "\f06e"; +} +.icon-eye-slash:before { + content: "\f070"; +} +.icon-exclamation-triangle:before { + content: "\f071"; +} +.icon-warning:before { + content: "\f071"; +} +.icon-plane:before { + content: "\f072"; +} +.icon-calendar:before { + content: "\f073"; +} +.icon-random:before { + content: "\f074"; +} +.icon-comment:before { + content: "\f075"; +} +.icon-magnet:before { + content: "\f076"; +} +.icon-chevron-up:before { + content: "\f077"; +} +.icon-chevron-down:before { + content: "\f078"; +} +.icon-retweet:before { + content: "\f079"; +} +.icon-shopping-cart:before { + content: "\f07a"; +} +.icon-folder:before { + content: "\f07b"; +} +.icon-folder-open:before { + content: "\f07c"; +} +.icon-arrows-v:before { + content: "\f07d"; +} +.icon-arrows-h:before { + content: "\f07e"; +} +.icon-bar-chart:before { + content: "\f080"; +} +.icon-bar-chart-o:before { + content: "\f080"; +} +.icon-twitter-square:before { + content: "\f081"; +} +.icon-facebook-square:before { + content: "\f082"; +} +.icon-camera-retro:before { + content: "\f083"; +} +.icon-key:before { + content: "\f084"; +} +.icon-cogs:before { + content: "\f085"; +} +.icon-gears:before { + content: "\f085"; +} +.icon-comments:before { + content: "\f086"; +} +.icon-thumbs-o-up:before { + content: "\f087"; +} +.icon-thumbs-o-down:before { + content: "\f088"; +} +.icon-star-half:before { + content: "\f089"; +} +.icon-heart-o:before { + content: "\f08a"; +} +.icon-sign-out:before { + content: "\f08b"; +} +.icon-linkedin-square:before { + content: "\f08c"; +} +.icon-thumb-tack:before { + content: "\f08d"; +} +.icon-external-link:before { + content: "\f08e"; +} +.icon-sign-in:before { + content: "\f090"; +} +.icon-trophy:before { + content: "\f091"; +} +.icon-github-square:before { + content: "\f092"; +} +.icon-upload:before { + content: "\f093"; +} +.icon-lemon-o:before { + content: "\f094"; +} +.icon-phone:before { + content: "\f095"; +} +.icon-square-o:before { + content: "\f096"; +} +.icon-bookmark-o:before { + content: "\f097"; +} +.icon-phone-square:before { + content: "\f098"; +} +.icon-twitter:before { + content: "\f099"; +} +.icon-facebook:before { + content: "\f09a"; +} +.icon-facebook-f:before { + content: "\f09a"; +} +.icon-github:before { + content: "\f09b"; +} +.icon-unlock:before { + content: "\f09c"; +} +.icon-credit-card:before { + content: "\f09d"; +} +.icon-feed:before { + content: "\f09e"; +} +.icon-rss:before { + content: "\f09e"; +} +.icon-hdd-o:before { + content: "\f0a0"; +} +.icon-bullhorn:before { + content: "\f0a1"; +} +.icon-bell-o:before { + content: "\f0a2"; +} +.icon-certificate:before { + content: "\f0a3"; +} +.icon-hand-o-right:before { + content: "\f0a4"; +} +.icon-hand-o-left:before { + content: "\f0a5"; +} +.icon-hand-o-up:before { + content: "\f0a6"; +} +.icon-hand-o-down:before { + content: "\f0a7"; +} +.icon-arrow-circle-left:before { + content: "\f0a8"; +} +.icon-arrow-circle-right:before { + content: "\f0a9"; +} +.icon-arrow-circle-up:before { + content: "\f0aa"; +} +.icon-arrow-circle-down:before { + content: "\f0ab"; +} +.icon-globe:before { + content: "\f0ac"; +} +.icon-wrench:before { + content: "\f0ad"; +} +.icon-tasks:before { + content: "\f0ae"; +} +.icon-filter:before { + content: "\f0b0"; +} +.icon-briefcase:before { + content: "\f0b1"; +} +.icon-arrows-alt:before { + content: "\f0b2"; +} +.icon-group:before { + content: "\f0c0"; +} +.icon-users:before { + content: "\f0c0"; +} +.icon-chain:before { + content: "\f0c1"; +} +.icon-link:before { + content: "\f0c1"; +} +.icon-cloud:before { + content: "\f0c2"; +} +.icon-flask:before { + content: "\f0c3"; +} +.icon-cut:before { + content: "\f0c4"; +} +.icon-scissors:before { + content: "\f0c4"; +} +.icon-copy:before { + content: "\f0c5"; +} +.icon-files-o:before { + content: "\f0c5"; +} +.icon-paperclip:before { + content: "\f0c6"; +} +.icon-floppy-o:before { + content: "\f0c7"; +} +.icon-save:before { + content: "\f0c7"; +} +.icon-square:before { + content: "\f0c8"; +} +.icon-bars:before { + content: "\f0c9"; +} +.icon-navicon:before { + content: "\f0c9"; +} +.icon-reorder:before { + content: "\f0c9"; +} +.icon-list-ul:before { + content: "\f0ca"; +} +.icon-list-ol:before { + content: "\f0cb"; +} +.icon-strikethrough:before { + content: "\f0cc"; +} +.icon-underline:before { + content: "\f0cd"; +} +.icon-table:before { + content: "\f0ce"; +} +.icon-magic:before { + content: "\f0d0"; +} +.icon-truck:before { + content: "\f0d1"; +} +.icon-pinterest:before { + content: "\f0d2"; +} +.icon-pinterest-square:before { + content: "\f0d3"; +} +.icon-google-plus-square:before { + content: "\f0d4"; +} +.icon-google-plus:before { + content: "\f0d5"; +} +.icon-money:before { + content: "\f0d6"; +} +.icon-caret-down:before { + content: "\f0d7"; +} +.icon-caret-up:before { + content: "\f0d8"; +} +.icon-caret-left:before { + content: "\f0d9"; +} +.icon-caret-right:before { + content: "\f0da"; +} +.icon-columns:before { + content: "\f0db"; +} +.icon-sort:before { + content: "\f0dc"; +} +.icon-unsorted:before { + content: "\f0dc"; +} +.icon-sort-desc:before { + content: "\f0dd"; +} +.icon-sort-down:before { + content: "\f0dd"; +} +.icon-sort-asc:before { + content: "\f0de"; +} +.icon-sort-up:before { + content: "\f0de"; +} +.icon-envelope:before { + content: "\f0e0"; +} +.icon-linkedin:before { + content: "\f0e1"; +} +.icon-rotate-left:before { + content: "\f0e2"; +} +.icon-undo:before { + content: "\f0e2"; +} +.icon-gavel:before { + content: "\f0e3"; +} +.icon-legal:before { + content: "\f0e3"; +} +.icon-dashboard:before { + content: "\f0e4"; +} +.icon-tachometer:before { + content: "\f0e4"; +} +.icon-comment-o:before { + content: "\f0e5"; +} +.icon-comments-o:before { + content: "\f0e6"; +} +.icon-bolt:before { + content: "\f0e7"; +} +.icon-flash:before { + content: "\f0e7"; +} +.icon-sitemap:before { + content: "\f0e8"; +} +.icon-umbrella:before { + content: "\f0e9"; +} +.icon-clipboard:before { + content: "\f0ea"; +} +.icon-paste:before { + content: "\f0ea"; +} +.icon-lightbulb-o:before { + content: "\f0eb"; +} +.icon-exchange:before { + content: "\f0ec"; +} +.icon-cloud-download:before { + content: "\f0ed"; +} +.icon-cloud-upload:before { + content: "\f0ee"; +} +.icon-user-md:before { + content: "\f0f0"; +} +.icon-stethoscope:before { + content: "\f0f1"; +} +.icon-suitcase:before { + content: "\f0f2"; +} +.icon-bell:before { + content: "\f0f3"; +} +.icon-coffee:before { + content: "\f0f4"; +} +.icon-cutlery:before { + content: "\f0f5"; +} +.icon-file-text-o:before { + content: "\f0f6"; +} +.icon-building-o:before { + content: "\f0f7"; +} +.icon-hospital-o:before { + content: "\f0f8"; +} +.icon-ambulance:before { + content: "\f0f9"; +} +.icon-medkit:before { + content: "\f0fa"; +} +.icon-fighter-jet:before { + content: "\f0fb"; +} +.icon-beer:before { + content: "\f0fc"; +} +.icon-h-square:before { + content: "\f0fd"; +} +.icon-plus-square:before { + content: "\f0fe"; +} +.icon-angle-double-left:before { + content: "\f100"; +} +.icon-angle-double-right:before { + content: "\f101"; +} +.icon-angle-double-up:before { + content: "\f102"; +} +.icon-angle-double-down:before { + content: "\f103"; +} +.icon-angle-left:before { + content: "\f104"; +} +.icon-angle-right:before { + content: "\f105"; +} +.icon-angle-up:before { + content: "\f106"; +} +.icon-angle-down:before { + content: "\f107"; +} +.icon-desktop:before { + content: "\f108"; +} +.icon-laptop:before { + content: "\f109"; +} +.icon-tablet:before { + content: "\f10a"; +} +.icon-mobile:before { + content: "\f10b"; +} +.icon-mobile-phone:before { + content: "\f10b"; +} +.icon-circle-o:before { + content: "\f10c"; +} +.icon-quote-left:before { + content: "\f10d"; +} +.icon-quote-right:before { + content: "\f10e"; +} +.icon-spinner:before { + content: "\f110"; +} +.icon-circle:before { + content: "\f111"; +} +.icon-mail-reply:before { + content: "\f112"; +} +.icon-reply:before { + content: "\f112"; +} +.icon-github-alt:before { + content: "\f113"; +} +.icon-folder-o:before { + content: "\f114"; +} +.icon-folder-open-o:before { + content: "\f115"; +} +.icon-smile-o:before { + content: "\f118"; +} +.icon-frown-o:before { + content: "\f119"; +} +.icon-meh-o:before { + content: "\f11a"; +} +.icon-gamepad:before { + content: "\f11b"; +} +.icon-keyboard-o:before { + content: "\f11c"; +} +.icon-flag-o:before { + content: "\f11d"; +} +.icon-flag-checkered:before { + content: "\f11e"; +} +.icon-terminal:before { + content: "\f120"; +} +.icon-code:before { + content: "\f121"; +} +.icon-mail-reply-all:before { + content: "\f122"; +} +.icon-reply-all:before { + content: "\f122"; +} +.icon-star-half-empty:before { + content: "\f123"; +} +.icon-star-half-full:before { + content: "\f123"; +} +.icon-star-half-o:before { + content: "\f123"; +} +.icon-location-arrow:before { + content: "\f124"; +} +.icon-crop:before { + content: "\f125"; +} +.icon-code-fork:before { + content: "\f126"; +} +.icon-chain-broken:before { + content: "\f127"; +} +.icon-unlink:before { + content: "\f127"; +} +.icon-info:before { + content: "\f129"; +} +.icon-exclamation:before { + content: "\f12a"; +} +.icon-superscript:before { + content: "\f12b"; +} +.icon-subscript:before { + content: "\f12c"; +} +.icon-eraser:before { + content: "\f12d"; +} +.icon-puzzle-piece:before { + content: "\f12e"; +} +.icon-microphone:before { + content: "\f130"; +} +.icon-microphone-slash:before { + content: "\f131"; +} +.icon-shield:before { + content: "\f132"; +} +.icon-calendar-o:before { + content: "\f133"; +} +.icon-fire-extinguisher:before { + content: "\f134"; +} +.icon-rocket:before { + content: "\f135"; +} +.icon-maxcdn:before { + content: "\f136"; +} +.icon-chevron-circle-left:before { + content: "\f137"; +} +.icon-chevron-circle-right:before { + content: "\f138"; +} +.icon-chevron-circle-up:before { + content: "\f139"; +} +.icon-chevron-circle-down:before { + content: "\f13a"; +} +.icon-html5:before { + content: "\f13b"; +} +.icon-css3:before { + content: "\f13c"; +} +.icon-anchor:before { + content: "\f13d"; +} +.icon-unlock-alt:before { + content: "\f13e"; +} +.icon-bullseye:before { + content: "\f140"; +} +.icon-ellipsis-h:before { + content: "\f141"; +} +.icon-ellipsis-v:before { + content: "\f142"; +} +.icon-rss-square:before { + content: "\f143"; +} +.icon-play-circle:before { + content: "\f144"; +} +.icon-ticket:before { + content: "\f145"; +} +.icon-minus-square:before { + content: "\f146"; +} +.icon-minus-square-o:before { + content: "\f147"; +} +.icon-level-up:before { + content: "\f148"; +} +.icon-level-down:before { + content: "\f149"; +} +.icon-check-square:before { + content: "\f14a"; +} +.icon-pencil-square:before { + content: "\f14b"; +} +.icon-external-link-square:before { + content: "\f14c"; +} +.icon-share-square:before { + content: "\f14d"; +} +.icon-compass:before { + content: "\f14e"; +} +.icon-caret-square-o-down:before { + content: "\f150"; +} +.icon-toggle-down:before { + content: "\f150"; +} +.icon-caret-square-o-up:before { + content: "\f151"; +} +.icon-toggle-up:before { + content: "\f151"; +} +.icon-caret-square-o-right:before { + content: "\f152"; +} +.icon-toggle-right:before { + content: "\f152"; +} +.icon-eur:before { + content: "\f153"; +} +.icon-euro:before { + content: "\f153"; +} +.icon-gbp:before { + content: "\f154"; +} +.icon-dollar:before { + content: "\f155"; +} +.icon-usd:before { + content: "\f155"; +} +.icon-inr:before { + content: "\f156"; +} +.icon-rupee:before { + content: "\f156"; +} +.icon-cny:before { + content: "\f157"; +} +.icon-jpy:before { + content: "\f157"; +} +.icon-rmb:before { + content: "\f157"; +} +.icon-yen:before { + content: "\f157"; +} +.icon-rouble:before { + content: "\f158"; +} +.icon-rub:before { + content: "\f158"; +} +.icon-ruble:before { + content: "\f158"; +} +.icon-krw:before { + content: "\f159"; +} +.icon-won:before { + content: "\f159"; +} +.icon-bitcoin:before { + content: "\f15a"; +} +.icon-btc:before { + content: "\f15a"; +} +.icon-file:before { + content: "\f15b"; +} +.icon-file-text:before { + content: "\f15c"; +} +.icon-sort-alpha-asc:before { + content: "\f15d"; +} +.icon-sort-alpha-desc:before { + content: "\f15e"; +} +.icon-sort-amount-asc:before { + content: "\f160"; +} +.icon-sort-amount-desc:before { + content: "\f161"; +} +.icon-sort-numeric-asc:before { + content: "\f162"; +} +.icon-sort-numeric-desc:before { + content: "\f163"; +} +.icon-thumbs-up:before { + content: "\f164"; +} +.icon-thumbs-down:before { + content: "\f165"; +} +.icon-youtube-square:before { + content: "\f166"; +} +.icon-youtube:before { + content: "\f167"; +} +.icon-xing:before { + content: "\f168"; +} +.icon-xing-square:before { + content: "\f169"; +} +.icon-youtube-play:before { + content: "\f16a"; +} +.icon-dropbox:before { + content: "\f16b"; +} +.icon-stack-overflow:before { + content: "\f16c"; +} +.icon-instagram:before { + content: "\f16d"; +} +.icon-flickr:before { + content: "\f16e"; +} +.icon-adn:before { + content: "\f170"; +} +.icon-bitbucket:before { + content: "\f171"; +} +.icon-bitbucket-square:before { + content: "\f172"; +} +.icon-tumblr:before { + content: "\f173"; +} +.icon-tumblr-square:before { + content: "\f174"; +} +.icon-long-arrow-down:before { + content: "\f175"; +} +.icon-long-arrow-up:before { + content: "\f176"; +} +.icon-long-arrow-left:before { + content: "\f177"; +} +.icon-long-arrow-right:before { + content: "\f178"; +} +.icon-apple:before { + content: "\f179"; +} +.icon-windows:before { + content: "\f17a"; +} +.icon-android:before { + content: "\f17b"; +} +.icon-linux:before { + content: "\f17c"; +} +.icon-dribbble:before { + content: "\f17d"; +} +.icon-skype:before { + content: "\f17e"; +} +.icon-foursquare:before { + content: "\f180"; +} +.icon-trello:before { + content: "\f181"; +} +.icon-female:before { + content: "\f182"; +} +.icon-male:before { + content: "\f183"; +} +.icon-gittip:before { + content: "\f184"; +} +.icon-gratipay:before { + content: "\f184"; +} +.icon-sun-o:before { + content: "\f185"; +} +.icon-moon-o:before { + content: "\f186"; +} +.icon-archive:before { + content: "\f187"; +} +.icon-bug:before { + content: "\f188"; +} +.icon-vk:before { + content: "\f189"; +} +.icon-weibo:before { + content: "\f18a"; +} +.icon-renren:before { + content: "\f18b"; +} +.icon-pagelines:before { + content: "\f18c"; +} +.icon-stack-exchange:before { + content: "\f18d"; +} +.icon-arrow-circle-o-right:before { + content: "\f18e"; +} +.icon-arrow-circle-o-left:before { + content: "\f190"; +} +.icon-caret-square-o-left:before { + content: "\f191"; +} +.icon-toggle-left:before { + content: "\f191"; +} +.icon-dot-circle-o:before { + content: "\f192"; +} +.icon-wheelchair:before { + content: "\f193"; +} +.icon-vimeo-square:before { + content: "\f194"; +} +.icon-try:before { + content: "\f195"; +} +.icon-turkish-lira:before { + content: "\f195"; +} +.icon-plus-square-o:before { + content: "\f196"; +} +.icon-space-shuttle:before { + content: "\f197"; +} +.icon-slack:before { + content: "\f198"; +} +.icon-envelope-square:before { + content: "\f199"; +} +.icon-wordpress:before { + content: "\f19a"; +} +.icon-openid:before { + content: "\f19b"; +} +.icon-bank:before { + content: "\f19c"; +} +.icon-institution:before { + content: "\f19c"; +} +.icon-university:before { + content: "\f19c"; +} +.icon-graduation-cap:before { + content: "\f19d"; +} +.icon-mortar-board:before { + content: "\f19d"; +} +.icon-yahoo:before { + content: "\f19e"; +} +.icon-google:before { + content: "\f1a0"; +} +.icon-reddit:before { + content: "\f1a1"; +} +.icon-reddit-square:before { + content: "\f1a2"; +} +.icon-stumbleupon-circle:before { + content: "\f1a3"; +} +.icon-stumbleupon:before { + content: "\f1a4"; +} +.icon-delicious:before { + content: "\f1a5"; +} +.icon-digg:before { + content: "\f1a6"; +} +.icon-pied-piper-pp:before { + content: "\f1a7"; +} +.icon-pied-piper-alt:before { + content: "\f1a8"; +} +.icon-drupal:before { + content: "\f1a9"; +} +.icon-joomla:before { + content: "\f1aa"; +} +.icon-language:before { + content: "\f1ab"; +} +.icon-fax:before { + content: "\f1ac"; +} +.icon-building:before { + content: "\f1ad"; +} +.icon-child:before { + content: "\f1ae"; +} +.icon-paw:before { + content: "\f1b0"; +} +.icon-spoon:before { + content: "\f1b1"; +} +.icon-cube:before { + content: "\f1b2"; +} +.icon-cubes:before { + content: "\f1b3"; +} +.icon-behance:before { + content: "\f1b4"; +} +.icon-behance-square:before { + content: "\f1b5"; +} +.icon-steam:before { + content: "\f1b6"; +} +.icon-steam-square:before { + content: "\f1b7"; +} +.icon-recycle:before { + content: "\f1b8"; +} +.icon-automobile:before { + content: "\f1b9"; +} +.icon-car:before { + content: "\f1b9"; +} +.icon-cab:before { + content: "\f1ba"; +} +.icon-taxi:before { + content: "\f1ba"; +} +.icon-tree:before { + content: "\f1bb"; +} +.icon-spotify:before { + content: "\f1bc"; +} +.icon-deviantart:before { + content: "\f1bd"; +} +.icon-soundcloud:before { + content: "\f1be"; +} +.icon-database:before { + content: "\f1c0"; +} +.icon-file-pdf-o:before { + content: "\f1c1"; +} +.icon-file-word-o:before { + content: "\f1c2"; +} +.icon-file-excel-o:before { + content: "\f1c3"; +} +.icon-file-powerpoint-o:before { + content: "\f1c4"; +} +.icon-file-image-o:before { + content: "\f1c5"; +} +.icon-file-photo-o:before { + content: "\f1c5"; +} +.icon-file-picture-o:before { + content: "\f1c5"; +} +.icon-file-archive-o:before { + content: "\f1c6"; +} +.icon-file-zip-o:before { + content: "\f1c6"; +} +.icon-file-audio-o:before { + content: "\f1c7"; +} +.icon-file-sound-o:before { + content: "\f1c7"; +} +.icon-file-movie-o:before { + content: "\f1c8"; +} +.icon-file-video-o:before { + content: "\f1c8"; +} +.icon-file-code-o:before { + content: "\f1c9"; +} +.icon-vine:before { + content: "\f1ca"; +} +.icon-codepen:before { + content: "\f1cb"; +} +.icon-jsfiddle:before { + content: "\f1cc"; +} +.icon-life-bouy:before { + content: "\f1cd"; +} +.icon-life-buoy:before { + content: "\f1cd"; +} +.icon-life-ring:before { + content: "\f1cd"; +} +.icon-life-saver:before { + content: "\f1cd"; +} +.icon-support:before { + content: "\f1cd"; +} +.icon-circle-o-notch:before { + content: "\f1ce"; +} +.icon-ra:before { + content: "\f1d0"; +} +.icon-rebel:before { + content: "\f1d0"; +} +.icon-resistance:before { + content: "\f1d0"; +} +.icon-empire:before { + content: "\f1d1"; +} +.icon-ge:before { + content: "\f1d1"; +} +.icon-git-square:before { + content: "\f1d2"; +} +.icon-git:before { + content: "\f1d3"; +} +.icon-hacker-news:before { + content: "\f1d4"; +} +.icon-y-combinator-square:before { + content: "\f1d4"; +} +.icon-yc-square:before { + content: "\f1d4"; +} +.icon-tencent-weibo:before { + content: "\f1d5"; +} +.icon-qq:before { + content: "\f1d6"; +} +.icon-wechat:before { + content: "\f1d7"; +} +.icon-weixin:before { + content: "\f1d7"; +} +.icon-paper-plane:before { + content: "\f1d8"; +} +.icon-send:before { + content: "\f1d8"; +} +.icon-paper-plane-o:before { + content: "\f1d9"; +} +.icon-send-o:before { + content: "\f1d9"; +} +.icon-history:before { + content: "\f1da"; +} +.icon-circle-thin:before { + content: "\f1db"; +} +.icon-header:before { + content: "\f1dc"; +} +.icon-paragraph:before { + content: "\f1dd"; +} +.icon-sliders:before { + content: "\f1de"; +} +.icon-share-alt:before { + content: "\f1e0"; +} +.icon-share-alt-square:before { + content: "\f1e1"; +} +.icon-bomb:before { + content: "\f1e2"; +} +.icon-futbol-o:before { + content: "\f1e3"; +} +.icon-soccer-ball-o:before { + content: "\f1e3"; +} +.icon-tty:before { + content: "\f1e4"; +} +.icon-binoculars:before { + content: "\f1e5"; +} +.icon-plug:before { + content: "\f1e6"; +} +.icon-slideshare:before { + content: "\f1e7"; +} +.icon-twitch:before { + content: "\f1e8"; +} +.icon-yelp:before { + content: "\f1e9"; +} +.icon-newspaper-o:before { + content: "\f1ea"; +} +.icon-wifi:before { + content: "\f1eb"; +} +.icon-calculator:before { + content: "\f1ec"; +} +.icon-paypal:before { + content: "\f1ed"; +} +.icon-google-wallet:before { + content: "\f1ee"; +} +.icon-cc-visa:before { + content: "\f1f0"; +} +.icon-cc-mastercard:before { + content: "\f1f1"; +} +.icon-cc-discover:before { + content: "\f1f2"; +} +.icon-cc-amex:before { + content: "\f1f3"; +} +.icon-cc-paypal:before { + content: "\f1f4"; +} +.icon-cc-stripe:before { + content: "\f1f5"; +} +.icon-bell-slash:before { + content: "\f1f6"; +} +.icon-bell-slash-o:before { + content: "\f1f7"; +} +.icon-trash:before { + content: "\f1f8"; +} +.icon-copyright:before { + content: "\f1f9"; +} +.icon-at:before { + content: "\f1fa"; +} +.icon-eyedropper:before { + content: "\f1fb"; +} +.icon-paint-brush:before { + content: "\f1fc"; +} +.icon-birthday-cake:before { + content: "\f1fd"; +} +.icon-area-chart:before { + content: "\f1fe"; +} +.icon-pie-chart:before { + content: "\f200"; +} +.icon-line-chart:before { + content: "\f201"; +} +.icon-lastfm:before { + content: "\f202"; +} +.icon-lastfm-square:before { + content: "\f203"; +} +.icon-toggle-off:before { + content: "\f204"; +} +.icon-toggle-on:before { + content: "\f205"; +} +.icon-bicycle:before { + content: "\f206"; +} +.icon-bus:before { + content: "\f207"; +} +.icon-ioxhost:before { + content: "\f208"; +} +.icon-angellist:before { + content: "\f209"; +} +.icon-cc:before { + content: "\f20a"; +} +.icon-ils:before { + content: "\f20b"; +} +.icon-shekel:before { + content: "\f20b"; +} +.icon-sheqel:before { + content: "\f20b"; +} +.icon-meanpath:before { + content: "\f20c"; +} +.icon-buysellads:before { + content: "\f20d"; +} +.icon-connectdevelop:before { + content: "\f20e"; +} +.icon-dashcube:before { + content: "\f210"; +} +.icon-forumbee:before { + content: "\f211"; +} +.icon-leanpub:before { + content: "\f212"; +} +.icon-sellsy:before { + content: "\f213"; +} +.icon-shirtsinbulk:before { + content: "\f214"; +} +.icon-simplybuilt:before { + content: "\f215"; +} +.icon-skyatlas:before { + content: "\f216"; +} +.icon-cart-plus:before { + content: "\f217"; +} +.icon-cart-arrow-down:before { + content: "\f218"; +} +.icon-diamond:before { + content: "\f219"; +} +.icon-ship:before { + content: "\f21a"; +} +.icon-user-secret:before { + content: "\f21b"; +} +.icon-motorcycle:before { + content: "\f21c"; +} +.icon-street-view:before { + content: "\f21d"; +} +.icon-heartbeat:before { + content: "\f21e"; +} +.icon-venus:before { + content: "\f221"; +} +.icon-mars:before { + content: "\f222"; +} +.icon-mercury:before { + content: "\f223"; +} +.icon-intersex:before { + content: "\f224"; +} +.icon-transgender:before { + content: "\f224"; +} +.icon-transgender-alt:before { + content: "\f225"; +} +.icon-venus-double:before { + content: "\f226"; +} +.icon-mars-double:before { + content: "\f227"; +} +.icon-venus-mars:before { + content: "\f228"; +} +.icon-mars-stroke:before { + content: "\f229"; +} +.icon-mars-stroke-v:before { + content: "\f22a"; +} +.icon-mars-stroke-h:before { + content: "\f22b"; +} +.icon-neuter:before { + content: "\f22c"; +} +.icon-genderless:before { + content: "\f22d"; +} +.icon-facebook-official:before { + content: "\f230"; +} +.icon-pinterest-p:before { + content: "\f231"; +} +.icon-whatsapp:before { + content: "\f232"; +} +.icon-server:before { + content: "\f233"; +} +.icon-user-plus:before { + content: "\f234"; +} +.icon-user-times:before { + content: "\f235"; +} +.icon-bed:before { + content: "\f236"; +} +.icon-hotel:before { + content: "\f236"; +} +.icon-viacoin:before { + content: "\f237"; +} +.icon-train:before { + content: "\f238"; +} +.icon-subway:before { + content: "\f239"; +} +.icon-medium:before { + content: "\f23a"; +} +.icon-y-combinator:before { + content: "\f23b"; +} +.icon-yc:before { + content: "\f23b"; +} +.icon-optin-monster:before { + content: "\f23c"; +} +.icon-opencart:before { + content: "\f23d"; +} +.icon-expeditedssl:before { + content: "\f23e"; +} +.icon-battery:before { + content: "\f240"; +} +.icon-battery-4:before { + content: "\f240"; +} +.icon-battery-full:before { + content: "\f240"; +} +.icon-battery-3:before { + content: "\f241"; +} +.icon-battery-three-quarters:before { + content: "\f241"; +} +.icon-battery-2:before { + content: "\f242"; +} +.icon-battery-half:before { + content: "\f242"; +} +.icon-battery-1:before { + content: "\f243"; +} +.icon-battery-quarter:before { + content: "\f243"; +} +.icon-battery-0:before { + content: "\f244"; +} +.icon-battery-empty:before { + content: "\f244"; +} +.icon-mouse-pointer:before { + content: "\f245"; +} +.icon-i-cursor:before { + content: "\f246"; +} +.icon-object-group:before { + content: "\f247"; +} +.icon-object-ungroup:before { + content: "\f248"; +} +.icon-sticky-note:before { + content: "\f249"; +} +.icon-sticky-note-o:before { + content: "\f24a"; +} +.icon-cc-jcb:before { + content: "\f24b"; +} +.icon-cc-diners-club:before { + content: "\f24c"; +} +.icon-clone:before { + content: "\f24d"; +} +.icon-balance-scale:before { + content: "\f24e"; +} +.icon-hourglass-o:before { + content: "\f250"; +} +.icon-hourglass-1:before { + content: "\f251"; +} +.icon-hourglass-start:before { + content: "\f251"; +} +.icon-hourglass-2:before { + content: "\f252"; +} +.icon-hourglass-half:before { + content: "\f252"; +} +.icon-hourglass-3:before { + content: "\f253"; +} +.icon-hourglass-end:before { + content: "\f253"; +} +.icon-hourglass:before { + content: "\f254"; +} +.icon-hand-grab-o:before { + content: "\f255"; +} +.icon-hand-rock-o:before { + content: "\f255"; +} +.icon-hand-paper-o:before { + content: "\f256"; +} +.icon-hand-stop-o:before { + content: "\f256"; +} +.icon-hand-scissors-o:before { + content: "\f257"; +} +.icon-hand-lizard-o:before { + content: "\f258"; +} +.icon-hand-spock-o:before { + content: "\f259"; +} +.icon-hand-pointer-o:before { + content: "\f25a"; +} +.icon-hand-peace-o:before { + content: "\f25b"; +} +.icon-trademark:before { + content: "\f25c"; +} +.icon-registered:before { + content: "\f25d"; +} +.icon-creative-commons:before { + content: "\f25e"; +} +.icon-gg:before { + content: "\f260"; +} +.icon-gg-circle:before { + content: "\f261"; +} +.icon-tripadvisor:before { + content: "\f262"; +} +.icon-odnoklassniki:before { + content: "\f263"; +} +.icon-odnoklassniki-square:before { + content: "\f264"; +} +.icon-get-pocket:before { + content: "\f265"; +} +.icon-wikipedia-w:before { + content: "\f266"; +} +.icon-safari:before { + content: "\f267"; +} +.icon-chrome:before { + content: "\f268"; +} +.icon-firefox:before { + content: "\f269"; +} +.icon-opera:before { + content: "\f26a"; +} +.icon-internet-explorer:before { + content: "\f26b"; +} +.icon-television:before { + content: "\f26c"; +} +.icon-tv:before { + content: "\f26c"; +} +.icon-contao:before { + content: "\f26d"; +} +.icon-500px:before { + content: "\f26e"; +} +.icon-amazon:before { + content: "\f270"; +} +.icon-calendar-plus-o:before { + content: "\f271"; +} +.icon-calendar-minus-o:before { + content: "\f272"; +} +.icon-calendar-times-o:before { + content: "\f273"; +} +.icon-calendar-check-o:before { + content: "\f274"; +} +.icon-industry:before { + content: "\f275"; +} +.icon-map-pin:before { + content: "\f276"; +} +.icon-map-signs:before { + content: "\f277"; +} +.icon-map-o:before { + content: "\f278"; +} +.icon-map:before { + content: "\f279"; +} +.icon-commenting:before { + content: "\f27a"; +} +.icon-commenting-o:before { + content: "\f27b"; +} +.icon-houzz:before { + content: "\f27c"; +} +.icon-vimeo:before { + content: "\f27d"; +} +.icon-black-tie:before { + content: "\f27e"; +} +.icon-fonticons:before { + content: "\f280"; +} +.icon-reddit-alien:before { + content: "\f281"; +} +.icon-edge:before { + content: "\f282"; +} +.icon-credit-card-alt:before { + content: "\f283"; +} +.icon-codiepie:before { + content: "\f284"; +} +.icon-modx:before { + content: "\f285"; +} +.icon-fort-awesome:before { + content: "\f286"; +} +.icon-usb:before { + content: "\f287"; +} +.icon-product-hunt:before { + content: "\f288"; +} +.icon-mixcloud:before { + content: "\f289"; +} +.icon-scribd:before { + content: "\f28a"; +} +.icon-pause-circle:before { + content: "\f28b"; +} +.icon-pause-circle-o:before { + content: "\f28c"; +} +.icon-stop-circle:before { + content: "\f28d"; +} +.icon-stop-circle-o:before { + content: "\f28e"; +} +.icon-shopping-bag:before { + content: "\f290"; +} +.icon-shopping-basket:before { + content: "\f291"; +} +.icon-hashtag:before { + content: "\f292"; +} +.icon-bluetooth:before { + content: "\f293"; +} +.icon-bluetooth-b:before { + content: "\f294"; +} +.icon-percent:before { + content: "\f295"; +} +.icon-gitlab:before { + content: "\f296"; +} +.icon-wpbeginner:before { + content: "\f297"; +} +.icon-wpforms:before { + content: "\f298"; +} +.icon-envira:before { + content: "\f299"; +} +.icon-universal-access:before { + content: "\f29a"; +} +.icon-wheelchair-alt:before { + content: "\f29b"; +} +.icon-question-circle-o:before { + content: "\f29c"; +} +.icon-blind:before { + content: "\f29d"; +} +.icon-audio-description:before { + content: "\f29e"; +} +.icon-volume-control-phone:before { + content: "\f2a0"; +} +.icon-braille:before { + content: "\f2a1"; +} +.icon-assistive-listening-systems:before { + content: "\f2a2"; +} +.icon-american-sign-language-interpreting:before { + content: "\f2a3"; +} +.icon-asl-interpreting:before { + content: "\f2a3"; +} +.icon-deaf:before { + content: "\f2a4"; +} +.icon-deafness:before { + content: "\f2a4"; +} +.icon-hard-of-hearing:before { + content: "\f2a4"; +} +.icon-glide:before { + content: "\f2a5"; +} +.icon-glide-g:before { + content: "\f2a6"; +} +.icon-sign-language:before { + content: "\f2a7"; +} +.icon-signing:before { + content: "\f2a7"; +} +.icon-low-vision:before { + content: "\f2a8"; +} +.icon-viadeo:before { + content: "\f2a9"; +} +.icon-viadeo-square:before { + content: "\f2aa"; +} +.icon-snapchat:before { + content: "\f2ab"; +} +.icon-snapchat-ghost:before { + content: "\f2ac"; +} +.icon-snapchat-square:before { + content: "\f2ad"; +} +.icon-pied-piper:before { + content: "\f2ae"; +} +.icon-first-order:before { + content: "\f2b0"; +} +.icon-yoast:before { + content: "\f2b1"; +} +.icon-themeisle:before { + content: "\f2b2"; +} +.icon-google-plus-circle:before { + content: "\f2b3"; +} +.icon-google-plus-official:before { + content: "\f2b3"; +} +.icon-fa:before { + content: "\f2b4"; +} +.icon-font-awesome:before { + content: "\f2b4"; +} +.icon-handshake-o:before { + content: "\f2b5"; +} +.icon-envelope-open:before { + content: "\f2b6"; +} +.icon-envelope-open-o:before { + content: "\f2b7"; +} +.icon-linode:before { + content: "\f2b8"; +} +.icon-address-book:before { + content: "\f2b9"; +} +.icon-address-book-o:before { + content: "\f2ba"; +} +.icon-address-card:before { + content: "\f2bb"; +} +.icon-vcard:before { + content: "\f2bb"; +} +.icon-address-card-o:before { + content: "\f2bc"; +} +.icon-vcard-o:before { + content: "\f2bc"; +} +.icon-user-circle:before { + content: "\f2bd"; +} +.icon-user-circle-o:before { + content: "\f2be"; +} +.icon-user-o:before { + content: "\f2c0"; +} +.icon-id-badge:before { + content: "\f2c1"; +} +.icon-drivers-license:before { + content: "\f2c2"; +} +.icon-id-card:before { + content: "\f2c2"; +} +.icon-drivers-license-o:before { + content: "\f2c3"; +} +.icon-id-card-o:before { + content: "\f2c3"; +} +.icon-quora:before { + content: "\f2c4"; +} +.icon-free-code-camp:before { + content: "\f2c5"; +} +.icon-telegram:before { + content: "\f2c6"; +} +.icon-thermometer:before { + content: "\f2c7"; +} +.icon-thermometer-4:before { + content: "\f2c7"; +} +.icon-thermometer-full:before { + content: "\f2c7"; +} +.icon-thermometer-3:before { + content: "\f2c8"; +} +.icon-thermometer-three-quarters:before { + content: "\f2c8"; +} +.icon-thermometer-2:before { + content: "\f2c9"; +} +.icon-thermometer-half:before { + content: "\f2c9"; +} +.icon-thermometer-1:before { + content: "\f2ca"; +} +.icon-thermometer-quarter:before { + content: "\f2ca"; +} +.icon-thermometer-0:before { + content: "\f2cb"; +} +.icon-thermometer-empty:before { + content: "\f2cb"; +} +.icon-shower:before { + content: "\f2cc"; +} +.icon-bath:before { + content: "\f2cd"; +} +.icon-bathtub:before { + content: "\f2cd"; +} +.icon-s15:before { + content: "\f2cd"; +} +.icon-podcast:before { + content: "\f2ce"; +} +.icon-window-maximize:before { + content: "\f2d0"; +} +.icon-window-minimize:before { + content: "\f2d1"; +} +.icon-window-restore:before { + content: "\f2d2"; +} +.icon-times-rectangle:before { + content: "\f2d3"; +} +.icon-window-close:before { + content: "\f2d3"; +} +.icon-times-rectangle-o:before { + content: "\f2d4"; +} +.icon-window-close-o:before { + content: "\f2d4"; +} +.icon-bandcamp:before { + content: "\f2d5"; +} +.icon-grav:before { + content: "\f2d6"; +} +.icon-etsy:before { + content: "\f2d7"; +} +.icon-imdb:before { + content: "\f2d8"; +} +.icon-ravelry:before { + content: "\f2d9"; +} +.icon-eercast:before { + content: "\f2da"; +} +.icon-microchip:before { + content: "\f2db"; +} +.icon-snowflake-o:before { + content: "\f2dc"; +} +.icon-superpowers:before { + content: "\f2dd"; +} +.icon-wpexplorer:before { + content: "\f2de"; +} +.icon-meetup:before { + content: "\f2e0"; +} +.icon-3d_rotation:before { + content: "\e84d"; +} +.icon-ac_unit:before { + content: "\eb3b"; +} +.icon-alarm:before { + content: "\e855"; +} +.icon-access_alarms:before { + content: "\e191"; +} +.icon-schedule:before { + content: "\e8b5"; +} +.icon-accessibility:before { + content: "\e84e"; +} +.icon-accessible:before { + content: "\e914"; +} +.icon-account_balance:before { + content: "\e84f"; +} +.icon-account_balance_wallet:before { + content: "\e850"; +} +.icon-account_box:before { + content: "\e851"; +} +.icon-account_circle:before { + content: "\e853"; +} +.icon-adb:before { + content: "\e60e"; +} +.icon-add:before { + content: "\e145"; +} +.icon-add_a_photo:before { + content: "\e439"; +} +.icon-alarm_add:before { + content: "\e856"; +} +.icon-add_alert:before { + content: "\e003"; +} +.icon-add_box:before { + content: "\e146"; +} +.icon-add_circle:before { + content: "\e147"; +} +.icon-control_point:before { + content: "\e3ba"; +} +.icon-add_location:before { + content: "\e567"; +} +.icon-add_shopping_cart:before { + content: "\e854"; +} +.icon-queue:before { + content: "\e03c"; +} +.icon-add_to_queue:before { + content: "\e05c"; +} +.icon-adjust2:before { + content: "\e39e"; +} +.icon-airline_seat_flat:before { + content: "\e630"; +} +.icon-airline_seat_flat_angled:before { + content: "\e631"; +} +.icon-airline_seat_individual_suite:before { + content: "\e632"; +} +.icon-airline_seat_legroom_extra:before { + content: "\e633"; +} +.icon-airline_seat_legroom_normal:before { + content: "\e634"; +} +.icon-airline_seat_legroom_reduced:before { + content: "\e635"; +} +.icon-airline_seat_recline_extra:before { + content: "\e636"; +} +.icon-airline_seat_recline_normal:before { + content: "\e637"; +} +.icon-flight:before { + content: "\e539"; +} +.icon-airplanemode_inactive:before { + content: "\e194"; +} +.icon-airplay:before { + content: "\e055"; +} +.icon-airport_shuttle:before { + content: "\eb3c"; +} +.icon-alarm_off:before { + content: "\e857"; +} +.icon-alarm_on:before { + content: "\e858"; +} +.icon-album:before { + content: "\e019"; +} +.icon-all_inclusive:before { + content: "\eb3d"; +} +.icon-all_out:before { + content: "\e90b"; +} +.icon-android2:before { + content: "\e859"; +} +.icon-announcement:before { + content: "\e85a"; +} +.icon-apps:before { + content: "\e5c3"; +} +.icon-archive2:before { + content: "\e149"; +} +.icon-arrow_back:before { + content: "\e5c4"; +} +.icon-arrow_downward:before { + content: "\e5db"; +} +.icon-arrow_drop_down:before { + content: "\e5c5"; +} +.icon-arrow_drop_down_circle:before { + content: "\e5c6"; +} +.icon-arrow_drop_up:before { + content: "\e5c7"; +} +.icon-arrow_forward:before { + content: "\e5c8"; +} +.icon-arrow_upward:before { + content: "\e5d8"; +} +.icon-art_track:before { + content: "\e060"; +} +.icon-aspect_ratio:before { + content: "\e85b"; +} +.icon-poll:before { + content: "\e801"; +} +.icon-assignment:before { + content: "\e85d"; +} +.icon-assignment_ind:before { + content: "\e85e"; +} +.icon-assignment_late:before { + content: "\e85f"; +} +.icon-assignment_return:before { + content: "\e860"; +} +.icon-assignment_returned:before { + content: "\e861"; +} +.icon-assignment_turned_in:before { + content: "\e862"; +} +.icon-assistant:before { + content: "\e39f"; +} +.icon-flag2:before { + content: "\e153"; +} +.icon-attach_file:before { + content: "\e226"; +} +.icon-attach_money:before { + content: "\e227"; +} +.icon-attachment:before { + content: "\e2bc"; +} +.icon-audiotrack:before { + content: "\e3a1"; +} +.icon-autorenew:before { + content: "\e863"; +} +.icon-av_timer:before { + content: "\e01b"; +} +.icon-backspace:before { + content: "\e14a"; +} +.icon-cloud_upload:before { + content: "\e2c3"; +} +.icon-battery_alert:before { + content: "\e19c"; +} +.icon-battery_charging_full:before { + content: "\e1a3"; +} +.icon-battery_std:before { + content: "\e1a5"; +} +.icon-battery_unknown:before { + content: "\e1a6"; +} +.icon-beach_access:before { + content: "\eb3e"; +} +.icon-beenhere:before { + content: "\e52d"; +} +.icon-block:before { + content: "\e14b"; +} +.icon-bluetooth2:before { + content: "\e1a7"; +} +.icon-bluetooth_searching:before { + content: "\e1aa"; +} +.icon-bluetooth_connected:before { + content: "\e1a8"; +} +.icon-bluetooth_disabled:before { + content: "\e1a9"; +} +.icon-blur_circular:before { + content: "\e3a2"; +} +.icon-blur_linear:before { + content: "\e3a3"; +} +.icon-blur_off:before { + content: "\e3a4"; +} +.icon-blur_on:before { + content: "\e3a5"; +} +.icon-class:before { + content: "\e86e"; +} +.icon-turned_in:before { + content: "\e8e6"; +} +.icon-turned_in_not:before { + content: "\e8e7"; +} +.icon-border_all:before { + content: "\e228"; +} +.icon-border_bottom:before { + content: "\e229"; +} +.icon-border_clear:before { + content: "\e22a"; +} +.icon-border_color:before { + content: "\e22b"; +} +.icon-border_horizontal:before { + content: "\e22c"; +} +.icon-border_inner:before { + content: "\e22d"; +} +.icon-border_left:before { + content: "\e22e"; +} +.icon-border_outer:before { + content: "\e22f"; +} +.icon-border_right:before { + content: "\e230"; +} +.icon-border_style:before { + content: "\e231"; +} +.icon-border_top:before { + content: "\e232"; +} +.icon-border_vertical:before { + content: "\e233"; +} +.icon-branding_watermark:before { + content: "\e06b"; +} +.icon-brightness_1:before { + content: "\e3a6"; +} +.icon-brightness_2:before { + content: "\e3a7"; +} +.icon-brightness_3:before { + content: "\e3a8"; +} +.icon-brightness_4:before { + content: "\e3a9"; +} +.icon-brightness_low:before { + content: "\e1ad"; +} +.icon-brightness_medium:before { + content: "\e1ae"; +} +.icon-brightness_high:before { + content: "\e1ac"; +} +.icon-brightness_auto:before { + content: "\e1ab"; +} +.icon-broken_image:before { + content: "\e3ad"; +} +.icon-brush:before { + content: "\e3ae"; +} +.icon-bubble_chart:before { + content: "\e6dd"; +} +.icon-bug_report:before { + content: "\e868"; +} +.icon-build:before { + content: "\e869"; +} +.icon-burst_mode:before { + content: "\e43c"; +} +.icon-domain:before { + content: "\e7ee"; +} +.icon-business_center:before { + content: "\eb3f"; +} +.icon-cached:before { + content: "\e86a"; +} +.icon-cake:before { + content: "\e7e9"; +} +.icon-phone2:before { + content: "\e0cd"; +} +.icon-call_end:before { + content: "\e0b1"; +} +.icon-call_made:before { + content: "\e0b2"; +} +.icon-merge_type:before { + content: "\e252"; +} +.icon-call_missed:before { + content: "\e0b4"; +} +.icon-call_missed_outgoing:before { + content: "\e0e4"; +} +.icon-call_received:before { + content: "\e0b5"; +} +.icon-call_split:before { + content: "\e0b6"; +} +.icon-call_to_action:before { + content: "\e06c"; +} +.icon-camera2:before { + content: "\e3af"; +} +.icon-photo_camera:before { + content: "\e412"; +} +.icon-camera_enhance:before { + content: "\e8fc"; +} +.icon-camera_front:before { + content: "\e3b1"; +} +.icon-camera_rear:before { + content: "\e3b2"; +} +.icon-camera_roll:before { + content: "\e3b3"; +} +.icon-cancel:before { + content: "\e5c9"; +} +.icon-redeem:before { + content: "\e8b1"; +} +.icon-card_membership:before { + content: "\e8f7"; +} +.icon-card_travel:before { + content: "\e8f8"; +} +.icon-casino:before { + content: "\eb40"; +} +.icon-cast:before { + content: "\e307"; +} +.icon-cast_connected:before { + content: "\e308"; +} +.icon-center_focus_strong:before { + content: "\e3b4"; +} +.icon-center_focus_weak:before { + content: "\e3b5"; +} +.icon-change_history:before { + content: "\e86b"; +} +.icon-chat:before { + content: "\e0b7"; +} +.icon-chat_bubble:before { + content: "\e0ca"; +} +.icon-chat_bubble_outline:before { + content: "\e0cb"; +} +.icon-check2:before { + content: "\e5ca"; +} +.icon-check_box:before { + content: "\e834"; +} +.icon-check_box_outline_blank:before { + content: "\e835"; +} +.icon-check_circle:before { + content: "\e86c"; +} +.icon-navigate_before:before { + content: "\e408"; +} +.icon-navigate_next:before { + content: "\e409"; +} +.icon-child_care:before { + content: "\eb41"; +} +.icon-child_friendly:before { + content: "\eb42"; +} +.icon-chrome_reader_mode:before { + content: "\e86d"; +} +.icon-close2:before { + content: "\e5cd"; +} +.icon-clear_all:before { + content: "\e0b8"; +} +.icon-closed_caption:before { + content: "\e01c"; +} +.icon-wb_cloudy:before { + content: "\e42d"; +} +.icon-cloud_circle:before { + content: "\e2be"; +} +.icon-cloud_done:before { + content: "\e2bf"; +} +.icon-cloud_download:before { + content: "\e2c0"; +} +.icon-cloud_off:before { + content: "\e2c1"; +} +.icon-cloud_queue:before { + content: "\e2c2"; +} +.icon-code2:before { + content: "\e86f"; +} +.icon-photo_library:before { + content: "\e413"; +} +.icon-collections_bookmark:before { + content: "\e431"; +} +.icon-palette:before { + content: "\e40a"; +} +.icon-colorize:before { + content: "\e3b8"; +} +.icon-comment2:before { + content: "\e0b9"; +} +.icon-compare:before { + content: "\e3b9"; +} +.icon-compare_arrows:before { + content: "\e915"; +} +.icon-laptop2:before { + content: "\e31e"; +} +.icon-confirmation_number:before { + content: "\e638"; +} +.icon-contact_mail:before { + content: "\e0d0"; +} +.icon-contact_phone:before { + content: "\e0cf"; +} +.icon-contacts:before { + content: "\e0ba"; +} +.icon-content_copy:before { + content: "\e14d"; +} +.icon-content_cut:before { + content: "\e14e"; +} +.icon-content_paste:before { + content: "\e14f"; +} +.icon-control_point_duplicate:before { + content: "\e3bb"; +} +.icon-copyright2:before { + content: "\e90c"; +} +.icon-mode_edit:before { + content: "\e254"; +} +.icon-create_new_folder:before { + content: "\e2cc"; +} +.icon-payment:before { + content: "\e8a1"; +} +.icon-crop2:before { + content: "\e3be"; +} +.icon-crop_16_9:before { + content: "\e3bc"; +} +.icon-crop_3_2:before { + content: "\e3bd"; +} +.icon-crop_landscape:before { + content: "\e3c3"; +} +.icon-crop_7_5:before { + content: "\e3c0"; +} +.icon-crop_din:before { + content: "\e3c1"; +} +.icon-crop_free:before { + content: "\e3c2"; +} +.icon-crop_original:before { + content: "\e3c4"; +} +.icon-crop_portrait:before { + content: "\e3c5"; +} +.icon-crop_rotate:before { + content: "\e437"; +} +.icon-crop_square:before { + content: "\e3c6"; +} +.icon-dashboard2:before { + content: "\e871"; +} +.icon-data_usage:before { + content: "\e1af"; +} +.icon-date_range:before { + content: "\e916"; +} +.icon-dehaze:before { + content: "\e3c7"; +} +.icon-delete:before { + content: "\e872"; +} +.icon-delete_forever:before { + content: "\e92b"; +} +.icon-delete_sweep:before { + content: "\e16c"; +} +.icon-description:before { + content: "\e873"; +} +.icon-desktop_mac:before { + content: "\e30b"; +} +.icon-desktop_windows:before { + content: "\e30c"; +} +.icon-details:before { + content: "\e3c8"; +} +.icon-developer_board:before { + content: "\e30d"; +} +.icon-developer_mode:before { + content: "\e1b0"; +} +.icon-device_hub:before { + content: "\e335"; +} +.icon-phonelink:before { + content: "\e326"; +} +.icon-devices_other:before { + content: "\e337"; +} +.icon-dialer_sip:before { + content: "\e0bb"; +} +.icon-dialpad:before { + content: "\e0bc"; +} +.icon-directions:before { + content: "\e52e"; +} +.icon-directions_bike:before { + content: "\e52f"; +} +.icon-directions_boat:before { + content: "\e532"; +} +.icon-directions_bus:before { + content: "\e530"; +} +.icon-directions_car:before { + content: "\e531"; +} +.icon-directions_railway:before { + content: "\e534"; +} +.icon-directions_run:before { + content: "\e566"; +} +.icon-directions_transit:before { + content: "\e535"; +} +.icon-directions_walk:before { + content: "\e536"; +} +.icon-disc_full:before { + content: "\e610"; +} +.icon-dns:before { + content: "\e875"; +} +.icon-not_interested:before { + content: "\e033"; +} +.icon-do_not_disturb_alt:before { + content: "\e611"; +} +.icon-do_not_disturb_off:before { + content: "\e643"; +} +.icon-remove_circle:before { + content: "\e15c"; +} +.icon-dock:before { + content: "\e30e"; +} +.icon-done:before { + content: "\e876"; +} +.icon-done_all:before { + content: "\e877"; +} +.icon-donut_large:before { + content: "\e917"; +} +.icon-donut_small:before { + content: "\e918"; +} +.icon-drafts:before { + content: "\e151"; +} +.icon-drag_handle:before { + content: "\e25d"; +} +.icon-time_to_leave:before { + content: "\e62c"; +} +.icon-dvr:before { + content: "\e1b2"; +} +.icon-edit_location:before { + content: "\e568"; +} +.icon-eject2:before { + content: "\e8fb"; +} +.icon-markunread:before { + content: "\e159"; +} +.icon-enhanced_encryption:before { + content: "\e63f"; +} +.icon-equalizer:before { + content: "\e01d"; +} +.icon-error:before { + content: "\e000"; +} +.icon-error_outline:before { + content: "\e001"; +} +.icon-euro_symbol:before { + content: "\e926"; +} +.icon-ev_station:before { + content: "\e56d"; +} +.icon-insert_invitation:before { + content: "\e24f"; +} +.icon-event_available:before { + content: "\e614"; +} +.icon-event_busy:before { + content: "\e615"; +} +.icon-event_note:before { + content: "\e616"; +} +.icon-event_seat:before { + content: "\e903"; +} +.icon-exit_to_app:before { + content: "\e879"; +} +.icon-expand_less:before { + content: "\e5ce"; +} +.icon-expand_more:before { + content: "\e5cf"; +} +.icon-explicit:before { + content: "\e01e"; +} +.icon-explore:before { + content: "\e87a"; +} +.icon-exposure:before { + content: "\e3ca"; +} +.icon-exposure_neg_1:before { + content: "\e3cb"; +} +.icon-exposure_neg_2:before { + content: "\e3cc"; +} +.icon-exposure_plus_1:before { + content: "\e3cd"; +} +.icon-exposure_plus_2:before { + content: "\e3ce"; +} +.icon-exposure_zero:before { + content: "\e3cf"; +} +.icon-extension:before { + content: "\e87b"; +} +.icon-face:before { + content: "\e87c"; +} +.icon-fast_forward:before { + content: "\e01f"; +} +.icon-fast_rewind:before { + content: "\e020"; +} +.icon-favorite:before { + content: "\e87d"; +} +.icon-favorite_border:before { + content: "\e87e"; +} +.icon-featured_play_list:before { + content: "\e06d"; +} +.icon-featured_video:before { + content: "\e06e"; +} +.icon-sms_failed:before { + content: "\e626"; +} +.icon-fiber_dvr:before { + content: "\e05d"; +} +.icon-fiber_manual_record:before { + content: "\e061"; +} +.icon-fiber_new:before { + content: "\e05e"; +} +.icon-fiber_pin:before { + content: "\e06a"; +} +.icon-fiber_smart_record:before { + content: "\e062"; +} +.icon-get_app:before { + content: "\e884"; +} +.icon-file_upload:before { + content: "\e2c6"; +} +.icon-filter2:before { + content: "\e3d3"; +} +.icon-filter_1:before { + content: "\e3d0"; +} +.icon-filter_2:before { + content: "\e3d1"; +} +.icon-filter_3:before { + content: "\e3d2"; +} +.icon-filter_4:before { + content: "\e3d4"; +} +.icon-filter_5:before { + content: "\e3d5"; +} +.icon-filter_6:before { + content: "\e3d6"; +} +.icon-filter_7:before { + content: "\e3d7"; +} +.icon-filter_8:before { + content: "\e3d8"; +} +.icon-filter_9:before { + content: "\e3d9"; +} +.icon-filter_9_plus:before { + content: "\e3da"; +} +.icon-filter_b_and_w:before { + content: "\e3db"; +} +.icon-filter_center_focus:before { + content: "\e3dc"; +} +.icon-filter_drama:before { + content: "\e3dd"; +} +.icon-filter_frames:before { + content: "\e3de"; +} +.icon-terrain:before { + content: "\e564"; +} +.icon-filter_list:before { + content: "\e152"; +} +.icon-filter_none:before { + content: "\e3e0"; +} +.icon-filter_tilt_shift:before { + content: "\e3e2"; +} +.icon-filter_vintage:before { + content: "\e3e3"; +} +.icon-find_in_page:before { + content: "\e880"; +} +.icon-find_replace:before { + content: "\e881"; +} +.icon-fingerprint:before { + content: "\e90d"; +} +.icon-first_page:before { + content: "\e5dc"; +} +.icon-fitness_center:before { + content: "\eb43"; +} +.icon-flare:before { + content: "\e3e4"; +} +.icon-flash_auto:before { + content: "\e3e5"; +} +.icon-flash_off:before { + content: "\e3e6"; +} +.icon-flash_on:before { + content: "\e3e7"; +} +.icon-flight_land:before { + content: "\e904"; +} +.icon-flight_takeoff:before { + content: "\e905"; +} +.icon-flip:before { + content: "\e3e8"; +} +.icon-flip_to_back:before { + content: "\e882"; +} +.icon-flip_to_front:before { + content: "\e883"; +} +.icon-folder2:before { + content: "\e2c7"; +} +.icon-folder_open:before { + content: "\e2c8"; +} +.icon-folder_shared:before { + content: "\e2c9"; +} +.icon-folder_special:before { + content: "\e617"; +} +.icon-font_download:before { + content: "\e167"; +} +.icon-format_align_center:before { + content: "\e234"; +} +.icon-format_align_justify:before { + content: "\e235"; +} +.icon-format_align_left:before { + content: "\e236"; +} +.icon-format_align_right:before { + content: "\e237"; +} +.icon-format_bold:before { + content: "\e238"; +} +.icon-format_clear:before { + content: "\e239"; +} +.icon-format_color_fill:before { + content: "\e23a"; +} +.icon-format_color_reset:before { + content: "\e23b"; +} +.icon-format_color_text:before { + content: "\e23c"; +} +.icon-format_indent_decrease:before { + content: "\e23d"; +} +.icon-format_indent_increase:before { + content: "\e23e"; +} +.icon-format_italic:before { + content: "\e23f"; +} +.icon-format_line_spacing:before { + content: "\e240"; +} +.icon-format_list_bulleted:before { + content: "\e241"; +} +.icon-format_list_numbered:before { + content: "\e242"; +} +.icon-format_paint:before { + content: "\e243"; +} +.icon-format_quote:before { + content: "\e244"; +} +.icon-format_shapes:before { + content: "\e25e"; +} +.icon-format_size:before { + content: "\e245"; +} +.icon-format_strikethrough:before { + content: "\e246"; +} +.icon-format_textdirection_l_to_r:before { + content: "\e247"; +} +.icon-format_textdirection_r_to_l:before { + content: "\e248"; +} +.icon-format_underlined:before { + content: "\e249"; +} +.icon-question_answer:before { + content: "\e8af"; +} +.icon-forward2:before { + content: "\e154"; +} +.icon-forward_10:before { + content: "\e056"; +} +.icon-forward_30:before { + content: "\e057"; +} +.icon-forward_5:before { + content: "\e058"; +} +.icon-free_breakfast:before { + content: "\eb44"; +} +.icon-fullscreen:before { + content: "\e5d0"; +} +.icon-fullscreen_exit:before { + content: "\e5d1"; +} +.icon-functions:before { + content: "\e24a"; +} +.icon-g_translate:before { + content: "\e927"; +} +.icon-games:before { + content: "\e021"; +} +.icon-gavel2:before { + content: "\e90e"; +} +.icon-gesture:before { + content: "\e155"; +} +.icon-gif:before { + content: "\e908"; +} +.icon-goat:before { + content: "\e900"; +} +.icon-golf_course:before { + content: "\eb45"; +} +.icon-my_location:before { + content: "\e55c"; +} +.icon-location_searching:before { + content: "\e1b7"; +} +.icon-location_disabled:before { + content: "\e1b6"; +} +.icon-star2:before { + content: "\e838"; +} +.icon-gradient:before { + content: "\e3e9"; +} +.icon-grain:before { + content: "\e3ea"; +} +.icon-graphic_eq:before { + content: "\e1b8"; +} +.icon-grid_off:before { + content: "\e3eb"; +} +.icon-grid_on:before { + content: "\e3ec"; +} +.icon-people:before { + content: "\e7fb"; +} +.icon-group_add:before { + content: "\e7f0"; +} +.icon-group_work:before { + content: "\e886"; +} +.icon-hd:before { + content: "\e052"; +} +.icon-hdr_off:before { + content: "\e3ed"; +} +.icon-hdr_on:before { + content: "\e3ee"; +} +.icon-hdr_strong:before { + content: "\e3f1"; +} +.icon-hdr_weak:before { + content: "\e3f2"; +} +.icon-headset:before { + content: "\e310"; +} +.icon-headset_mic:before { + content: "\e311"; +} +.icon-healing:before { + content: "\e3f3"; +} +.icon-hearing:before { + content: "\e023"; +} +.icon-help:before { + content: "\e887"; +} +.icon-help_outline:before { + content: "\e8fd"; +} +.icon-high_quality:before { + content: "\e024"; +} +.icon-highlight:before { + content: "\e25f"; +} +.icon-highlight_off:before { + content: "\e888"; +} +.icon-restore:before { + content: "\e8b3"; +} +.icon-home2:before { + content: "\e88a"; +} +.icon-hot_tub:before { + content: "\eb46"; +} +.icon-local_hotel:before { + content: "\e549"; +} +.icon-hourglass_empty:before { + content: "\e88b"; +} +.icon-hourglass_full:before { + content: "\e88c"; +} +.icon-http:before { + content: "\e902"; +} +.icon-lock2:before { + content: "\e897"; +} +.icon-photo2:before { + content: "\e410"; +} +.icon-image_aspect_ratio:before { + content: "\e3f5"; +} +.icon-import_contacts:before { + content: "\e0e0"; +} +.icon-import_export:before { + content: "\e0c3"; +} +.icon-important_devices:before { + content: "\e912"; +} +.icon-inbox2:before { + content: "\e156"; +} +.icon-indeterminate_check_box:before { + content: "\e909"; +} +.icon-info2:before { + content: "\e88e"; +} +.icon-info_outline:before { + content: "\e88f"; +} +.icon-input:before { + content: "\e890"; +} +.icon-insert_comment:before { + content: "\e24c"; +} +.icon-insert_drive_file:before { + content: "\e24d"; +} +.icon-tag_faces:before { + content: "\e420"; +} +.icon-link2:before { + content: "\e157"; +} +.icon-invert_colors:before { + content: "\e891"; +} +.icon-invert_colors_off:before { + content: "\e0c4"; +} +.icon-iso:before { + content: "\e3f6"; +} +.icon-keyboard:before { + content: "\e312"; +} +.icon-keyboard_arrow_down:before { + content: "\e313"; +} +.icon-keyboard_arrow_left:before { + content: "\e314"; +} +.icon-keyboard_arrow_right:before { + content: "\e315"; +} +.icon-keyboard_arrow_up:before { + content: "\e316"; +} +.icon-keyboard_backspace:before { + content: "\e317"; +} +.icon-keyboard_capslock:before { + content: "\e318"; +} +.icon-keyboard_hide:before { + content: "\e31a"; +} +.icon-keyboard_return:before { + content: "\e31b"; +} +.icon-keyboard_tab:before { + content: "\e31c"; +} +.icon-keyboard_voice:before { + content: "\e31d"; +} +.icon-kitchen:before { + content: "\eb47"; +} +.icon-label:before { + content: "\e892"; +} +.icon-label_outline:before { + content: "\e893"; +} +.icon-language2:before { + content: "\e894"; +} +.icon-laptop_chromebook:before { + content: "\e31f"; +} +.icon-laptop_mac:before { + content: "\e320"; +} +.icon-laptop_windows:before { + content: "\e321"; +} +.icon-last_page:before { + content: "\e5dd"; +} +.icon-open_in_new:before { + content: "\e89e"; +} +.icon-layers:before { + content: "\e53b"; +} +.icon-layers_clear:before { + content: "\e53c"; +} +.icon-leak_add:before { + content: "\e3f8"; +} +.icon-leak_remove:before { + content: "\e3f9"; +} +.icon-lens:before { + content: "\e3fa"; +} +.icon-library_books:before { + content: "\e02f"; +} +.icon-library_music:before { + content: "\e030"; +} +.icon-lightbulb_outline:before { + content: "\e90f"; +} +.icon-line_style:before { + content: "\e919"; +} +.icon-line_weight:before { + content: "\e91a"; +} +.icon-linear_scale:before { + content: "\e260"; +} +.icon-linked_camera:before { + content: "\e438"; +} +.icon-list2:before { + content: "\e896"; +} +.icon-live_help:before { + content: "\e0c6"; +} +.icon-live_tv:before { + content: "\e639"; +} +.icon-local_play:before { + content: "\e553"; +} +.icon-local_airport:before { + content: "\e53d"; +} +.icon-local_atm:before { + content: "\e53e"; +} +.icon-local_bar:before { + content: "\e540"; +} +.icon-local_cafe:before { + content: "\e541"; +} +.icon-local_car_wash:before { + content: "\e542"; +} +.icon-local_convenience_store:before { + content: "\e543"; +} +.icon-restaurant_menu:before { + content: "\e561"; +} +.icon-local_drink:before { + content: "\e544"; +} +.icon-local_florist:before { + content: "\e545"; +} +.icon-local_gas_station:before { + content: "\e546"; +} +.icon-shopping_cart:before { + content: "\e8cc"; +} +.icon-local_hospital:before { + content: "\e548"; +} +.icon-local_laundry_service:before { + content: "\e54a"; +} +.icon-local_library:before { + content: "\e54b"; +} +.icon-local_mall:before { + content: "\e54c"; +} +.icon-theaters:before { + content: "\e8da"; +} +.icon-local_offer:before { + content: "\e54e"; +} +.icon-local_parking:before { + content: "\e54f"; +} +.icon-local_pharmacy:before { + content: "\e550"; +} +.icon-local_pizza:before { + content: "\e552"; +} +.icon-print2:before { + content: "\e8ad"; +} +.icon-local_shipping:before { + content: "\e558"; +} +.icon-local_taxi:before { + content: "\e559"; +} +.icon-location_city:before { + content: "\e7f1"; +} +.icon-location_off:before { + content: "\e0c7"; +} +.icon-room:before { + content: "\e8b4"; +} +.icon-lock_open:before { + content: "\e898"; +} +.icon-lock_outline:before { + content: "\e899"; +} +.icon-looks:before { + content: "\e3fc"; +} +.icon-looks_3:before { + content: "\e3fb"; +} +.icon-looks_4:before { + content: "\e3fd"; +} +.icon-looks_5:before { + content: "\e3fe"; +} +.icon-looks_6:before { + content: "\e3ff"; +} +.icon-looks_one:before { + content: "\e400"; +} +.icon-looks_two:before { + content: "\e401"; +} +.icon-sync:before { + content: "\e627"; +} +.icon-loupe:before { + content: "\e402"; +} +.icon-low_priority:before { + content: "\e16d"; +} +.icon-loyalty:before { + content: "\e89a"; +} +.icon-mail_outline:before { + content: "\e0e1"; +} +.icon-map2:before { + content: "\e55b"; +} +.icon-markunread_mailbox:before { + content: "\e89b"; +} +.icon-memory:before { + content: "\e322"; +} +.icon-menu:before { + content: "\e5d2"; +} +.icon-message:before { + content: "\e0c9"; +} +.icon-mic:before { + content: "\e029"; +} +.icon-mic_none:before { + content: "\e02a"; +} +.icon-mic_off:before { + content: "\e02b"; +} +.icon-mms:before { + content: "\e618"; +} +.icon-mode_comment:before { + content: "\e253"; +} +.icon-monetization_on:before { + content: "\e263"; +} +.icon-money_off:before { + content: "\e25c"; +} +.icon-monochrome_photos:before { + content: "\e403"; +} +.icon-mood_bad:before { + content: "\e7f3"; +} +.icon-more:before { + content: "\e619"; +} +.icon-more_horiz:before { + content: "\e5d3"; +} +.icon-more_vert:before { + content: "\e5d4"; +} +.icon-motorcycle2:before { + content: "\e91b"; +} +.icon-mouse:before { + content: "\e323"; +} +.icon-move_to_inbox:before { + content: "\e168"; +} +.icon-movie_creation:before { + content: "\e404"; +} +.icon-movie_filter:before { + content: "\e43a"; +} +.icon-multiline_chart:before { + content: "\e6df"; +} +.icon-music_note:before { + content: "\e405"; +} +.icon-music_video:before { + content: "\e063"; +} +.icon-nature:before { + content: "\e406"; +} +.icon-nature_people:before { + content: "\e407"; +} +.icon-navigation:before { + content: "\e55d"; +} +.icon-near_me:before { + content: "\e569"; +} +.icon-network_cell:before { + content: "\e1b9"; +} +.icon-network_check:before { + content: "\e640"; +} +.icon-network_locked:before { + content: "\e61a"; +} +.icon-network_wifi:before { + content: "\e1ba"; +} +.icon-new_releases:before { + content: "\e031"; +} +.icon-next_week:before { + content: "\e16a"; +} +.icon-nfc:before { + content: "\e1bb"; +} +.icon-no_encryption:before { + content: "\e641"; +} +.icon-signal_cellular_no_sim:before { + content: "\e1ce"; +} +.icon-note:before { + content: "\e06f"; +} +.icon-note_add:before { + content: "\e89c"; +} +.icon-notifications:before { + content: "\e7f4"; +} +.icon-notifications_active:before { + content: "\e7f7"; +} +.icon-notifications_none:before { + content: "\e7f5"; +} +.icon-notifications_off:before { + content: "\e7f6"; +} +.icon-notifications_paused:before { + content: "\e7f8"; +} +.icon-offline_pin:before { + content: "\e90a"; +} +.icon-ondemand_video:before { + content: "\e63a"; +} +.icon-opacity:before { + content: "\e91c"; +} +.icon-open_in_browser:before { + content: "\e89d"; +} +.icon-open_with:before { + content: "\e89f"; +} +.icon-pages:before { + content: "\e7f9"; +} +.icon-pageview:before { + content: "\e8a0"; +} +.icon-pan_tool:before { + content: "\e925"; +} +.icon-panorama:before { + content: "\e40b"; +} +.icon-radio_button_unchecked:before { + content: "\e836"; +} +.icon-panorama_horizontal:before { + content: "\e40d"; +} +.icon-panorama_vertical:before { + content: "\e40e"; +} +.icon-panorama_wide_angle:before { + content: "\e40f"; +} +.icon-party_mode:before { + content: "\e7fa"; +} +.icon-pause2:before { + content: "\e034"; +} +.icon-pause_circle_filled:before { + content: "\e035"; +} +.icon-pause_circle_outline:before { + content: "\e036"; +} +.icon-people_outline:before { + content: "\e7fc"; +} +.icon-perm_camera_mic:before { + content: "\e8a2"; +} +.icon-perm_contact_calendar:before { + content: "\e8a3"; +} +.icon-perm_data_setting:before { + content: "\e8a4"; +} +.icon-perm_device_information:before { + content: "\e8a5"; +} +.icon-person_outline:before { + content: "\e7ff"; +} +.icon-perm_media:before { + content: "\e8a7"; +} +.icon-perm_phone_msg:before { + content: "\e8a8"; +} +.icon-perm_scan_wifi:before { + content: "\e8a9"; +} +.icon-person:before { + content: "\e7fd"; +} +.icon-person_add:before { + content: "\e7fe"; +} +.icon-person_pin:before { + content: "\e55a"; +} +.icon-person_pin_circle:before { + content: "\e56a"; +} +.icon-personal_video:before { + content: "\e63b"; +} +.icon-pets:before { + content: "\e91d"; +} +.icon-phone_android:before { + content: "\e324"; +} +.icon-phone_bluetooth_speaker:before { + content: "\e61b"; +} +.icon-phone_forwarded:before { + content: "\e61c"; +} +.icon-phone_in_talk:before { + content: "\e61d"; +} +.icon-phone_iphone:before { + content: "\e325"; +} +.icon-phone_locked:before { + content: "\e61e"; +} +.icon-phone_missed:before { + content: "\e61f"; +} +.icon-phone_paused:before { + content: "\e620"; +} +.icon-phonelink_erase:before { + content: "\e0db"; +} +.icon-phonelink_lock:before { + content: "\e0dc"; +} +.icon-phonelink_off:before { + content: "\e327"; +} +.icon-phonelink_ring:before { + content: "\e0dd"; +} +.icon-phonelink_setup:before { + content: "\e0de"; +} +.icon-photo_album:before { + content: "\e411"; +} +.icon-photo_filter:before { + content: "\e43b"; +} +.icon-photo_size_select_actual:before { + content: "\e432"; +} +.icon-photo_size_select_large:before { + content: "\e433"; +} +.icon-photo_size_select_small:before { + content: "\e434"; +} +.icon-picture_as_pdf:before { + content: "\e415"; +} +.icon-picture_in_picture:before { + content: "\e8aa"; +} +.icon-picture_in_picture_alt:before { + content: "\e911"; +} +.icon-pie_chart:before { + content: "\e6c4"; +} +.icon-pie_chart_outlined:before { + content: "\e6c5"; +} +.icon-pin_drop:before { + content: "\e55e"; +} +.icon-play_arrow:before { + content: "\e037"; +} +.icon-play_circle_filled:before { + content: "\e038"; +} +.icon-play_circle_outline:before { + content: "\e039"; +} +.icon-play_for_work:before { + content: "\e906"; +} +.icon-playlist_add:before { + content: "\e03b"; +} +.icon-playlist_add_check:before { + content: "\e065"; +} +.icon-playlist_play:before { + content: "\e05f"; +} +.icon-plus_one:before { + content: "\e800"; +} +.icon-polymer:before { + content: "\e8ab"; +} +.icon-pool:before { + content: "\eb48"; +} +.icon-portable_wifi_off:before { + content: "\e0ce"; +} +.icon-portrait:before { + content: "\e416"; +} +.icon-power:before { + content: "\e63c"; +} +.icon-power_input:before { + content: "\e336"; +} +.icon-power_settings_new:before { + content: "\e8ac"; +} +.icon-pregnant_woman:before { + content: "\e91e"; +} +.icon-present_to_all:before { + content: "\e0df"; +} +.icon-priority_high:before { + content: "\e645"; +} +.icon-public:before { + content: "\e80b"; +} +.icon-publish:before { + content: "\e255"; +} +.icon-queue_music:before { + content: "\e03d"; +} +.icon-queue_play_next:before { + content: "\e066"; +} +.icon-radio:before { + content: "\e03e"; +} +.icon-radio_button_checked:before { + content: "\e837"; +} +.icon-rate_review:before { + content: "\e560"; +} +.icon-receipt:before { + content: "\e8b0"; +} +.icon-recent_actors:before { + content: "\e03f"; +} +.icon-record_voice_over:before { + content: "\e91f"; +} +.icon-redo:before { + content: "\e15a"; +} +.icon-refresh2:before { + content: "\e5d5"; +} +.icon-remove2:before { + content: "\e15b"; +} +.icon-remove_circle_outline:before { + content: "\e15d"; +} +.icon-remove_from_queue:before { + content: "\e067"; +} +.icon-visibility:before { + content: "\e8f4"; +} +.icon-remove_shopping_cart:before { + content: "\e928"; +} +.icon-reorder2:before { + content: "\e8fe"; +} +.icon-repeat2:before { + content: "\e040"; +} +.icon-repeat_one:before { + content: "\e041"; +} +.icon-replay:before { + content: "\e042"; +} +.icon-replay_10:before { + content: "\e059"; +} +.icon-replay_30:before { + content: "\e05a"; +} +.icon-replay_5:before { + content: "\e05b"; +} +.icon-reply2:before { + content: "\e15e"; +} +.icon-reply_all:before { + content: "\e15f"; +} +.icon-report:before { + content: "\e160"; +} +.icon-warning2:before { + content: "\e002"; +} +.icon-restaurant:before { + content: "\e56c"; +} +.icon-restore_page:before { + content: "\e929"; +} +.icon-ring_volume:before { + content: "\e0d1"; +} +.icon-room_service:before { + content: "\eb49"; +} +.icon-rotate_90_degrees_ccw:before { + content: "\e418"; +} +.icon-rotate_left:before { + content: "\e419"; +} +.icon-rotate_right:before { + content: "\e41a"; +} +.icon-rounded_corner:before { + content: "\e920"; +} +.icon-router:before { + content: "\e328"; +} +.icon-rowing:before { + content: "\e921"; +} +.icon-rss_feed:before { + content: "\e0e5"; +} +.icon-rv_hookup:before { + content: "\e642"; +} +.icon-satellite:before { + content: "\e562"; +} +.icon-save2:before { + content: "\e161"; +} +.icon-scanner:before { + content: "\e329"; +} +.icon-school:before { + content: "\e80c"; +} +.icon-screen_lock_landscape:before { + content: "\e1be"; +} +.icon-screen_lock_portrait:before { + content: "\e1bf"; +} +.icon-screen_lock_rotation:before { + content: "\e1c0"; +} +.icon-screen_rotation:before { + content: "\e1c1"; +} +.icon-screen_share:before { + content: "\e0e2"; +} +.icon-sd_storage:before { + content: "\e1c2"; +} +.icon-search2:before { + content: "\e8b6"; +} +.icon-security:before { + content: "\e32a"; +} +.icon-select_all:before { + content: "\e162"; +} +.icon-send2:before { + content: "\e163"; +} +.icon-sentiment_dissatisfied:before { + content: "\e811"; +} +.icon-sentiment_neutral:before { + content: "\e812"; +} +.icon-sentiment_satisfied:before { + content: "\e813"; +} +.icon-sentiment_very_dissatisfied:before { + content: "\e814"; +} +.icon-sentiment_very_satisfied:before { + content: "\e815"; +} +.icon-settings:before { + content: "\e8b8"; +} +.icon-settings_applications:before { + content: "\e8b9"; +} +.icon-settings_backup_restore:before { + content: "\e8ba"; +} +.icon-settings_bluetooth:before { + content: "\e8bb"; +} +.icon-settings_brightness:before { + content: "\e8bd"; +} +.icon-settings_cell:before { + content: "\e8bc"; +} +.icon-settings_ethernet:before { + content: "\e8be"; +} +.icon-settings_input_antenna:before { + content: "\e8bf"; +} +.icon-settings_input_composite:before { + content: "\e8c1"; +} +.icon-settings_input_hdmi:before { + content: "\e8c2"; +} +.icon-settings_input_svideo:before { + content: "\e8c3"; +} +.icon-settings_overscan:before { + content: "\e8c4"; +} +.icon-settings_phone:before { + content: "\e8c5"; +} +.icon-settings_power:before { + content: "\e8c6"; +} +.icon-settings_remote:before { + content: "\e8c7"; +} +.icon-settings_system_daydream:before { + content: "\e1c3"; +} +.icon-settings_voice:before { + content: "\e8c8"; +} +.icon-share2:before { + content: "\e80d"; +} +.icon-shop:before { + content: "\e8c9"; +} +.icon-shop_two:before { + content: "\e8ca"; +} +.icon-shopping_basket:before { + content: "\e8cb"; +} +.icon-short_text:before { + content: "\e261"; +} +.icon-show_chart:before { + content: "\e6e1"; +} +.icon-shuffle:before { + content: "\e043"; +} +.icon-signal_cellular_4_bar:before { + content: "\e1c8"; +} +.icon-signal_cellular_connected_no_internet_4_bar:before { + content: "\e1cd"; +} +.icon-signal_cellular_null:before { + content: "\e1cf"; +} +.icon-signal_cellular_off:before { + content: "\e1d0"; +} +.icon-signal_wifi_4_bar:before { + content: "\e1d8"; +} +.icon-signal_wifi_4_bar_lock:before { + content: "\e1d9"; +} +.icon-signal_wifi_off:before { + content: "\e1da"; +} +.icon-sim_card:before { + content: "\e32b"; +} +.icon-sim_card_alert:before { + content: "\e624"; +} +.icon-skip_next:before { + content: "\e044"; +} +.icon-skip_previous:before { + content: "\e045"; +} +.icon-slideshow:before { + content: "\e41b"; +} +.icon-slow_motion_video:before { + content: "\e068"; +} +.icon-stay_primary_portrait:before { + content: "\e0d6"; +} +.icon-smoke_free:before { + content: "\eb4a"; +} +.icon-smoking_rooms:before { + content: "\eb4b"; +} +.icon-textsms:before { + content: "\e0d8"; +} +.icon-snooze:before { + content: "\e046"; +} +.icon-sort2:before { + content: "\e164"; +} +.icon-sort_by_alpha:before { + content: "\e053"; +} +.icon-spa:before { + content: "\eb4c"; +} +.icon-space_bar:before { + content: "\e256"; +} +.icon-speaker:before { + content: "\e32d"; +} +.icon-speaker_group:before { + content: "\e32e"; +} +.icon-speaker_notes:before { + content: "\e8cd"; +} +.icon-speaker_notes_off:before { + content: "\e92a"; +} +.icon-speaker_phone:before { + content: "\e0d2"; +} +.icon-spellcheck:before { + content: "\e8ce"; +} +.icon-star_border:before { + content: "\e83a"; +} +.icon-star_half:before { + content: "\e839"; +} +.icon-stars:before { + content: "\e8d0"; +} +.icon-stay_primary_landscape:before { + content: "\e0d5"; +} +.icon-stop2:before { + content: "\e047"; +} +.icon-stop_screen_share:before { + content: "\e0e3"; +} +.icon-storage:before { + content: "\e1db"; +} +.icon-store_mall_directory:before { + content: "\e563"; +} +.icon-straighten:before { + content: "\e41c"; +} +.icon-streetview:before { + content: "\e56e"; +} +.icon-strikethrough_s:before { + content: "\e257"; +} +.icon-style:before { + content: "\e41d"; +} +.icon-subdirectory_arrow_left:before { + content: "\e5d9"; +} +.icon-subdirectory_arrow_right:before { + content: "\e5da"; +} +.icon-subject:before { + content: "\e8d2"; +} +.icon-subscriptions:before { + content: "\e064"; +} +.icon-subtitles:before { + content: "\e048"; +} +.icon-subway2:before { + content: "\e56f"; +} +.icon-supervisor_account:before { + content: "\e8d3"; +} +.icon-surround_sound:before { + content: "\e049"; +} +.icon-swap_calls:before { + content: "\e0d7"; +} +.icon-swap_horiz:before { + content: "\e8d4"; +} +.icon-swap_vert:before { + content: "\e8d5"; +} +.icon-swap_vertical_circle:before { + content: "\e8d6"; +} +.icon-switch_camera:before { + content: "\e41e"; +} +.icon-switch_video:before { + content: "\e41f"; +} +.icon-sync_disabled:before { + content: "\e628"; +} +.icon-sync_problem:before { + content: "\e629"; +} +.icon-system_update:before { + content: "\e62a"; +} +.icon-system_update_alt:before { + content: "\e8d7"; +} +.icon-tab:before { + content: "\e8d8"; +} +.icon-tab_unselected:before { + content: "\e8d9"; +} +.icon-tablet2:before { + content: "\e32f"; +} +.icon-tablet_android:before { + content: "\e330"; +} +.icon-tablet_mac:before { + content: "\e331"; +} +.icon-tap_and_play:before { + content: "\e62b"; +} +.icon-text_fields:before { + content: "\e262"; +} +.icon-text_format:before { + content: "\e165"; +} +.icon-texture:before { + content: "\e421"; +} +.icon-thumb_down:before { + content: "\e8db"; +} +.icon-thumb_up:before { + content: "\e8dc"; +} +.icon-thumbs_up_down:before { + content: "\e8dd"; +} +.icon-timelapse:before { + content: "\e422"; +} +.icon-timeline:before { + content: "\e922"; +} +.icon-timer:before { + content: "\e425"; +} +.icon-timer_10:before { + content: "\e423"; +} +.icon-timer_3:before { + content: "\e424"; +} +.icon-timer_off:before { + content: "\e426"; +} +.icon-title:before { + content: "\e264"; +} +.icon-toc:before { + content: "\e8de"; +} +.icon-today:before { + content: "\e8df"; +} +.icon-toll:before { + content: "\e8e0"; +} +.icon-tonality:before { + content: "\e427"; +} +.icon-touch_app:before { + content: "\e913"; +} +.icon-toys:before { + content: "\e332"; +} +.icon-track_changes:before { + content: "\e8e1"; +} +.icon-traffic:before { + content: "\e565"; +} +.icon-train2:before { + content: "\e570"; +} +.icon-tram:before { + content: "\e571"; +} +.icon-transfer_within_a_station:before { + content: "\e572"; +} +.icon-transform:before { + content: "\e428"; +} +.icon-translate:before { + content: "\e8e2"; +} +.icon-trending_down:before { + content: "\e8e3"; +} +.icon-trending_flat:before { + content: "\e8e4"; +} +.icon-trending_up:before { + content: "\e8e5"; +} +.icon-tune:before { + content: "\e429"; +} +.icon-tv2:before { + content: "\e333"; +} +.icon-unarchive:before { + content: "\e169"; +} +.icon-undo2:before { + content: "\e166"; +} +.icon-unfold_less:before { + content: "\e5d6"; +} +.icon-unfold_more:before { + content: "\e5d7"; +} +.icon-update:before { + content: "\e923"; +} +.icon-usb2:before { + content: "\e1e0"; +} +.icon-verified_user:before { + content: "\e8e8"; +} +.icon-vertical_align_bottom:before { + content: "\e258"; +} +.icon-vertical_align_center:before { + content: "\e259"; +} +.icon-vertical_align_top:before { + content: "\e25a"; +} +.icon-vibration:before { + content: "\e62d"; +} +.icon-video_call:before { + content: "\e070"; +} +.icon-video_label:before { + content: "\e071"; +} +.icon-video_library:before { + content: "\e04a"; +} +.icon-videocam:before { + content: "\e04b"; +} +.icon-videocam_off:before { + content: "\e04c"; +} +.icon-videogame_asset:before { + content: "\e338"; +} +.icon-view_agenda:before { + content: "\e8e9"; +} +.icon-view_array:before { + content: "\e8ea"; +} +.icon-view_carousel:before { + content: "\e8eb"; +} +.icon-view_column:before { + content: "\e8ec"; +} +.icon-view_comfy:before { + content: "\e42a"; +} +.icon-view_compact:before { + content: "\e42b"; +} +.icon-view_day:before { + content: "\e8ed"; +} +.icon-view_headline:before { + content: "\e8ee"; +} +.icon-view_list:before { + content: "\e8ef"; +} +.icon-view_module:before { + content: "\e8f0"; +} +.icon-view_quilt:before { + content: "\e8f1"; +} +.icon-view_stream:before { + content: "\e8f2"; +} +.icon-view_week:before { + content: "\e8f3"; +} +.icon-vignette:before { + content: "\e435"; +} +.icon-visibility_off:before { + content: "\e8f5"; +} +.icon-voice_chat:before { + content: "\e62e"; +} +.icon-voicemail:before { + content: "\e0d9"; +} +.icon-volume_down:before { + content: "\e04d"; +} +.icon-volume_mute:before { + content: "\e04e"; +} +.icon-volume_off:before { + content: "\e04f"; +} +.icon-volume_up:before { + content: "\e050"; +} +.icon-vpn_key:before { + content: "\e0da"; +} +.icon-vpn_lock:before { + content: "\e62f"; +} +.icon-wallpaper:before { + content: "\e1bc"; +} +.icon-watch:before { + content: "\e334"; +} +.icon-watch_later:before { + content: "\e924"; +} +.icon-wb_auto:before { + content: "\e42c"; +} +.icon-wb_incandescent:before { + content: "\e42e"; +} +.icon-wb_iridescent:before { + content: "\e436"; +} +.icon-wb_sunny:before { + content: "\e430"; +} +.icon-wc:before { + content: "\e63d"; +} +.icon-web:before { + content: "\e051"; +} +.icon-web_asset:before { + content: "\e069"; +} +.icon-weekend:before { + content: "\e16b"; +} +.icon-whatshot:before { + content: "\e80e"; +} +.icon-widgets:before { + content: "\e1bd"; +} +.icon-wifi2:before { + content: "\e63e"; +} +.icon-wifi_lock:before { + content: "\e1e1"; +} +.icon-wifi_tethering:before { + content: "\e1e2"; +} +.icon-work:before { + content: "\e8f9"; +} +.icon-wrap_text:before { + content: "\e25b"; +} +.icon-youtube_searched_for:before { + content: "\e8fa"; +} +.icon-zoom_in:before { + content: "\e8ff"; +} +.icon-zoom_out:before { + content: "\e901"; +} +.icon-zoom_out_map:before { + content: "\e56b"; +} diff --git a/src/main/resources/static/css/owl.carousel.min.css b/src/main/resources/static/css/owl.carousel.min.css new file mode 100644 index 0000000..1ece042 --- /dev/null +++ b/src/main/resources/static/css/owl.carousel.min.css @@ -0,0 +1,6 @@ +/** + * Owl Carousel v2.2.1 + * Copyright 2013-2017 David Deutsch + * Licensed under () + */ +.owl-carousel,.owl-carousel .owl-item{-webkit-tap-highlight-color:transparent;position:relative}.owl-carousel{display:none;width:100%;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y;-moz-backface-visibility:hidden}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0,0,0)}.owl-carousel .owl-item,.owl-carousel .owl-wrapper{-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0)}.owl-carousel .owl-item{min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-touch-callout:none}.owl-carousel .owl-item img{display:block;width:100%}.owl-carousel .owl-dots.disabled,.owl-carousel .owl-nav.disabled{display:none}.no-js .owl-carousel,.owl-carousel.owl-loaded{display:block}.owl-carousel .owl-dot,.owl-carousel .owl-nav .owl-next,.owl-carousel .owl-nav .owl-prev{cursor:pointer;cursor:hand;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel.owl-refresh .owl-item{visibility:hidden}.owl-carousel.owl-drag .owl-item{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-grab{cursor:move;cursor:grab}.owl-carousel.owl-rtl{direction:rtl}.owl-carousel.owl-rtl .owl-item{float:right}.owl-carousel .animated{animation-duration:1s;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{animation-name:fadeOut}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{transition:height .5s ease-in-out}.owl-carousel .owl-item .owl-lazy{opacity:0;transition:opacity .4s ease}.owl-carousel .owl-item img.owl-lazy{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;transition:transform .1s ease}.owl-carousel .owl-video-play-icon:hover{-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;background-size:contain;transition:opacity .4s ease}.owl-carousel .owl-video-frame{position:relative;z-index:1;height:100%;width:100%} \ No newline at end of file diff --git a/src/main/resources/static/css/owl.theme.default.min.css b/src/main/resources/static/css/owl.theme.default.min.css new file mode 100644 index 0000000..e15a77e --- /dev/null +++ b/src/main/resources/static/css/owl.theme.default.min.css @@ -0,0 +1,15 @@ +/** + * Owl Carousel v2.2.1 + * Copyright 2013-2017 David Deutsch + * Licensed under () + */ +.owl-theme .owl-dots, +.owl-theme .owl-nav{text-align:center;-webkit-tap-highlight-color:transparent} +.owl-theme .owl-nav{margin-top:10px} +.owl-theme .owl-nav [class*=owl-]{color:#FFF;font-size:14px;margin:5px;padding:4px 7px;background:#D6D6D6;display:inline-block;cursor:pointer;border-radius:3px;position: absolute;} +.owl-theme .owl-nav [class*=owl-]:hover{background:#869791;color:#FFF;text-decoration:none} +.owl-theme .owl-nav .disabled{opacity:.5;cursor:default} +.owl-theme .owl-nav.disabled+.owl-dots{margin-top:10px} +.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1} +.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#D6D6D6;display:block;-webkit-backface-visibility:visible;transition:opacity .2s ease;border-radius:30px} +.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791} \ No newline at end of file diff --git a/src/main/resources/static/css/star-rating-theme.min.css b/src/main/resources/static/css/star-rating-theme.min.css new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/static/css/star-rating.min.css b/src/main/resources/static/css/star-rating.min.css new file mode 100644 index 0000000..f21e9ae --- /dev/null +++ b/src/main/resources/static/css/star-rating.min.css @@ -0,0 +1,10 @@ +/*! + * bootstrap-star-rating v4.0.6 + * http://plugins.krajee.com/star-rating + * + * Author: Kartik Visweswaran + * Copyright: 2013 - 2019, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD 3-Clause + * https://github.com/kartik-v/bootstrap-star-rating/blob/master/LICENSE.md + */.rating-loading{width:25px;height:25px;font-size:0;color:#fff;background:url(../img/loading.gif) top left no-repeat;border:none}.rating-container .rating-stars{position:relative;cursor:pointer;vertical-align:middle;display:inline-block;overflow:hidden;white-space:nowrap}.rating-container .rating-input{position:absolute;cursor:pointer;width:100%;height:1px;bottom:0;left:0;font-size:1px;border:none;background:0 0;opacity:0;padding:0;margin:0}.rating-container.is-display-only .rating-input,.rating-container.is-display-only .rating-stars{cursor:default}.rating-disabled .rating-input,.rating-disabled .rating-stars{cursor:not-allowed}.rating-container .star{display:inline-block;margin:0 2px;text-align:center}.rating-container .empty-stars{color:#aaa}.rating-container .filled-stars{position:absolute;left:0;top:0;margin:auto;color:#fde16d;white-space:nowrap;overflow:hidden;-webkit-text-stroke:1px #777;text-shadow:1px 1px #999}.rating-rtl{float:right}.rating-animate .filled-stars{transition:width .25s ease}.rating-rtl .filled-stars{left:auto;right:0;transition:none;-webkit-transform:matrix(-1,0,0,1,0,0);transform:matrix(-1,0,0,1,0,0)}.rating-rtl.is-star .filled-stars{right:.06em}.rating-rtl.is-heart .empty-stars{margin-right:.07em}.rating-container .clear-rating{color:#aaa;cursor:not-allowed;display:inline-block;vertical-align:middle;font-size:60%;padding-right:5px}.clear-rating-active{cursor:pointer!important}.clear-rating-active:hover{color:#843534}.rating-container .caption .label{display:inline-block;padding:.25em .4em;line-height:1;text-align:center;vertical-align:baseline;border-radius:.25rem}.rating-container .caption{color:#999;display:inline-block;vertical-align:middle;line-height:1;margin-left:5px;margin-right:0}.rating-rtl .caption{margin-right:5px;margin-left:0}@media print{.rating-container .clear-rating{display:none}}.rating-xl{font-size:48px}.rating-lg{font-size:40px}.rating-md{font-size:32px}.rating-sm{font-size:24px}.rating-xs{font-size:16px}.rating-xl .caption{font-size:20px}.rating-lg .caption{font-size:18px}.rating-md .caption{font-size:16px}.rating-sm .caption{font-size:14px}.rating-xs .caption{font-size:12px} diff --git a/src/main/resources/static/debug.txt b/src/main/resources/static/debug.txt new file mode 100644 index 0000000..0efb5a9 --- /dev/null +++ b/src/main/resources/static/debug.txt @@ -0,0 +1 @@ +This file should probably not be here. \ No newline at end of file diff --git a/src/main/resources/static/img/awaiting-image-sm.png b/src/main/resources/static/img/awaiting-image-sm.png new file mode 100644 index 0000000..8d7be30 Binary files /dev/null and b/src/main/resources/static/img/awaiting-image-sm.png differ diff --git a/src/main/resources/static/img/awaiting-image.png b/src/main/resources/static/img/awaiting-image.png new file mode 100644 index 0000000..50a112d Binary files /dev/null and b/src/main/resources/static/img/awaiting-image.png differ diff --git a/src/main/resources/static/img/bg_1.jpg b/src/main/resources/static/img/bg_1.jpg new file mode 100644 index 0000000..7db58d2 Binary files /dev/null and b/src/main/resources/static/img/bg_1.jpg differ diff --git a/src/main/resources/static/img/bg_2.jpg b/src/main/resources/static/img/bg_2.jpg new file mode 100644 index 0000000..18eaaae Binary files /dev/null and b/src/main/resources/static/img/bg_2.jpg differ diff --git a/src/main/resources/static/img/favicons/favicon.ico b/src/main/resources/static/img/favicons/favicon.ico new file mode 100644 index 0000000..9fa1cf9 Binary files /dev/null and b/src/main/resources/static/img/favicons/favicon.ico differ diff --git a/src/main/resources/static/img/favicons/favicon.png b/src/main/resources/static/img/favicons/favicon.png new file mode 100644 index 0000000..9a9d21e Binary files /dev/null and b/src/main/resources/static/img/favicons/favicon.png differ diff --git a/src/main/resources/static/img/favicons/favicon_whitebg.png b/src/main/resources/static/img/favicons/favicon_whitebg.png new file mode 100644 index 0000000..809b1ce Binary files /dev/null and b/src/main/resources/static/img/favicons/favicon_whitebg.png differ diff --git a/src/main/resources/static/img/hero_1.jpg b/src/main/resources/static/img/hero_1.jpg new file mode 100644 index 0000000..82c2ad9 Binary files /dev/null and b/src/main/resources/static/img/hero_1.jpg differ diff --git a/src/main/resources/static/img/home_hero_1.jpg b/src/main/resources/static/img/home_hero_1.jpg new file mode 100644 index 0000000..cb11ada Binary files /dev/null and b/src/main/resources/static/img/home_hero_1.jpg differ diff --git a/src/main/resources/static/img/home_hero_1a.jpg b/src/main/resources/static/img/home_hero_1a.jpg new file mode 100644 index 0000000..db5a5fc Binary files /dev/null and b/src/main/resources/static/img/home_hero_1a.jpg differ diff --git a/src/main/resources/static/img/login_logo.png b/src/main/resources/static/img/login_logo.png new file mode 100644 index 0000000..f98e726 Binary files /dev/null and b/src/main/resources/static/img/login_logo.png differ diff --git a/src/main/resources/static/img/login_logo_whitebg.png b/src/main/resources/static/img/login_logo_whitebg.png new file mode 100644 index 0000000..d39897c Binary files /dev/null and b/src/main/resources/static/img/login_logo_whitebg.png differ diff --git a/src/main/resources/static/img/logo.png b/src/main/resources/static/img/logo.png new file mode 100644 index 0000000..856d790 Binary files /dev/null and b/src/main/resources/static/img/logo.png differ diff --git a/src/main/resources/static/img/mf_logo.png b/src/main/resources/static/img/mf_logo.png new file mode 100644 index 0000000..340bc81 Binary files /dev/null and b/src/main/resources/static/img/mf_logo.png differ diff --git a/src/main/resources/static/img/mf_logomark_black_lrg.png b/src/main/resources/static/img/mf_logomark_black_lrg.png new file mode 100644 index 0000000..b5bc306 Binary files /dev/null and b/src/main/resources/static/img/mf_logomark_black_lrg.png differ diff --git a/src/main/resources/static/img/mf_logomark_black_small.png b/src/main/resources/static/img/mf_logomark_black_small.png new file mode 100644 index 0000000..f0eee59 Binary files /dev/null and b/src/main/resources/static/img/mf_logomark_black_small.png differ diff --git a/src/main/resources/static/img/mf_logomark_blue_med.png b/src/main/resources/static/img/mf_logomark_blue_med.png new file mode 100644 index 0000000..c7e6da8 Binary files /dev/null and b/src/main/resources/static/img/mf_logomark_blue_med.png differ diff --git a/src/main/resources/static/img/mf_logomark_white_lrg.png b/src/main/resources/static/img/mf_logomark_white_lrg.png new file mode 100644 index 0000000..ceef968 Binary files /dev/null and b/src/main/resources/static/img/mf_logomark_white_lrg.png differ diff --git a/src/main/resources/static/img/mf_logomark_white_small.png b/src/main/resources/static/img/mf_logomark_white_small.png new file mode 100644 index 0000000..3e37bf2 Binary files /dev/null and b/src/main/resources/static/img/mf_logomark_white_small.png differ diff --git a/src/main/resources/static/img/person_1.jpg b/src/main/resources/static/img/person_1.jpg new file mode 100644 index 0000000..8d02206 Binary files /dev/null and b/src/main/resources/static/img/person_1.jpg differ diff --git a/src/main/resources/static/img/person_2.jpg b/src/main/resources/static/img/person_2.jpg new file mode 100644 index 0000000..cda2645 Binary files /dev/null and b/src/main/resources/static/img/person_2.jpg differ diff --git a/src/main/resources/static/img/person_3.jpg b/src/main/resources/static/img/person_3.jpg new file mode 100644 index 0000000..a39d365 Binary files /dev/null and b/src/main/resources/static/img/person_3.jpg differ diff --git a/src/main/resources/static/img/person_4.jpg b/src/main/resources/static/img/person_4.jpg new file mode 100644 index 0000000..175080a Binary files /dev/null and b/src/main/resources/static/img/person_4.jpg differ diff --git a/src/main/resources/static/img/person_5.jpg b/src/main/resources/static/img/person_5.jpg new file mode 100644 index 0000000..c2b5303 Binary files /dev/null and b/src/main/resources/static/img/person_5.jpg differ diff --git a/src/main/resources/static/img/pexels-background-1.jpg b/src/main/resources/static/img/pexels-background-1.jpg new file mode 100644 index 0000000..e92bacb Binary files /dev/null and b/src/main/resources/static/img/pexels-background-1.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-1.jpg b/src/main/resources/static/img/pexels-photo-1.jpg new file mode 100644 index 0000000..ffae10b Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-1.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-2.jpg b/src/main/resources/static/img/pexels-photo-2.jpg new file mode 100644 index 0000000..56e2d6f Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-2.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-3.jpg b/src/main/resources/static/img/pexels-photo-3.jpg new file mode 100644 index 0000000..711dfab Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-3.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-4.jpg b/src/main/resources/static/img/pexels-photo-4.jpg new file mode 100644 index 0000000..18f8111 Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-4.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-hero.jpg b/src/main/resources/static/img/pexels-photo-hero.jpg new file mode 100644 index 0000000..eeaedd2 Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-hero.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-large-1.jpg b/src/main/resources/static/img/pexels-photo-large-1.jpg new file mode 100644 index 0000000..a2627d0 Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-large-1.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-large-2.jpg b/src/main/resources/static/img/pexels-photo-large-2.jpg new file mode 100644 index 0000000..0a3d8be Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-large-2.jpg differ diff --git a/src/main/resources/static/img/pexels-photo-large-3.jpg b/src/main/resources/static/img/pexels-photo-large-3.jpg new file mode 100644 index 0000000..738b0e2 Binary files /dev/null and b/src/main/resources/static/img/pexels-photo-large-3.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-1.jpg b/src/main/resources/static/img/products/generic-product-1.jpg new file mode 100644 index 0000000..9613b23 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-1.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-10.jpg b/src/main/resources/static/img/products/generic-product-10.jpg new file mode 100644 index 0000000..7a181bb Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-10.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-11.jpg b/src/main/resources/static/img/products/generic-product-11.jpg new file mode 100644 index 0000000..82564b9 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-11.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-12.jpg b/src/main/resources/static/img/products/generic-product-12.jpg new file mode 100644 index 0000000..1e84608 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-12.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-2.jpg b/src/main/resources/static/img/products/generic-product-2.jpg new file mode 100644 index 0000000..2b1f231 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-2.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-3.jpg b/src/main/resources/static/img/products/generic-product-3.jpg new file mode 100644 index 0000000..637af13 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-3.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-4.jpg b/src/main/resources/static/img/products/generic-product-4.jpg new file mode 100644 index 0000000..a1cd169 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-4.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-5.jpg b/src/main/resources/static/img/products/generic-product-5.jpg new file mode 100644 index 0000000..cd09594 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-5.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-6.jpg b/src/main/resources/static/img/products/generic-product-6.jpg new file mode 100644 index 0000000..058e8d3 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-6.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-7.jpg b/src/main/resources/static/img/products/generic-product-7.jpg new file mode 100644 index 0000000..6a2f235 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-7.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-8.jpg b/src/main/resources/static/img/products/generic-product-8.jpg new file mode 100644 index 0000000..28e732d Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-8.jpg differ diff --git a/src/main/resources/static/img/products/generic-product-9.jpg b/src/main/resources/static/img/products/generic-product-9.jpg new file mode 100644 index 0000000..1ad7af8 Binary files /dev/null and b/src/main/resources/static/img/products/generic-product-9.jpg differ diff --git a/src/main/resources/static/img/sale.png b/src/main/resources/static/img/sale.png new file mode 100644 index 0000000..faac7de Binary files /dev/null and b/src/main/resources/static/img/sale.png differ diff --git a/src/main/resources/static/index.old b/src/main/resources/static/index.old new file mode 100644 index 0000000..b8e11d2 --- /dev/null +++ b/src/main/resources/static/index.old @@ -0,0 +1,62 @@ + + + + Home + + + +
+
+
+
+

+

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tempor rhoncus mauris a rutrum. + Nullam nec sem porttitor lorem imperdiet suscipit. Praesent rutrum orci a nunc euismod congue. +

+

+ Register + Your Profile + Site Administration +

+
+
+
+ +
+
+
+ photo-1 +

Suspendisse in

+

Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna. Cras mattis consectetur purus sit amet fermentum.

+

Etiam euismod, lorem ut commodo viverra, justo sem egestas lorem, quis lacinia dui nunc eu lacus. Integer non risus eget purus fringilla maximus. Praesent suscipit dolor at molestie cursus. Nam ac elit maximus, sagittis nunc eget, ultrices turpis.

+

View details »

+
+
+ photo-1 +

Proin enim

+

Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Cras mattis consectetur purus sit amet fermentum. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.

+

Aliquam venenatis at diam at bibendum. Nam aliquam urna ac cursus efficitur. Aliquam ultrices euismod neque eget fringilla. Ut pellentesque mollis tortor rhoncus rhoncus. Nam placerat pellentesque leo a porta.

+

View details »

+
+
+ photo-1 +

Aliquam efficitur

+

Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

+ Praesent rutrum augue magna, sed convallis ipsum fringilla eget. Mauris sodales arcu eget leo pulvinar, et posuere lectus iaculis. Nunc vehicula tortor ac turpis sagittis condimentum. Maecenas convallis efficitur metus.

+

View details »

+
+
+
+
+ + + diff --git a/src/main/resources/static/js/app.js b/src/main/resources/static/js/app.js new file mode 100644 index 0000000..373b316 --- /dev/null +++ b/src/main/resources/static/js/app.js @@ -0,0 +1,160 @@ + +jQuery(document).ready(function($) { + + "use strict"; + + // create site menu for mobile browsers + var siteMenuClone = function() { + console.log("siteMenuClone"); + + $('
').prependTo('.site-wrap'); + + $('
').prependTo('.site-mobile-menu'); + $('
').prependTo('.site-mobile-menu-header'); + $('').prependTo('.site-mobile-menu-header'); + + $('
').appendTo('.site-mobile-menu'); + + $('.js-logo-clone').clone().appendTo('.site-mobile-menu-logo'); + + $('').prependTo('.site-mobile-menu-close'); + + $('.js-clone-nav').each(function() { + var $this = $(this); + console.log("cloning:" + $this) + $this.clone().attr('class', 'site-nav-wrap').appendTo('.site-mobile-menu-body'); + }); + + setTimeout(function() { + + var counter = 0; + $('.site-mobile-menu .has-children').each(function(){ + var $this = $(this); + + $this.prepend('