diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..85f4aafa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +### Eclipse/STS ### +*.apt_generated +*.classpath +*.factorypath +*.project +*.settings +*.springBeans +*.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### General ### +.git +*/target +*.txt +/certificates +/config +/documentation +/qos +/scripts +/serviceregistry +README.md \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d31b0740 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Set line endings to \n +* text eol=lf + +*.png binary +*.docx binary +*.pptx binary +*.pdf binary +*.vsdx binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..175bdf8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +### Eclipse/STS ### +*.apt_generated +*.classpath +*.factorypath +*.project +*.settings +*.springBeans +*.sts4-cache + +### IntelliJ IDEA ### +.idea/*.xml +.idea/dataSources +.idea/libraries +.idea/markdown-navigator +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### General ### +/config/ +*.properties +*/target +*.txt +*app.conf \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..ba0a0055 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +arrowhead-core \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..e98e9236 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,601 @@ + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/00_ServiceRegistry.xml b/.idea/runConfigurations/00_ServiceRegistry.xml new file mode 100644 index 00000000..eb706536 --- /dev/null +++ b/.idea/runConfigurations/00_ServiceRegistry.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/00_ServiceRegistry__TLS_.xml b/.idea/runConfigurations/00_ServiceRegistry__TLS_.xml new file mode 100644 index 00000000..dc77cdf0 --- /dev/null +++ b/.idea/runConfigurations/00_ServiceRegistry__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Authorization.xml b/.idea/runConfigurations/01_Authorization.xml new file mode 100644 index 00000000..1832c303 --- /dev/null +++ b/.idea/runConfigurations/01_Authorization.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Authorization__TLS_.xml b/.idea/runConfigurations/01_Authorization__TLS_.xml new file mode 100644 index 00000000..6db53e14 --- /dev/null +++ b/.idea/runConfigurations/01_Authorization__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_EventHandler.xml b/.idea/runConfigurations/01_EventHandler.xml new file mode 100644 index 00000000..dcebdcb5 --- /dev/null +++ b/.idea/runConfigurations/01_EventHandler.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_EventHandler__TLS_.xml b/.idea/runConfigurations/01_EventHandler__TLS_.xml new file mode 100644 index 00000000..f1173bc0 --- /dev/null +++ b/.idea/runConfigurations/01_EventHandler__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Gatekeeper.xml b/.idea/runConfigurations/01_Gatekeeper.xml new file mode 100644 index 00000000..428171b8 --- /dev/null +++ b/.idea/runConfigurations/01_Gatekeeper.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Gatekeeper__TLS_.xml b/.idea/runConfigurations/01_Gatekeeper__TLS_.xml new file mode 100644 index 00000000..f76b1d83 --- /dev/null +++ b/.idea/runConfigurations/01_Gatekeeper__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Gateway.xml b/.idea/runConfigurations/01_Gateway.xml new file mode 100644 index 00000000..fa99b23d --- /dev/null +++ b/.idea/runConfigurations/01_Gateway.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Gateway__TLS_.xml b/.idea/runConfigurations/01_Gateway__TLS_.xml new file mode 100644 index 00000000..97b9daf0 --- /dev/null +++ b/.idea/runConfigurations/01_Gateway__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Orchestrator.xml b/.idea/runConfigurations/01_Orchestrator.xml new file mode 100644 index 00000000..7a2da36c --- /dev/null +++ b/.idea/runConfigurations/01_Orchestrator.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/01_Orchestrator__TLS_.xml b/.idea/runConfigurations/01_Orchestrator__TLS_.xml new file mode 100644 index 00000000..d1a54b45 --- /dev/null +++ b/.idea/runConfigurations/01_Orchestrator__TLS_.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Core_Systems.xml b/.idea/runConfigurations/Core_Systems.xml new file mode 100644 index 00000000..a6a34405 --- /dev/null +++ b/.idea/runConfigurations/Core_Systems.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Core_Systems__TLS_.xml b/.idea/runConfigurations/Core_Systems__TLS_.xml new file mode 100644 index 00000000..62465bcd --- /dev/null +++ b/.idea/runConfigurations/Core_Systems__TLS_.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c0f28cfa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: java +jdk: oraclejdk8 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..5920de27 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,56 @@ +### November 12th, 2018 +* All dependencies updated to their latest versions and code base now runs on Java 11 +* Changes to support MySQL 8 server +* Most core systems now have their debian packages, which offers fast installation on Debian based Linux systems. See [Debian Packages](https://github.com/arrowhead-f/core-java/tree/develop/documentation/Debian%20Packages) for more details. +* Added windows batch files for starting/stopping the core systems +* Device Registry progress +* Added version based ArrowheadService filtering to Service Registry querying +* Service Registry management API also supports regular expression based querying +* Many bug fixes + +### October 1st, 2018 +* Input validation properly works now, used the incorrect dependencies before +* Exception handling improved +* Fixed some NullPointerException sources +* Removed `broker_name` and `authentication_info` columns from `broker` database table, unique constraint also deleted +* Added [Swagger UI](https://swagger.io/tools/swagger-ui/) to each core system, to help the discoverability of the API +* Merged the `feature/dev_sys_reg` branch into the `develop` branch, which contains the updated System Registry core system + +### September 24th, 2018 +* `README.md` now has a detailed guide on how to setup an arrowhead cloud from source +* The Gatekeeper AccessControlFilter had a critical bug fixed +* Added a 2nd relay test certificate +* Basic continuous integration tooling added to the project +* Core systems now wait 10 second and retry with the service registration 3 times, when the SR is not available at start (makes core system +deployment easier) + +### September 12th, 2018 +* Changed the way config files (properties file before) are processed by the core systems. Now there is a `default.conf` for each core system, +containing default values, and these values can be overridden with key-value pairs placed in a `app.conf` file, which only has to contain the +properties with the new values. + +### September 7th, 2018 +* Certificate Authority core system is ready for use now. It uses the Bouncy Castle library. There is a certificate requester client inside the +client-java repository, which connects to this core system, and creates a usable keystore from the response. + +### September 5th, 2018 +* AccessControlFilters now have an abstract parent class to avoid duplicate codes +* Added 2 different certificate signing method to the CA module, both of which still need thorough testing + +### August 27th, 2018 +* Fixed the registered packages on web server startup +* ArrowheadCloud and ArrowheadSystem names now allow for the following special characters too: _ - : +* Added custom exception to certificate validation path related errors, when sending request to other systems +* ServiceRegistryEntry contains a String metadata field again (With the current security metadata, this is needed to be able to provide a service +in secure and insecure mode at the same time) +* Fixed a bug in Gatekeeper AccessControlFilter, preventing GSD/ICN process in secure mode +* Secure and insecure mode of the full framework can now work at the same time with the same database, if the ArrowheadCloud (and OwnCloud + +NeighborCloud) table contains both Gatekeeper versions. + +### August 13th, 2018 +* Our complicated custom input validation solution replaced with a clean, easy to use annotation based validation library (Hibernate validator) +* Management API refactorings, to provide ID-field based resource query, update and delete operations +* Common resources (services, systems, clouds, devices) have their management REST endpoints in the common module now, so each module can provide their own endpoints for their database +* Complex entity classes now use Hibernate's @OnDelete annotation to delete child entities, when a parent entity gets deleted +* Created new common resource: ArrowheadDevice +* Service metadata is moved back to ArrowheadService, and the port field is moved back to ArrowheadSystem (from ServiceRegistryEntry) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 81ecdcfa..6e6e1827 100644 --- a/README.md +++ b/README.md @@ -10,4 +10,3 @@ ### Build and run ### Windows 10 Install Guide - diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/authorization/config/certificates/authorization.p12 b/authorization/config/certificates/authorization.p12 new file mode 100644 index 00000000..bcb8f5a4 Binary files /dev/null and b/authorization/config/certificates/authorization.p12 differ diff --git a/authorization/config/certificates/truststore.p12 b/authorization/config/certificates/truststore.p12 new file mode 100644 index 00000000..aa386178 Binary files /dev/null and b/authorization/config/certificates/truststore.p12 differ diff --git a/authorization/config/default.conf b/authorization/config/default.conf new file mode 100644 index 00000000..eb8cb01d --- /dev/null +++ b/authorization/config/default.conf @@ -0,0 +1,73 @@ +# +# This work is part of the Productive 4.0 innovation project, which receives grants from the +# European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking +# (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and +# national funding authorities from involved countries. +# + +############################################ +### APPLICATION PARAMETERS ### +############################################ + +# Database connection (mandatory) +db_user=arrowhead +db_password=arrowhead +db_address=jdbc:mysql://127.0.0.1:3306/arrowhead + +# Certificate related paths and passwords (mandatory in secure mode) +keystore=config/certificates/authorization.p12 +keystorepass=123456 +keypass=123456 +truststore=config/certificates/truststore.p12 +truststorepass=123456 + +# Authorization web-server parameters +address=0.0.0.0 +insecure_port=8444 +secure_port=8445 + +# Service Registry web-server parameters (to register the Authorization services) +sr_address=0.0.0.0 +sr_insecure_port=8442 +sr_secure_port=8443 + +#Allow querying access to the authorization tables for application systems (true/false - only has effect in secure mode) +enable_auth_for_cloud=false + + +############################################ +### LOGGING PARAMETERS ### +############################################ + +# Define the root logger with appender file +log4j.rootLogger=INFO, DB, FILE +# Database related config +# Define the DB appender +log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender +# Set Database URL +log4j.appender.DB.URL=jdbc:mysql://127.0.0.1:3306/arrowhead?autoReconnect=true +# Set database user name and password +log4j.appender.DB.user=arrowhead +log4j.appender.DB.password=arrowhead +# Set the SQL statement to be executed. +log4j.appender.DB.sql=INSERT INTO logs(id, date, origin, level, message) VALUES(DEFAULT,'%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m') +# Define the layout for file appender +log4j.appender.DB.layout=org.apache.log4j.PatternLayout +# Disable Hibernate verbose logging +log4j.logger.org.hibernate=fatal + + +# File related config +# Define the file appender +log4j.appender.FILE=org.apache.log4j.FileAppender +# Set the name of the file +log4j.appender.FILE.File=log4j_log.txt +# Set the immediate flush to true (default) +log4j.appender.FILE.ImmediateFlush=true +# Set the threshold to debug mode +log4j.appender.FILE.Threshold=debug +# Set the append to false, overwrite +log4j.appender.FILE.Append=false +# Define the layout for file appender +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss}, %C, %p, %m%n diff --git a/authorization/pom.xml b/authorization/pom.xml new file mode 100644 index 00000000..74de0a63 --- /dev/null +++ b/authorization/pom.xml @@ -0,0 +1,192 @@ + + + + 4.0.0 + + + eu.arrowhead + core + ${revision} + + + arrowhead-authorization + jar + + + 1.59 + 1.8 + 1.8 + 3.8.0 + 2.10 + 3.0.2 + 2.7 + + + + + + eu.arrowhead + arrowhead-core-common + + + + org.bouncycastle + bcprov-jdk15on + ${bouncy.castle.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + + default-compile + + true + true + + ${maven.compiler.target} + ${maven.compiler.source} + + + + + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.version} + + + + + config/ + + + true + eu.arrowhead.core.authorization.AuthorizationMain + lib/ + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + copy-dependencies + package + + copy-dependencies + + + + ${basedir}/target/lib/ + + + + + + maven-resources-plugin + ${maven.resources.version} + + + copy-resources + + validate + + copy-resources + + + ${basedir}/target/config + + + config + + + + + + + + jdeb + org.vafer + 1.7 + + + package + + jdeb + + + true + [YYMMddHHmm].${git.commit.id.abbrev} + target/${project.artifactId}_${revision}.deb + true + ${basedir}/src/deb/control + + + + file + ${project.build.directory}/${project.build.finalName}.jar + + perm + /usr/share/arrowhead + arrowhead + 755 + + + + + link + /usr/share/arrowhead/${project.artifactId}.jar + /usr/share/arrowhead/${project.build.finalName}.jar + + + + file + ${project.basedir}/src/deb/arrowhead-authorization.service + + perm + /etc/systemd/system + 664 + + + + + file + ${project.build.directory}/lib/bcprov-jdk15on-${bouncy.castle.version}.jar + + perm + /usr/share/arrowhead/lib + + + + + + + + + + + diff --git a/authorization/src/deb/arrowhead-authorization.service b/authorization/src/deb/arrowhead-authorization.service new file mode 100644 index 00000000..3786572b --- /dev/null +++ b/authorization/src/deb/arrowhead-authorization.service @@ -0,0 +1,15 @@ +[Unit] +Description=arrowhead-authorization +After=network.target mysql.target arrowhead-serviceregistry-sql.service +Requires=arrowhead-serviceregistry-sql.service + +[Service] +WorkingDirectory=/etc/arrowhead/systems/authorization +ExecStart=/usr/bin/java -jar /usr/share/arrowhead/arrowhead-authorization.jar -d -daemon -tls +TimeoutStopSec=5 +Type=simple +User=arrowhead +Group=arrowhead + +[Install] +WantedBy=default.target diff --git a/authorization/src/deb/control/control b/authorization/src/deb/control/control new file mode 100644 index 00000000..9246b4ec --- /dev/null +++ b/authorization/src/deb/control/control @@ -0,0 +1,10 @@ +Package: [[name]] +Version: [[version]] +Section: contrib/java +Priority: optional +Architecture: all +Maintainer: Thomas Pedersen +Homepage: http://www.arrowhead.eu +Description: Arrowhead Authorization System +Distribution: development +Depends: java-runtime-headless, virtual-mysql-server, arrowhead-core-common, arrowhead-serviceregistry-sql diff --git a/authorization/src/deb/control/postinst b/authorization/src/deb/control/postinst new file mode 100644 index 00000000..948c59ba --- /dev/null +++ b/authorization/src/deb/control/postinst @@ -0,0 +1,132 @@ +#!/bin/sh +# postinst script for arrowhead-authorization +# +# see: dh_installdeb(1) + +set -e + +. /usr/share/debconf/confmodule + +SYSTEM_NAME="authorization" +PKG_NAME="arrowhead-authorization" + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + configure) + . /usr/share/arrowhead/conf/ahconf.sh + SYSTEM_DIR="${AH_SYSTEMS_DIR}/${SYSTEM_NAME}" + + echo "Configuring MySQL database..." >&2 + ah_db_user + + if [ ! -d "${SYSTEM_DIR}" ]; then + mkdir -p ${SYSTEM_DIR} + fi + + ah_cert_signed_system ${SYSTEM_NAME} + + if [ ! -f "${SYSTEM_DIR}/default.conf" ]; then + /bin/cat <${SYSTEM_DIR}/default.conf +############################################ +### APPLICATION PARAMETERS ### +############################################ + +# Database parameters (mandatory) +db_user=arrowhead +db_password=${AH_PASS_DB} +db_address=jdbc:mysql://127.0.0.1:3306/arrowhead + +# Certificate related paths and passwords (mandatory in secure mode) +keystore=${SYSTEM_DIR}/${SYSTEM_NAME}.p12 +keystorepass=${AH_PASS_CERT} +keypass=${AH_PASS_CERT} +truststore=${AH_CONF_DIR}/truststore.p12 +truststorepass=${AH_PASS_CERT} + +# Authorization web-server parameters +address=0.0.0.0 +insecure_port=8444 +secure_port=8445 + +# Service Registry web-server parameters (to register the Authorization services) +sr_address=0.0.0.0 +sr_insecure_port=8442 +sr_secure_port=8443 + +#Allow querying access to the authorization tables for application systems (true/false - only has effect in secure mode) +enable_auth_for_cloud=false + +############################################ +### LOGGING PARAMETERS ### +############################################ + +# Define the root logger with appender file +log4j.rootLogger=INFO, DB, FILE + +# Database related config +# Define the DB appender +log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender +# Set Database URL +log4j.appender.DB.URL=jdbc:mysql://127.0.0.1:3306/arrowhead +# Set database user name and password +log4j.appender.DB.user=arrowhead +log4j.appender.DB.password=${AH_PASS_DB} +# Set the SQL statement to be executed. +log4j.appender.DB.sql=INSERT INTO logs(id, date, origin, level, message) VALUES(DEFAULT,'%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m') +# Define the layout for file appender +log4j.appender.DB.layout=org.apache.log4j.PatternLayout +# Disable Hibernate verbose logging +log4j.logger.org.hibernate=fatal + +# File related config +# Define the file appender +log4j.appender.FILE=org.apache.log4j.FileAppender +# Set the name of the file +log4j.appender.FILE.File=/var/log/arrowhead/${SYSTEM_NAME}.log +# Set the immediate flush to true (default) +log4j.appender.FILE.ImmediateFlush=true +# Set the threshold to debug mode +log4j.appender.FILE.Threshold=debug +# Set the append to false, overwrite +log4j.appender.FILE.Append=false +# Define the layout for file appender +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss}, %C, %p, %m%n +EOF + chown root:arrowhead ${SYSTEM_DIR}/default.conf + chmod 640 ${SYSTEM_DIR}/default.conf + fi + + #ah_log4j_conf ${SYSTEM_NAME} + + echo "Restarting ${PKG_NAME}..." >&2 + systemctl daemon-reload + systemctl restart ${PKG_NAME}.service + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/authorization/src/deb/control/postrm b/authorization/src/deb/control/postrm new file mode 100644 index 00000000..d8db8b32 --- /dev/null +++ b/authorization/src/deb/control/postrm @@ -0,0 +1,54 @@ +#!/bin/sh +# postrm script for arrowhead-authorization +# +# see: dh_installdeb(1) + +set -e + +. /usr/share/debconf/confmodule + +SYSTEM_NAME="authorization" + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + AH_CONF_DIR="/etc/arrowhead" + AH_SYSTEMS_DIR="${AH_CONF_DIR}/systems" + SYSTEM_DIR="${AH_SYSTEMS_DIR}/${SYSTEM_NAME}" + + rm -f \ + /var/log/arrowhead/${SYSTEM_NAME}.log \ + ${SYSTEM_DIR}/default.conf \ + ${SYSTEM_DIR}/${SYSTEM_NAME}.p12 + rmdir ${SYSTEM_DIR} 2>/dev/null || true + rmdir /var/log/arrowhead 2>/dev/null || true + db_purge + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/authorization/src/deb/control/preinst b/authorization/src/deb/control/preinst new file mode 100644 index 00000000..ffafdbae --- /dev/null +++ b/authorization/src/deb/control/preinst @@ -0,0 +1,35 @@ +#!/bin/sh +# preinst script for arrowhead-authorization +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/authorization/src/deb/control/prerm b/authorization/src/deb/control/prerm new file mode 100644 index 00000000..11ef8168 --- /dev/null +++ b/authorization/src/deb/control/prerm @@ -0,0 +1,44 @@ +#!/bin/sh +# prerm script for arrowhead-authorization +# +# see: dh_installdeb(1) + +set -e + +PKG_NAME="arrowhead-authorization" + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + remove) + systemctl stop ${PKG_NAME}.service + ;; + + upgrade|deconfigure) + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationApi.java b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationApi.java new file mode 100644 index 00000000..0b44effd --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationApi.java @@ -0,0 +1,514 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.InterCloudAuthorization; +import eu.arrowhead.common.database.IntraCloudAuthorization; +import eu.arrowhead.common.exception.DataNotFoundException; +import eu.arrowhead.common.messages.InterCloudAuthEntry; +import eu.arrowhead.common.messages.IntraCloudAuthEntry; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; + +@Path("authorization/mgmt") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class AuthorizationApi { + + private final HashMap restrictionMap = new HashMap<>(); + private static final Logger log = Logger.getLogger(AuthorizationApi.class.getName()); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getIt() { + return "authorization/mgmt got it"; + } + + @GET + @Path("publickey") + @Produces(MediaType.TEXT_PLAIN) + public String getMyPublicKey() { + return Base64.getEncoder().encodeToString(AuthorizationMain.publicKey.getEncoded()); + } + + @GET + @Path("intracloud/{id}") + public IntraCloudAuthorization getIntraCloudAuthRight(@PathParam("id") long id) { + return dm.get(IntraCloudAuthorization.class, id).orElseThrow( + () -> new DataNotFoundException("Intra-Cloud Auhtorization entry not found with id: " + id)); + } + + /** + * Returns all the IntraCloud authorization rights from the database. + * + * @return List + */ + @GET + @Path("intracloud") + public List getIntraCloudAuthRights() { + + List authRights = dm.getAll(IntraCloudAuthorization.class, restrictionMap); + if (authRights.isEmpty()) { + log.info("getIntraCloudAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("IntraCloud authorization rights were not found in the database."); + } + + log.info("getIntraCloudAuthRights successfully returns " + authRights.size() + " entries."); + return authRights; + } + + /** + * Returns the list of consumable Services of a System. + * + * @return List + */ + + @GET + @Path("intracloud/systemId/{systemId}/services") + public Set getSystemServices(@PathParam("systemId") long systemId, + @QueryParam("provider_side") boolean providerSide) { + ArrowheadSystem system = dm.get(ArrowheadSystem.class, systemId).orElseThrow(() -> { + log.info("getSystemServices throws DataNotFoundException."); + throw new DataNotFoundException("ArrowheadSystem not found with id:" + systemId); + }); + + if (!providerSide) { + restrictionMap.put("consumer", system); + } else { + restrictionMap.put("provider", system); + } + List authRightsList = dm.getAll(IntraCloudAuthorization.class, restrictionMap); + if (authRightsList.isEmpty()) { + log.info("getSystemServices throws DataNotFoundException."); + throw new DataNotFoundException( + "IntraCloud authorization rights were not found in the database for this consumer system."); + } + + Set serviceList = new HashSet<>(); + for (IntraCloudAuthorization authRight : authRightsList) { + serviceList.add(authRight.getService()); + } + log.info("getSystemServices successfully returns " + serviceList.size() + " services"); + return serviceList; + } + + @GET + @Path("intracloud/systemId/{systemId}") + public List getSystemAuthRights(@PathParam("systemId") long systemId) { + ArrowheadSystem system = dm.get(ArrowheadSystem.class, systemId).orElseThrow(() -> { + log.info("getSystemAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("ArrowheadSystem not found with id:" + systemId); + }); + + restrictionMap.put("consumer", system); + restrictionMap.put("provider", system); + List authRights = dm.getAllOfEither(IntraCloudAuthorization.class, restrictionMap); + if (authRights.isEmpty()) { + log.info("getSystemAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("This System is not in the authorization database. " + system.toString()); + } + log.info("getSystemAuthRights returns " + authRights.size() + " entries"); + return authRights; + } + + @GET + @Path("intracloud/servicedef/{serviceDefinition}") + public List getServiceIntraAuthRights( + @PathParam("serviceDefinition") String serviceDefinition) { + + restrictionMap.put("serviceDefinition", serviceDefinition); + ArrowheadService service = dm.get(ArrowheadService.class, restrictionMap); + if (service == null) { + log.info("getServiceIntraAuthRights throws DataNotFoundException."); + throw new DataNotFoundException( + "The service " + serviceDefinition + " is not in the authorization database"); + } + + restrictionMap.clear(); + restrictionMap.put("service", service); + List authRights = dm.getAll(IntraCloudAuthorization.class, restrictionMap); + if (authRights.isEmpty()) { + log.info("getServiceIntraAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("This Service is not in the authorization database. " + service.toString()); + } + log.info("getServiceIntraAuthRights returns " + authRights.size() + " entries"); + return authRights; + } + + /** + * Creates relations between local Systems, defining the consumable services + * between Systems. (Not bidirectional.) OneToMany relation between consumer and + * providers, OneToMany relation between consumer and services. + * + * @return JAX-RS Response with status code 201 and ArrowheadSystem entity (the + * consumer system) + */ + @POST + @Path("intracloud") + public Response addSystemToAuthorized(@Valid IntraCloudAuthEntry entry) { + return addSystemToAuthorizedGeneric(entry); + } + + public Response addSystemToAuthorizedGeneric(IntraCloudAuthEntry entry) { + restrictionMap.put("systemName", entry.getConsumer().getSystemName()); + restrictionMap.put("address", entry.getConsumer().getAddress()); + restrictionMap.put("port", entry.getConsumer().getPort()); + ArrowheadSystem consumer = dm.get(ArrowheadSystem.class, restrictionMap); + if (consumer == null) { + log.info("Consumer System " + entry.getConsumer().getSystemName() + + " was not in the database, saving it now."); + consumer = dm.save(entry.getConsumer()); + } + + ArrowheadSystem retrievedSystem; + ArrowheadService retrievedService; + List savedAuthRights = new ArrayList<>(); + for (ArrowheadSystem providerSystem : entry.getProviderList()) { + restrictionMap.clear(); + restrictionMap.put("systemName", providerSystem.getSystemName()); + restrictionMap.put("address", providerSystem.getAddress()); + restrictionMap.put("port", providerSystem.getPort()); + retrievedSystem = dm.get(ArrowheadSystem.class, restrictionMap); + if (retrievedSystem == null) { + log.info("Provider System " + providerSystem.getSystemName() + + " was not in the database, saving it now."); + retrievedSystem = dm.save(providerSystem); + } + for (ArrowheadService service : entry.getServiceList()) { + restrictionMap.clear(); + restrictionMap.put("serviceDefinition", service.getServiceDefinition()); + retrievedService = dm.get(ArrowheadService.class, restrictionMap); + if (retrievedService == null) { + log.info("Service " + service.toString() + " was not in the database, saving it now."); + retrievedService = dm.save(service); + } + restrictionMap.clear(); + restrictionMap.put("consumer", consumer); + restrictionMap.put("provider", retrievedSystem); + restrictionMap.put("service", retrievedService); + IntraCloudAuthorization authRight = dm.get(IntraCloudAuthorization.class, restrictionMap); + if (authRight == null) { + authRight = dm.save(new IntraCloudAuthorization(consumer, retrievedSystem, retrievedService)); + savedAuthRights.add(authRight); + } + } + } + + log.info("addSystemToAuthorized: " + savedAuthRights.size() + " authorization rights created."); + GenericEntity> entity = new GenericEntity>( + savedAuthRights) { + }; + return Response.status(Status.CREATED).entity(entity).build(); + } + + @PUT + @Path("intracloud") + public Response updateIntraEntry(@Valid IntraCloudAuthorization updatedEntry) { + IntraCloudAuthorization entry = dm.get(IntraCloudAuthorization.class, updatedEntry.getId()) + .orElseThrow(() -> new DataNotFoundException( + "IntraCloudAuthorization entry not found with id: " + updatedEntry.getId())); + entry.updateEntryWith(updatedEntry); + entry = dm.merge(entry); + log.info("updateIntraEntry successfully returns."); + return Response.ok().entity(entry).build(); + } + + /** + * Deletes the IntraCloudAuthorization entry with the id specified by the path + * parameter. + */ + @DELETE + @Path("intracloud/{id}") + public Response deleteIntraEntry(@PathParam("id") long id) { + return dm.get(IntraCloudAuthorization.class, id).map(entry -> { + dm.delete(entry); + log.info("deleteIntraEntry successfully returns."); + return Response.ok().build(); + }).orElseThrow(() -> { + log.info("deleteIntraEntry had no effect."); + throw new DataNotFoundException("Intra-Cloud Auhtorization entry not found with id: " + id); + }); + } + + /** + * Deletes all the authorization right relations where the given System is the + * consumer/provider (decided by query parameter). + * + * @return JAX-RS Response with status code 200 (if delete is succesxfull) or + * 204 (if nothing happens). + */ + @DELETE + @Path("intracloud/systemId/{systemId}") + public Response deleteSystemRelations(@PathParam("systemId") long systemId, + @QueryParam("provider_side") boolean providerSide) { + ArrowheadSystem system = dm.get(ArrowheadSystem.class, systemId).orElseThrow(() -> { + log.info("deleteSystemRelations throws DNF."); + throw new DataNotFoundException("ArrowheadSystem not found with id: " + systemId); + }); + + if (!providerSide) { + restrictionMap.put("consumer", system); + } else { + restrictionMap.put("provider", system); + } + List authRightsList = dm.getAll(IntraCloudAuthorization.class, restrictionMap); + if (!authRightsList.isEmpty()) { + for (IntraCloudAuthorization authRight : authRightsList) { + dm.delete(authRight); + } + + log.info("deleteSystemRelations successfully returns."); + return Response.ok().build(); + } + + log.info("deleteSystemRelations had no effect."); + return Response.noContent().build(); + } + + @GET + @Path("intercloud/{id}") + public InterCloudAuthorization getInterCloudAuthRight(@PathParam("id") long id) { + return dm.get(InterCloudAuthorization.class, id).orElseThrow( + () -> new DataNotFoundException("Inter-Cloud Auhtorization entry not found with id: " + id)); + } + + /** + * Returns all the InterCloud authorization rights from the database. + * + * @return List + */ + @GET + @Path("intercloud") + public List getInterCloudAuthRights() { + + List authRights = dm.getAll(InterCloudAuthorization.class, restrictionMap); + if (authRights.isEmpty()) { + log.info("getInterCloudAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("InterCloud authorization rights were not found in the database."); + } + + log.info("getInterCloudAuthRights successfully returns " + authRights.size() + " entries."); + return authRights; + } + + /** + * Returns the list of consumable Services of a Cloud. + * + * @return List + */ + + @GET + @Path("intercloud/operator/{operator}/cloudname/{cloudName}/services") + public Set getCloudServices(@PathParam("operator") String operator, + @PathParam("cloudName") String cloudName) { + + restrictionMap.put("operator", operator); + restrictionMap.put("cloudName", cloudName); + ArrowheadCloud cloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (cloud == null) { + log.info("getCloudServices throws DataNotFoundException."); + throw new DataNotFoundException( + "Consumer Cloud (" + operator + ":" + cloudName + ") is not in the authorization database"); + } + + restrictionMap.clear(); + restrictionMap.put("cloud", cloud); + List authRightsList = dm.getAll(InterCloudAuthorization.class, restrictionMap); + Set serviceList = new HashSet<>(); + for (InterCloudAuthorization authRight : authRightsList) { + serviceList.add(authRight.getService()); + } + + log.info("getCloudServices successfully returns " + serviceList.size() + " services."); + return serviceList; + } + + @GET + @Path("intercloud/operator/{operator}/cloudname/{cloudName}") + public List getCloudAuthRights(@PathParam("operator") String operator, + @PathParam("cloudName") String cloudName) { + + restrictionMap.put("operator", operator); + restrictionMap.put("cloudName", cloudName); + ArrowheadCloud cloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (cloud == null) { + log.info("getCloudServices throws DataNotFoundException."); + throw new DataNotFoundException( + "Consumer Cloud (" + operator + ":" + cloudName + ") is not in the authorization database"); + } + + restrictionMap.clear(); + restrictionMap.put("cloud", cloud); + List authRightsList = dm.getAll(InterCloudAuthorization.class, restrictionMap); + if (authRightsList.isEmpty()) { + log.info("getCloudAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("This Cloud is not in the authorization database. " + cloud.toString()); + } + log.info("getCloudAuthRights successfully returns " + authRightsList.size() + " auth entries."); + return authRightsList; + } + + @GET + @Path("intercloud/servicedef/{serviceDefinition}") + public List getServiceInterAuthRights( + @PathParam("serviceDefinition") String serviceDefinition) { + + restrictionMap.put("serviceDefinition", serviceDefinition); + ArrowheadService service = dm.get(ArrowheadService.class, restrictionMap); + if (service == null) { + log.info("getServiceInterAuthRights throws DataNotFoundException."); + throw new DataNotFoundException( + "Consumer Cloud (" + serviceDefinition + ") is not in the authorization database"); + } + + restrictionMap.clear(); + restrictionMap.put("service", service); + List authRights = dm.getAll(InterCloudAuthorization.class, restrictionMap); + if (authRights.isEmpty()) { + log.info("getServiceInterAuthRights throws DataNotFoundException."); + throw new DataNotFoundException("This Service is not in the authorization database. " + service.toString()); + } + log.info("getServiceInterAuthRights returns"); + return authRights; + } + + /** + * Adds a new Cloud and its consumable Services to the database. + * + * @return JAX-RS Response with status code 201 and ArrowheadCloud entity + */ + @POST + @Path("intercloud") + public Response addCloudToAuthorized(@Valid InterCloudAuthEntry entry) { + + restrictionMap.put("operator", entry.getCloud().getOperator()); + restrictionMap.put("cloudName", entry.getCloud().getCloudName()); + ArrowheadCloud cloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (cloud == null) { + log.info("Consumer Cloud was not in the database, saving it now." + entry.getCloud().toString()); + cloud = dm.save(entry.getCloud()); + } + + ArrowheadService retrievedService; + List savedAuthRights = new ArrayList<>(); + for (ArrowheadService service : entry.getServiceList()) { + restrictionMap.clear(); + restrictionMap.put("serviceDefinition", service.getServiceDefinition()); + retrievedService = dm.get(ArrowheadService.class, restrictionMap); + if (retrievedService == null) { + log.info("Service was not in the database, saving it now." + service.toString()); + retrievedService = dm.save(service); + } + restrictionMap.clear(); + restrictionMap.put("cloud", cloud); + restrictionMap.put("service", retrievedService); + InterCloudAuthorization authRight = dm.get(InterCloudAuthorization.class, restrictionMap); + if (authRight == null) { + authRight = dm.save(new InterCloudAuthorization(cloud, retrievedService)); + savedAuthRights.add(authRight); + } + } + + log.info("addCloudToAuthorized: " + savedAuthRights.size() + " authorization rights created."); + GenericEntity> entity = new GenericEntity>( + savedAuthRights) { + }; + return Response.status(Status.CREATED).entity(entity).build(); + } + + @PUT + @Path("intercloud") + public Response updateInterEntry(@Valid InterCloudAuthorization updatedEntry) { + InterCloudAuthorization entry = dm.get(InterCloudAuthorization.class, updatedEntry.getId()) + .orElseThrow(() -> new DataNotFoundException( + "InterCloudAuthorization entry not found with id: " + updatedEntry.getId())); + entry.updateEntryWith(updatedEntry); + entry = dm.merge(entry); + log.info("InterCloudAuthorization successfully returns."); + return Response.ok().entity(entry).build(); + } + + /** + * Deletes the InterCloudAuthorization entry with the id specified by the path + * parameter. Returns 200 if the delete is successful, 204 (no content) if the + * entry was not in the database to begin with. + */ + @DELETE + @Path("intercloud/{id}") + public Response deleteInterEntry(@PathParam("id") long id) { + return dm.get(InterCloudAuthorization.class, id).map(entry -> { + dm.delete(entry); + log.info("deleteInterEntry successfully returns."); + return Response.ok().build(); + }).orElseThrow(() -> { + log.info("deleteInterEntry had no effect."); + throw new DataNotFoundException("Inter-Cloud Auhtorization entry not found with id: " + id); + }); + } + + /** + * Deletes the authorization rights of the Cloud. + * + * @return JAX-RS Response with status code 200 (if delete is successful) or 204 + * (if nothing happens). + */ + @DELETE + @Path("intercloud/operator/{operator}/cloudname/{cloudName}") + public Response deleteCloudRelations(@PathParam("operator") String operator, + @PathParam("cloudName") String cloudName) { + log.info("Entered the deleteCloudRelations method."); + + restrictionMap.put("operator", operator); + restrictionMap.put("cloudName", cloudName); + ArrowheadCloud cloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (cloud == null) { + log.info("deleteCloudRelations had no effect."); + return Response.noContent().build(); + } + + restrictionMap.clear(); + restrictionMap.put("cloud", cloud); + List authRightsList = dm.getAll(InterCloudAuthorization.class, restrictionMap); + if (!authRightsList.isEmpty()) { + for (InterCloudAuthorization authRight : authRightsList) { + dm.delete(authRight); + } + + log.info("deleteCloudRelations successfully returns."); + return Response.ok().build(); + } + + log.info("deleteCloudRelations had no effect."); + return Response.noContent().build(); + } + +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationMain.java b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationMain.java new file mode 100644 index 00000000..5c6f3c2e --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationMain.java @@ -0,0 +1,46 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization; + +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; + +import eu.arrowhead.common.ArrowheadMain; +import eu.arrowhead.common.misc.CoreSystem; +import eu.arrowhead.common.misc.SecurityUtils; + +public class AuthorizationMain extends ArrowheadMain { + + public static boolean enableAuthForCloud; + + static PrivateKey privateKey; + static PublicKey publicKey; + + private AuthorizationMain(String[] args) { + KeyStore keyStore = SecurityUtils.loadKeyStore(props.getProperty("keystore"), + props.getProperty("keystorepass")); + privateKey = SecurityUtils.getPrivateKey(keyStore, props.getProperty("keystorepass")); + publicKey = SecurityUtils.getFirstCertFromKeyStore(keyStore).getPublicKey(); + enableAuthForCloud = props.getBooleanProperty("enable_auth_for_cloud", false); + + String[] packages = { "eu.arrowhead.common", "eu.arrowhead.core.authorization" }; + init(CoreSystem.AUTHORIZATION, args, null, packages); + for (String s : args) { + if (s.equals("-opcua")) { + startUaServer("authorization"); + new AuthorizationOpcUa(getArrowheadOpcUaServer()); + } + } + listenForInput(); + } + + public static void main(String[] args) { + new AuthorizationMain(args); + } +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationOpcUa.java b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationOpcUa.java new file mode 100644 index 00000000..d0061fc6 --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationOpcUa.java @@ -0,0 +1,22 @@ +package eu.arrowhead.core.authorization; + +import org.apache.commons.lang3.ArrayUtils; +import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode; +import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode; +import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort; + +import eu.arrowhead.common.opcua.ArrowheadOpcUaServer; +import eu.arrowhead.core.authorization.opcua.AddSystemToAuthorized; + +public class AuthorizationOpcUa { + private int namespaceIndex; + + public AuthorizationOpcUa(ArrowheadOpcUaServer server) { + namespaceIndex = ArrayUtils.indexOf(server.getNodeContext().getNamespaceTable().toArray(), + "urn:arrowhead:namespace"); + UaFolderNode authorizationFolder = server.addFolder(UShort.valueOf(namespaceIndex), "Authorization"); + UaMethodNode addSystemToAuthorized = server.addMethodNode(UShort.valueOf(namespaceIndex), authorizationFolder, + "addSystemToAuthorized"); + server.addMethodNodeInNamespace(addSystemToAuthorized, authorizationFolder, new AddSystemToAuthorized(addSystemToAuthorized)); + } +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationResource.java b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationResource.java new file mode 100644 index 00000000..b8775b5d --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/AuthorizationResource.java @@ -0,0 +1,186 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.InterCloudAuthorization; +import eu.arrowhead.common.database.IntraCloudAuthorization; +import eu.arrowhead.common.exception.DataNotFoundException; +import eu.arrowhead.common.messages.ArrowheadToken; +import eu.arrowhead.common.messages.InterCloudAuthRequest; +import eu.arrowhead.common.messages.InterCloudAuthResponse; +import eu.arrowhead.common.messages.IntraCloudAuthRequest; +import eu.arrowhead.common.messages.IntraCloudAuthResponse; +import eu.arrowhead.common.messages.TokenData; +import eu.arrowhead.common.messages.TokenGenerationRequest; +import eu.arrowhead.common.messages.TokenGenerationResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; + +/** + * This is the REST resource for the Authorization Core System. + */ +@Path("authorization") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class AuthorizationResource { + + private final HashMap restrictionMap = new HashMap<>(); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + private static final Logger log = Logger.getLogger(AuthorizationResource.class.getName()); + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getIt() { + return "This is the Authorization Resource."; + } + + /** + * Checks whether the consumer System can use a Service from a list of provider Systems. + * + * @return IntraCloudAuthResponse + * + * @throws DataNotFoundException, BadPayloadException + */ + @PUT + @Path("intracloud") + public Response isSystemAuthorized(@Valid IntraCloudAuthRequest request) { + restrictionMap.put("systemName", request.getConsumer().getSystemName()); + restrictionMap.put("address", request.getConsumer().getAddress()); + restrictionMap.put("port", request.getConsumer().getPort()); + ArrowheadSystem consumer = dm.get(ArrowheadSystem.class, restrictionMap); + if (consumer == null) { + log.error("Consumer is not in the database. isSystemAuthorized DataNotFoundException"); + throw new DataNotFoundException("Consumer System is not in the authorization database. " + request.getConsumer().getSystemName(), + Status.NOT_FOUND.getStatusCode()); + } + + IntraCloudAuthResponse response = new IntraCloudAuthResponse(); + HashMap authorizationState = new HashMap<>(); + restrictionMap.clear(); + restrictionMap.put("serviceDefinition", request.getService().getServiceDefinition()); + ArrowheadService service = dm.get(ArrowheadService.class, restrictionMap); + if (service == null) { + log.info("Service " + request.getService().toString() + " is not in the database. Returning NOT AUTHORIZED state for the consumer."); + for (ArrowheadSystem provider : request.getProviders()) { + authorizationState.put(provider, false); + } + response.setAuthorizationMap(authorizationState); + return Response.status(Status.OK).entity(response).build(); + } + + IntraCloudAuthorization authRight; + int authorizedCount = 0; + for (ArrowheadSystem provider : request.getProviders()) { + restrictionMap.clear(); + restrictionMap.put("systemName", provider.getSystemName()); + restrictionMap.put("address", provider.getAddress()); + restrictionMap.put("port", provider.getPort()); + ArrowheadSystem retrievedSystem = dm.get(ArrowheadSystem.class, restrictionMap); + + restrictionMap.clear(); + restrictionMap.put("consumer", consumer); + restrictionMap.put("provider", retrievedSystem); + restrictionMap.put("service", service); + authRight = dm.get(IntraCloudAuthorization.class, restrictionMap); + + if (authRight == null) { + authorizationState.put(provider, false); + } else { + authorizationState.put(provider, true); + authorizedCount++; + } + } + + log.info( + "IntraCloud auth check for consumer " + request.getConsumer().getSystemName() + " returns with " + authorizedCount + " possible provider"); + response.setAuthorizationMap(authorizationState); + return Response.status(Status.OK).entity(response).build(); + } + + /** + * Checks whether an external Cloud can use a local Service. + * + * @return boolean + * + * @throws DataNotFoundException, BadPayloadException + */ + @PUT + @Path("intercloud") + public Response isCloudAuthorized(@Valid InterCloudAuthRequest request) { + restrictionMap.put("operator", request.getCloud().getOperator()); + restrictionMap.put("cloudName", request.getCloud().getCloudName()); + ArrowheadCloud cloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (cloud == null) { + log.error("Requester cloud is not in the database. isCloudAuthorized DataNotFoundException"); + throw new DataNotFoundException("Consumer Cloud is not in the authorization database. " + request.getCloud().toString(), + Status.NOT_FOUND.getStatusCode()); + } + + restrictionMap.clear(); + restrictionMap.put("serviceDefinition", request.getService().getServiceDefinition()); + ArrowheadService service = dm.get(ArrowheadService.class, restrictionMap); + if (service == null) { + log.info("Service " + request.getService().toString() + " is not in the database. Returning NOT AUTHORIZED state for the consumer."); + return Response.status(Status.OK).entity(new InterCloudAuthResponse(false)).build(); + } + + InterCloudAuthorization authRight; + restrictionMap.clear(); + restrictionMap.put("cloud", cloud); + restrictionMap.put("service", service); + authRight = dm.get(InterCloudAuthorization.class, restrictionMap); + + boolean isAuthorized = false; + if (authRight != null) { + isAuthorized = true; + } + + log.info("Consumer Cloud is authorized: " + isAuthorized); + return Response.status(Status.OK).entity(new InterCloudAuthResponse(isAuthorized)).build(); + } + + /** + * Generates ArrowheadTokens for each consumer/service/provider trio + * + * @return TokenGenerationResponse + */ + @PUT + @Path("token") + public Response tokenGeneration(@Valid TokenGenerationRequest request) { + // Get the tokens from the service class (can throw run time exceptions) + List tokens = TokenGenerationService.generateTokens(request); + List tokenDataList = new ArrayList<>(); + + // Only add the successfully created tokens to the response, with the matching provider System + for (int i = 0; i < tokens.size(); i++) { + if (tokens.get(i) != null) { + TokenData tokenData = new TokenData(request.getProviders().get(i), request.getService(), tokens.get(i).getToken(), + tokens.get(i).getSignature()); + tokenDataList.add(tokenData); + } + } + + log.info("Token generation returns with " + tokenDataList.size() + " arrowhead tokens."); + return Response.status(Status.OK).entity(new TokenGenerationResponse(tokenDataList)).build(); + } +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/TokenGenerationService.java b/authorization/src/main/java/eu/arrowhead/core/authorization/TokenGenerationService.java new file mode 100644 index 00000000..12419900 --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/TokenGenerationService.java @@ -0,0 +1,185 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization; + +import eu.arrowhead.common.Utility; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.AuthException; +import eu.arrowhead.common.messages.ArrowheadToken; +import eu.arrowhead.common.messages.RawTokenInfo; +import eu.arrowhead.common.messages.TokenGenerationRequest; +import eu.arrowhead.common.misc.SecurityUtils; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.ServiceConfigurationError; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +class TokenGenerationService { + + private static final Logger log = Logger.getLogger(TokenGenerationService.class.getName()); + + static List generateTokens(TokenGenerationRequest request) { + // First get the public key for each provider + List publicKeys = getProviderPublicKeys(request.getProviders()); + + // Cryptographic object initializations + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher; + try { + cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException e) { + log.fatal("Cipher.getInstance(String) throws exception, code needs to be changed!"); + throw new AssertionError("Cipher.getInstance(String) throws exception, code needs to be changed!", e); + } + Signature signature; + try { + signature = Signature.getInstance("SHA256withRSA", "BC"); + signature.initSign(AuthorizationMain.privateKey); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + log.fatal("Signature.getInstance(String) throws exception, code needs to be changed!"); + throw new AssertionError("Signature.getInstance(String) throws exception, code needs to be changed!", e); + } catch (InvalidKeyException e) { + log.fatal("The private key of the Authorization module is invalid, keystore needs to be changed!"); + throw new ServiceConfigurationError("The private key of the Authorization module is invalid, keystore needs to be changed!", e); + } + + // Create the ArrowheadToken for each provider + RawTokenInfo rawTokenInfo = new RawTokenInfo(); + List tokens = new ArrayList<>(); + for (PublicKey key : publicKeys) { + // Can not generate token without the provider public key + if (key == null) { + tokens.add(null); + continue; + } + + // Set consumer info string + String c = request.getConsumer().getSystemName(); + if (request.getConsumerCloud() != null) { + c = c.concat(".").concat(request.getConsumerCloud().getCloudName()).concat(".").concat(request.getConsumerCloud().getOperator()); + } else { + boolean secureMode = Boolean.valueOf(System.getProperty("is_secure", "false")); + ArrowheadCloud ownCloud = Utility.getOwnCloud(secureMode); + c = c.concat(".").concat(ownCloud.getCloudName()).concat(".").concat(ownCloud.getOperator()); + } + rawTokenInfo.setC(c); + + String s = request.getService().getServiceDefinition(); + // Set service info string + List interfaces = new ArrayList<>(request.getService().getInterfaces()); + if (!interfaces.isEmpty()) { + s = interfaces.get(0) + "." + s; + } + rawTokenInfo.setS(s); + + // Set the token validity duration + if (request.getDuration() != 0) { + long endTime = System.currentTimeMillis() + request.getDuration(); + rawTokenInfo.setE(endTime); + } else { + // duration = 0 means a token is valid without a time limitation + rawTokenInfo.setE(0L); + } + + // There is an upper limit for the size of the token info, skip providers which exceeds this limit + String json = Utility.toPrettyJson(null, rawTokenInfo); + if (json == null) { + log.error("RawTokenInfo serialization failed. Skipped provider."); + continue; + } + System.out.println("Raw token info: "); + System.out.println(json); + if (json.length() > 244) { + tokens.add(null); + log.error("ArrowheadToken exceeded the size limit. Skipped provider."); + continue; + } + + // Finally, generate the token and signature strings + try { + cipher.init(Cipher.ENCRYPT_MODE, key); + byte[] tokenBytes = cipher.doFinal(json.getBytes(StandardCharsets.UTF_8)); + System.out.println("Token bytes: " + Arrays.toString(tokenBytes)); + signature.update(tokenBytes); + byte[] sigBytes = signature.sign(); + System.out.println("Signature bytes: " + Arrays.toString(sigBytes)); + + String tokenString = Base64.getEncoder().encodeToString(tokenBytes); + String signatureString = Base64.getEncoder().encodeToString(sigBytes); + tokens.add(new ArrowheadToken(tokenString, signatureString)); + } catch (Exception e) { + e.printStackTrace(); + log.error("Cipher or Signature class throws public key specific exception: " + e.getMessage()); + tokens.add(null); + } + } + + // Throw an exception if none of the token generation was successful + boolean nonNullTokenExists = false; + for (ArrowheadToken token : tokens) { + if (token != null) { + nonNullTokenExists = true; + break; + } + } + if (!nonNullTokenExists) { + log.error("None of the provider ArrowheadSystems in this orchestration have a valid RSA public key spec stored in the database."); + throw new ArrowheadException("Token generation failed for all the provider ArrowheadSystems.", Status.INTERNAL_SERVER_ERROR.getStatusCode()); + } + + return tokens; + } + + + private static List getProviderPublicKeys(List providers) { + List keys = new ArrayList<>(); + + for (ArrowheadSystem provider : providers) { + try { + PublicKey key = SecurityUtils.getPublicKey(provider.getAuthenticationInfo(), false); + keys.add(key); + } catch (AuthException e) { + log.error("The stored auth info for the ArrowheadSystem (" + provider.getSystemName() + + ") is not a proper RSA public key spec, or it is incorrectly encoded, or missing. The public key can not be decoded from " + + "it."); + keys.add(null); + } + } + + // Throw an exception if none of the public keys could be acquired from the specs + boolean nonNullKeyExists = false; + for (PublicKey key : keys) { + if (key != null) { + nonNullKeyExists = true; + break; + } + } + if (!nonNullKeyExists) { + log.error("None of the provider ArrowheadSystems in this orchestration have a valid RSA public key spec stored in the database."); + throw new ArrowheadException("Token generation failed for all the provider ArrowheadSystems.", Status.INTERNAL_SERVER_ERROR.getStatusCode()); + } + + return keys; + } + +} \ No newline at end of file diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/filter/AuthACF.java b/authorization/src/main/java/eu/arrowhead/core/authorization/filter/AuthACF.java new file mode 100644 index 00000000..315f9d8f --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/filter/AuthACF.java @@ -0,0 +1,47 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization.filter; + +import eu.arrowhead.common.filter.AccessControlFilter; +import eu.arrowhead.common.misc.SecurityUtils; +import eu.arrowhead.core.authorization.AuthorizationMain; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.AUTHORIZATION) //2nd highest priority constant, this filter gets executed after the SecurityFilter +public class AuthACF extends AccessControlFilter { + + @Override + public boolean isClientAuthorized(String clientCN, String method, String requestTarget, String requestJson) { + if (!SecurityUtils.isKeyStoreCNArrowheadValid(clientCN)) { + log.info(clientCN + " is not valid common name, access denied!"); + return false; + } + + String serverCN = (String) configuration.getProperty("server_common_name"); + String[] serverFields = serverCN.split("\\.", 2); + + if (AuthorizationMain.enableAuthForCloud) { + if (!requestTarget.contains("mgmt") || (requestTarget.endsWith("intracloud") && method.equalsIgnoreCase("post"))) { + String[] clientFields = clientCN.split("\\.", 2); + return serverFields[1].equalsIgnoreCase(clientFields[1]); + } else { + return clientCN.equalsIgnoreCase("sysop." + serverFields[1]); + } + } else { + if (requestTarget.contains("mgmt")) { + return clientCN.equalsIgnoreCase("sysop." + serverFields[1]) || (clientCN.equalsIgnoreCase("certificate_authority." + serverFields[1]) + && requestTarget.endsWith("publickey") && method.equalsIgnoreCase("get")); + } else { + return clientCN.equalsIgnoreCase("orchestrator." + serverFields[1]) || clientCN.equalsIgnoreCase("gatekeeper." + serverFields[1]); + } + } + } +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/filter/SupportAccessControlFilter.java b/authorization/src/main/java/eu/arrowhead/core/authorization/filter/SupportAccessControlFilter.java new file mode 100644 index 00000000..ae717a51 --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/filter/SupportAccessControlFilter.java @@ -0,0 +1,77 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.core.authorization.filter; + +import eu.arrowhead.common.Utility; +import eu.arrowhead.common.exception.AuthException; +import eu.arrowhead.common.misc.SecurityUtils; +import eu.arrowhead.core.authorization.AuthorizationMain; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.SecurityContext; +import org.apache.log4j.Logger; + +//Legacy version of the AuthACF, with certificates containing the system group field +//@Provider Uncomment this line to activate the filter +@Priority(Priorities.AUTHORIZATION) //2nd highest priority constant, this filter gets executed after the SecurityFilter +public class SupportAccessControlFilter implements ContainerRequestFilter { + + private static final Logger log = Logger.getLogger(SupportAccessControlFilter.class.getName()); + @Context + private Configuration configuration; + + @Override + public void filter(ContainerRequestContext requestContext) { + SecurityContext sc = requestContext.getSecurityContext(); + String requestTarget = Utility.stripEndSlash(requestContext.getUriInfo().getRequestUri().toString()); + if (sc.isSecure() && !isGetItCalled(requestContext.getMethod(), requestTarget)) { + String commonName = SecurityUtils.getCertCNFromSubject(sc.getUserPrincipal().getName()); + if (isClientAuthorized(commonName, requestTarget)) { + log.info("SSL identification is successful! Cert: " + commonName); + } else { + log.error(commonName + " is unauthorized to access " + requestTarget); + throw new AuthException(commonName + " is unauthorized to access " + requestTarget, Status.UNAUTHORIZED.getStatusCode()); + } + } + } + + private boolean isGetItCalled(String method, String requestTarget) { + return method.equals("GET") && (requestTarget.endsWith("authorization") || requestTarget.endsWith("mgmt")); + } + + private boolean isClientAuthorized(String clientCN, String requestTarget) { + String serverCN = (String) configuration.getProperty("server_common_name"); + + if (!SecurityUtils.isKeyStoreCNArrowheadValidLegacy(clientCN)) { + log.info("Client cert does not have 6 parts, so the access will be denied."); + return false; + } + + String[] serverFields = serverCN.split("\\.", 2); + // serverFields contains: coreSystemName, cloudName.operator.arrowhead.eu + if (requestTarget.contains("mgmt")) { + // Only the local System Operator can use these methods + return clientCN.equalsIgnoreCase("sysop." + serverFields[1]); + } else { + // If this property is true, then every system from the local cloud can use the auth services + if (AuthorizationMain.enableAuthForCloud) { + String[] clientFields = clientCN.split("\\.", 2); + return serverFields[1].equalsIgnoreCase(clientFields[1]); + } + // If it is not true, only the Orchestrator and Gatekeeper can use it + else { + return clientCN.equalsIgnoreCase("orchestrator." + serverFields[1]) || clientCN.equalsIgnoreCase("gatekeeper." + serverFields[1]); + } + } + } +} diff --git a/authorization/src/main/java/eu/arrowhead/core/authorization/opcua/AddSystemToAuthorized.java b/authorization/src/main/java/eu/arrowhead/core/authorization/opcua/AddSystemToAuthorized.java new file mode 100644 index 00000000..2bf3b70a --- /dev/null +++ b/authorization/src/main/java/eu/arrowhead/core/authorization/opcua/AddSystemToAuthorized.java @@ -0,0 +1,66 @@ +package eu.arrowhead.core.authorization.opcua; + +import java.io.IOException; + +import javax.ws.rs.core.Response; + +import org.eclipse.milo.opcua.sdk.core.ValueRanks; +import org.eclipse.milo.opcua.sdk.server.api.methods.AbstractMethodInvocationHandler; +import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode; +import org.eclipse.milo.opcua.stack.core.Identifiers; +import org.eclipse.milo.opcua.stack.core.UaException; +import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; +import org.eclipse.milo.opcua.stack.core.types.builtin.Variant; +import org.eclipse.milo.opcua.stack.core.types.structured.Argument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import eu.arrowhead.common.opcua.OpcUaHelper; +import eu.arrowhead.core.authorization.AuthorizationApi; + +public class AddSystemToAuthorized extends AbstractMethodInvocationHandler{ + private final Logger logger = LoggerFactory.getLogger(getClass()); + + public AddSystemToAuthorized(UaMethodNode node) { + super(node); + } + + public static final Argument IntraCloudAuthEntry = new Argument("IntraCloudAuthEntry", Identifiers.String, ValueRanks.Scalar, null, + new LocalizedText("IntraCloudAuthEntry")); + + public static final Argument ResponseStatus = new Argument("ResponseStatus", Identifiers.String, ValueRanks.Scalar, null, + new LocalizedText("ResponseStatus")); + + @Override + public Argument[] getInputArguments() { + return new Argument[] { IntraCloudAuthEntry }; + } + + @Override + public Argument[] getOutputArguments() { + return new Argument[] { ResponseStatus }; + } + + @Override + protected Variant[] invoke(InvocationContext invocationContext, Variant[] inputValues) throws UaException { + Response out = null; + logger.debug("Invoking query() method of Object '{}'", invocationContext.getObjectId()); + try { + out = new AuthorizationApi().addSystemToAuthorizedGeneric( + new OpcUaHelper().icaeFromJsonString(inputValues[0].getValue().toString())); + } catch (JsonParseException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + String res = out.getStatusInfo().getReasonPhrase(); + + return new Variant[] { new Variant(res) }; + } +} diff --git a/authorization/src/main/resources/hibernate.cfg.xml b/authorization/src/main/resources/hibernate.cfg.xml new file mode 100644 index 00000000..9cf7443b --- /dev/null +++ b/authorization/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,41 @@ + + + + + + + true + update + + org.hibernate.hikaricp.internal.HikariCPConnectionProvider + 10 + true + 250 + 2048 + true + true + true + true + true + true + true + false + Authorization + true + + + + + + + + + + diff --git a/authorization/src/main/resources/openapi.yaml b/authorization/src/main/resources/openapi.yaml new file mode 100644 index 00000000..b39d8dbd --- /dev/null +++ b/authorization/src/main/resources/openapi.yaml @@ -0,0 +1,7 @@ +prettyPrint: true +openAPI: + info: + title: Arrowhead Authorization Core System API + description: 'This page shows the REST interfaces offered by the Authorization Core System.' + contact: + url: https://github.com/arrowhead-f \ No newline at end of file diff --git a/certificates/broker/broker.crt b/certificates/broker/broker.crt new file mode 100644 index 00000000..238ef3f6 --- /dev/null +++ b/certificates/broker/broker.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+DCCAeCgAwIBAgIEWlYfrjANBgkqhkiG9w0BAQsFADBHMSEwHwYDVQQKDBhB +SVRJQSBJbnRlcm5hdGlvbmFsIEluYy4xCzAJBgNVBAYTAkhVMRUwEwYDVQQDDAxh +cnJvd2hlYWQuZXUwHhcNMTgwMTEwMTQxNDA2WhcNMjgwMTEwMTQxNDA2WjA1MQsw +CQYDVQQGEwJIVTEmMCQGA1UEAwwddGVzdGJyb2tlci5haXRpYS5hcnJvd2hlYWQu +ZXUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRkYEc6KdAXmy1gvuS +u4u6MIJbS9JlkdtXnsP4G/HWFx3LuObIAMzbBw3NOYCIhhFwVxjvf0ME0gTA+llw +7zdY+7dUj+TJ3EBtfgvajfHm2IO71S1kZTCCTfFdbbtXMj8uf7hCEFLvKM4GUNe6 +i368xkVh5eBihCbm/F77jKt/tV/K73NB91dJBC290RJjrkq0mj5Hs4+WY1ezX/B1 +XR1iOzjs6ZmL5gxh1A7PqQHkbL7/Qotos3qzHUIqzUR1QlpJYgS/fjZfrLoJvrfq +WvKsQ8sD0y5wxdO1QXOt2EDA0SUUjkJDkePujUsU5ljoXEOgYLGVQJoz+MGVrH4r +SFepAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEJUFWWWMMNnogOYiWdH4rUNVESw +8rj1oczkZg+h+oQV4Qg6GxXFr9qL5LUOlcDRalJbWjd8yJBtQDIT7A2AuCQjLocg +F1FZDa8nWcPkYNr7h4QX7E/7PNqAghjARSVaycMDtqaVCB0RlmcYMjreFjM71kRf +HNcMOKLpdIMPhpfr2MC8E7EG6zfK0zsN3+qgZizqfR7Q8f6S0T8srIMuvBjk2h1a +iM13ftu1/cn/d2RMAom46Mh1Z3qwhucO58BMwHzHJX24UE9xGWgOW3u/OrHNMmhz +mNvlTNpbu3hZWoVMKnkWM3PVgGJSSJ2LlMBqO3uaOo1rdQ9WwPORST/Urgc= +-----END CERTIFICATE----- diff --git a/certificates/broker/broker.key b/certificates/broker/broker.key new file mode 100644 index 00000000..4778f284 --- /dev/null +++ b/certificates/broker/broker.key @@ -0,0 +1,32 @@ +Bag Attributes + friendlyName: testbroker.aitia.arrowhead.eu + localKeyID: 54 69 6D 65 20 31 35 31 35 35 39 35 38 38 31 30 30 35 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRkYEc6KdAXmy1 +gvuSu4u6MIJbS9JlkdtXnsP4G/HWFx3LuObIAMzbBw3NOYCIhhFwVxjvf0ME0gTA ++llw7zdY+7dUj+TJ3EBtfgvajfHm2IO71S1kZTCCTfFdbbtXMj8uf7hCEFLvKM4G +UNe6i368xkVh5eBihCbm/F77jKt/tV/K73NB91dJBC290RJjrkq0mj5Hs4+WY1ez +X/B1XR1iOzjs6ZmL5gxh1A7PqQHkbL7/Qotos3qzHUIqzUR1QlpJYgS/fjZfrLoJ +vrfqWvKsQ8sD0y5wxdO1QXOt2EDA0SUUjkJDkePujUsU5ljoXEOgYLGVQJoz+MGV +rH4rSFepAgMBAAECggEAASDqiXweMe3hUMqGUiwayXaRrjaVps+1355By+pCE9Ct +YzNXC+RrnGcmhZxG3VM4TrBNp4pk13fNznu1U5lvy4jhoEQntlRyySyy3YKTyB6b +LWhxsCK4s8FXbwPnmXA2olu87AWfjLazJETD/B1T7t1jA5mvlalNEBCVu23dkk3R +HE+X3fknPXMotbnc9Jb6ujPIiAMAXB3lWFMwqeRXA9fqJ06YktjhWvOIIBe1vXRd +LqzoTSRP24XQdDctaMlmxZjCg+j4w2P5Dw0k1i8FoFWdLj2pjFP0Be4Nmm3z9cjo +KMNRYpYevYhI+TSCHjx4IytXeza9kP4xNZQxs/u9SQKBgQD7GXvA1wmvyRKER00O +bR4EHwMcVNb6ZUh2yknWrMwUkBUkKt+Xm+UuFACyuAPjFk95EEsfLxD9YtHfB6vy +oHhRuyoM5RgX2bAEvka1/vVkAmPnz5U5K66k95U8T04pQO/6A0Tlw/gDbd4Jx5dK +MMb+v2S/IZ9yioJCG3Ch1Z+6QwKBgQDVqIcB1/UwM6NOGpv9WSlic3eOOQcPfykZ +U9tCS4/knScM6JFMnf/yo6TVWmZgyhLZiaSCC+2We3kmSHBQ/KCSzvgnJMGzrr/v +vYpFVTTznZAQYLLw1AHw7eCWL2AHcSFtKzK4QIQfCcHYD5hG2eCH+Eh1e2YsmCIE +bkmI1snVowKBgQD1wDojQRKk7Yjm15Wt8dN3uxZjSxFf3XKveEoOBe5yjZVzYVaV +3ijOERuhzoFXZoV/ehfbvEfHhkjbHHoIK0+2xhRgrHMJHK+t5L/17UMjj0dSIBLu +8jaqE/oGHaPksj5sFXj/2u1IBeZS8fm6zcRknNMwUOrpwm+73Xk9moMKWwKBgCjL +OaPHeQAQC/EgsYHo4Tbz9vLHvqpdzBoS4b1BsiKjQs57JSrsy+6nAhTeYse+LWUt +m6Rc5KsjvhjqIc/EvF/rdoP6x6UoC2uZ0+mzAB43ikcmP2QuBoTa1lO2je490nUG +l3OIlfFbKpC3QYrdlLpBVIxh2DtRfMU9zXp8ECNjAoGBAPRTi4/CiKeNSMV8BGFe +EXzwLlaB+28TnSM/XW7npkdPkFU9nS7EOW7SsqkyfHFDj+c9JrvBnDGaHPu06Iv9 +Xw4SYer4u91sO6KiDm0fLX9tCzo9MFFH2Rj+GkOHlwkwe2J1Cc9Wdf48JhHCf/dD +YOk6MsK/prkzSJVtRNr4cViX +-----END PRIVATE KEY----- diff --git a/certificates/broker/broker.pem b/certificates/broker/broker.pem new file mode 100644 index 00000000..238ef3f6 --- /dev/null +++ b/certificates/broker/broker.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+DCCAeCgAwIBAgIEWlYfrjANBgkqhkiG9w0BAQsFADBHMSEwHwYDVQQKDBhB +SVRJQSBJbnRlcm5hdGlvbmFsIEluYy4xCzAJBgNVBAYTAkhVMRUwEwYDVQQDDAxh +cnJvd2hlYWQuZXUwHhcNMTgwMTEwMTQxNDA2WhcNMjgwMTEwMTQxNDA2WjA1MQsw +CQYDVQQGEwJIVTEmMCQGA1UEAwwddGVzdGJyb2tlci5haXRpYS5hcnJvd2hlYWQu +ZXUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRkYEc6KdAXmy1gvuS +u4u6MIJbS9JlkdtXnsP4G/HWFx3LuObIAMzbBw3NOYCIhhFwVxjvf0ME0gTA+llw +7zdY+7dUj+TJ3EBtfgvajfHm2IO71S1kZTCCTfFdbbtXMj8uf7hCEFLvKM4GUNe6 +i368xkVh5eBihCbm/F77jKt/tV/K73NB91dJBC290RJjrkq0mj5Hs4+WY1ezX/B1 +XR1iOzjs6ZmL5gxh1A7PqQHkbL7/Qotos3qzHUIqzUR1QlpJYgS/fjZfrLoJvrfq +WvKsQ8sD0y5wxdO1QXOt2EDA0SUUjkJDkePujUsU5ljoXEOgYLGVQJoz+MGVrH4r +SFepAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEJUFWWWMMNnogOYiWdH4rUNVESw +8rj1oczkZg+h+oQV4Qg6GxXFr9qL5LUOlcDRalJbWjd8yJBtQDIT7A2AuCQjLocg +F1FZDa8nWcPkYNr7h4QX7E/7PNqAghjARSVaycMDtqaVCB0RlmcYMjreFjM71kRf +HNcMOKLpdIMPhpfr2MC8E7EG6zfK0zsN3+qgZizqfR7Q8f6S0T8srIMuvBjk2h1a +iM13ftu1/cn/d2RMAom46Mh1Z3qwhucO58BMwHzHJX24UE9xGWgOW3u/OrHNMmhz +mNvlTNpbu3hZWoVMKnkWM3PVgGJSSJ2LlMBqO3uaOo1rdQ9WwPORST/Urgc= +-----END CERTIFICATE----- diff --git a/certificates/broker/broker2.crt b/certificates/broker/broker2.crt new file mode 100644 index 00000000..ef56b882 --- /dev/null +++ b/certificates/broker/broker2.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7DCCAdSgAwIBAgIEW6DbjjANBgkqhkiG9w0BAQsFADBHMSEwHwYDVQQKDBhB +SVRJQSBJbnRlcm5hdGlvbmFsIEluYy4xCzAJBgNVBAYTAkhVMRUwEwYDVQQDDAxh +cnJvd2hlYWQuZXUwHhcNMTgwOTE4MTEwMzQyWhcNMTkwOTE4MTEwMzQyWjApMScw +JQYDVQQDDB50ZXN0YnJva2VyMi5haXRpYS5hcnJvd2hlYWQuZXUwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC35mGViemS7yKtbAKDTWRRU3/9oE8Y5F7a +gIlTaalYUjGAPMGFeJ/asR4FuCgti0b3oDYHcpK6ficgFfDmhW46ab636188hDvQ +iiHpsUx85IuHYmsWbjjUtCgnPZwByl4ld7zc3DNPkG0VV5cLZaWdHzpmUOfrG3VZ +mnfV2a9/tetl9+RBU9KvVonBh7rBToq7HHAhzajdAPtVcIbrBqN82knZp+opObsX +HP62QsZqqn2z425pBbknUErBhQBJTQ6PqW1ATcGRmnbDo/56qxIGNCQ8Wd5m0z75 +QmnocDsyZiusbZ8wC1yo4vMvybsh7jKYeoEvY6mvgZQ+v9QWjVipAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBADCMWO5NgTVglH984VdikMhN0jW/F1spkuw171+6AluB +plK55p7JzLQK6RyYxam75F6KRc6TdsmwUk3hcPAe3QdmDISoqkQInTdfdavN8LF3 +jLfiEpv4caViZp4KFL3iCID7ygmzNJQOVD89+wbIeDCNCunbPVy3nfv/Q3hpR1x7 +WiV2hMdzZzsfdxnmVobxL/fitGKJEyJuhPoIQVr/cTXewHrve2SrxeD/xr8pORP9 +eISyelMqkDop/LpBnUyzSkYD0VuQ4IZDSlBZTLSIhe3/KDCIrmKjxUf2+QoHaksO +6aVWfeW917UQaMab3VkHeL9Si7OC+yQojBjt76Ye2No= +-----END CERTIFICATE----- diff --git a/certificates/broker/broker2.key b/certificates/broker/broker2.key new file mode 100644 index 00000000..2cd619aa --- /dev/null +++ b/certificates/broker/broker2.key @@ -0,0 +1,31 @@ +Bag Attributes + friendlyName: testbroker2.aitia.arrowhead.eu (arrowhead.eu) + localKeyID: 54 69 6D 65 20 31 35 33 37 32 37 32 38 37 34 32 38 37 +Key Attributes: +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAt+ZhlYnpku8irWwCg01kUVN//aBPGORe2oCJU2mpWFIxgDzB +hXif2rEeBbgoLYtG96A2B3KSun4nIBXw5oVuOmm+t+tfPIQ70Ioh6bFMfOSLh2Jr +Fm441LQoJz2cAcpeJXe83NwzT5BtFVeXC2WlnR86ZlDn6xt1WZp31dmvf7XrZffk +QVPSr1aJwYe6wU6KuxxwIc2o3QD7VXCG6wajfNpJ2afqKTm7Fxz+tkLGaqp9s+Nu +aQW5J1BKwYUASU0Oj6ltQE3BkZp2w6P+eqsSBjQkPFneZtM++UJp6HA7MmYrrG2f +MAtcqOLzL8m7Ie4ymHqBL2Opr4GUPr/UFo1YqQIDAQABAoIBAE1F3udJBf6ZwUaB +xiSKFocdwQA/pjzdaQYDULDeCyxzjR1HakG9R95K4kSg9zD7/xv6E/nZKNJ4Q9R1 +o74ZfxHIcT0zB9eT8NZV1J4vw+YO2ppOvpS0R+kIsn6aor0V3a18vL1JQb5C/7Mi +FPPTCF4XSyDHCkx+QzcdcxFG4TmV7qh3021TpLT5BAf+DSwy9W/xUsgttC039Q1C +lLAgMIUheOTUQhiapvL67F0iepE2Gms4RjYzbrfVzmJCeTmR4r7btOaGwQAUfeSO +STaMUW7rlyS6j8cHX2ekeEfCcg+FA0amI2Uyb39+UKQujYs9tt2AqabeVumi3ErA +KylW7kkCgYEA6oWmCjor+MXpCEDclqIrQgYZiytA8OpJbPuALi44+AIVPbdnipcW +1lF1SmOWHAmBLNpiNwgDCFWpH7E2nSt72WuDLv3R5f1Lgx/TO3fk09erfcCynJ5O +AmRcuriLbG60npzUggyq4kSuzNfJ9WuBMAAcwWjIjULjPSZD/MmRj+sCgYEAyL3m +iWs142NlpoeMBmiTspU6y5bGdqlLRViMPsHaNrmcxp0ZlK57nwQ4UqfzTRqYvnos +szzZ9vAjyqk4scyrVO1tb6Oh/s3ppDi9WnUfYB7CAe+/YqPYaljOtGjRNn4EfBbR +Q0JMOXLRs4fO0iJhdpUZK1Z0wbr4KUTN0O2VqLsCgYEA2NrsRdi0gDzA1ljjoIYj +6dEqHX3JdvutGZEVXZ5wHyQN8Bt/Kq+69kIsarTbQOGpCIiG+Mv3weUzlZKQJZTn +2IuJqhIZ3Mjy4jSzZfHfLePnN0jP3/Khv6R9+kpa4dEcQu5Rsk90U/WejNuca8sU +kgYIkO8LQ1PuO8T+aOkaKDECgYEAsSKRGVWXWa1N92LIdqc1HXJ9TUUgn3jHyMtY +BdC9I2kqJEBgYEiPIURyBs3Y7YK4oevhIdBibXp9uskUFCCMVwajeM2llthQgO7o +d7e+Fe1OOLM76Gi4AS+YRq3vXuxYPL9NrNIFvPPKg2clY1ufMSpj6VAC+yhm7tOd +4eykER0CgYA97QokT1rNiQOeDEeKQDZb612RXCr4dsNdwK/fAAjh9zvoixHVmnpT +6nCfhUfmGweV5q4s9xtF1wPBjWctiNoEi5lX+hzkhAPHpgVVwOx6VBovJ0H5Otw6 +7ILMg+QDXw6I6lzt1ryDbvvI112OJdc8jbD84SdXrZmCyj3EBscYLg== +-----END RSA PRIVATE KEY----- diff --git a/certificates/broker/broker2.pem b/certificates/broker/broker2.pem new file mode 100644 index 00000000..ef56b882 --- /dev/null +++ b/certificates/broker/broker2.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7DCCAdSgAwIBAgIEW6DbjjANBgkqhkiG9w0BAQsFADBHMSEwHwYDVQQKDBhB +SVRJQSBJbnRlcm5hdGlvbmFsIEluYy4xCzAJBgNVBAYTAkhVMRUwEwYDVQQDDAxh +cnJvd2hlYWQuZXUwHhcNMTgwOTE4MTEwMzQyWhcNMTkwOTE4MTEwMzQyWjApMScw +JQYDVQQDDB50ZXN0YnJva2VyMi5haXRpYS5hcnJvd2hlYWQuZXUwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC35mGViemS7yKtbAKDTWRRU3/9oE8Y5F7a +gIlTaalYUjGAPMGFeJ/asR4FuCgti0b3oDYHcpK6ficgFfDmhW46ab636188hDvQ +iiHpsUx85IuHYmsWbjjUtCgnPZwByl4ld7zc3DNPkG0VV5cLZaWdHzpmUOfrG3VZ +mnfV2a9/tetl9+RBU9KvVonBh7rBToq7HHAhzajdAPtVcIbrBqN82knZp+opObsX +HP62QsZqqn2z425pBbknUErBhQBJTQ6PqW1ATcGRmnbDo/56qxIGNCQ8Wd5m0z75 +QmnocDsyZiusbZ8wC1yo4vMvybsh7jKYeoEvY6mvgZQ+v9QWjVipAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBADCMWO5NgTVglH984VdikMhN0jW/F1spkuw171+6AluB +plK55p7JzLQK6RyYxam75F6KRc6TdsmwUk3hcPAe3QdmDISoqkQInTdfdavN8LF3 +jLfiEpv4caViZp4KFL3iCID7ygmzNJQOVD89+wbIeDCNCunbPVy3nfv/Q3hpR1x7 +WiV2hMdzZzsfdxnmVobxL/fitGKJEyJuhPoIQVr/cTXewHrve2SrxeD/xr8pORP9 +eISyelMqkDop/LpBnUyzSkYD0VuQ4IZDSlBZTLSIhe3/KDCIrmKjxUf2+QoHaksO +6aVWfeW917UQaMab3VkHeL9Si7OC+yQojBjt76Ye2No= +-----END CERTIFICATE----- diff --git a/certificates/broker/broker2_cert.jks b/certificates/broker/broker2_cert.jks new file mode 100644 index 00000000..5e2fb54e Binary files /dev/null and b/certificates/broker/broker2_cert.jks differ diff --git a/certificates/broker/broker_cert.jks b/certificates/broker/broker_cert.jks new file mode 100644 index 00000000..dd1d780b Binary files /dev/null and b/certificates/broker/broker_cert.jks differ diff --git a/certificates/broker/master_arrowhead_cert.pem b/certificates/broker/master_arrowhead_cert.pem new file mode 100644 index 00000000..66e5b00f --- /dev/null +++ b/certificates/broker/master_arrowhead_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDITCCAgmgAwIBAgIJAIiy+gkhV/z6MA0GCSqGSIb3DQEBCwUAMEcxITAfBgNV +BAoMGEFJVElBIEludGVybmF0aW9uYWwgSW5jLjELMAkGA1UEBhMCSFUxFTATBgNV +BAMMDGFycm93aGVhZC5ldTAeFw0xODAxMTAxMjM1MTFaFw0yODAxMDgxMjM1MTFa +MEcxITAfBgNVBAoMGEFJVElBIEludGVybmF0aW9uYWwgSW5jLjELMAkGA1UEBhMC +SFUxFTATBgNVBAMMDGFycm93aGVhZC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMMLdPYaRBqhIQFaIaq4icCKTGeviBYl8UDK4hWarO6Qrp1twh5I +6JLKEjpu4bfsAcF/q6ExuOWOdQVOWqxnHIvLK2CxTWMf14TH1W08sLExidr/7LyE +cgBKzlVBwBaLrrU0jPHsMeMgCaUfQJJ2dEiSNXD8uSeUON+uIA5K0P4LqVfLELjr +IN3oIMhUxzICmxFx+Ek0gdi8PsFaOKnYH0OOginXY2EtMkWSvTwZpEmdZVkm229m +MgDylBeqLWTonawCO2tbTSm0rJgDpfCiST9Zf2Ppu6zJ1H0BPTcT0/hvXSnZnkxF +dEWTSRV5xstCHn5VOGv4JdSB7T06J4uhTwsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEAiG/hAl69bJYeqFbi5lUElmCWHGatCnAz2zAj +ddGi8DHL3sEDgzSYy7UUiDKEhJvLOoJwq+m7RfCr3LyEXCCw3F0CLMAfXa+u5ZmQ +sNJiz43dvZ/TvBxMuyZNbqzspVVD0t8li+bmDMqAjVVfgmtV9fVygBMYlLzCmjvF +3ZmkU+1yuQhgTMe8/WY90vaMzB7pciScaVXVjF/AcCWu8NC6cG6rTbRtBUVgNb4I +NuHTc72XfbgvhGeVGstpvnJlFqCUvWprgQeglJkZSxtgmrwUC1PSzeUPlIbuKhuy +i+ZCHmiIPrBNMbwm56IspNVHwrDYC9BAm2MEn0VKgm0YdMrXyg== +-----END CERTIFICATE----- diff --git a/certificates/certificate_password.txt b/certificates/certificate_password.txt new file mode 100644 index 00000000..29de3ca4 --- /dev/null +++ b/certificates/certificate_password.txt @@ -0,0 +1,3 @@ +PASSWORD: 123456 + +Valid for all certificate files in this folder. \ No newline at end of file diff --git a/certificates/dev_notes.txt b/certificates/dev_notes.txt new file mode 100644 index 00000000..17642587 --- /dev/null +++ b/certificates/dev_notes.txt @@ -0,0 +1,77 @@ +How to create Arrowhead-compliant certificates + +0. Take the certificate keystore that is wished to be used as the signer + +1. Open it in KeyStore Explorer (http://keystore-explorer.org/downloads.html) + - default KeyStore and Key pwd-s for all test certificates: '12345' + +2. Right click on the certificate itself --> Sign / Sign New Key Pair + +3. Edit settings for the new certificate: + - validity period: e.g. set to 10 years + - Name --> There is a book icon with '@': set CN fields + CN: ...arrowhead.eu format + Other fields can be filled out arbitrarily. + +4. Move the newly created certificate to a new tab within the app + - drag & drop works + - it creates a new jks file for it + - 'Save as' this new keystore as a '.jks' file + +5. Tools --> Set Keystore Password + +6. Right click on the certificate --> Set Password + - the 'old' password is empty + - give a new password to the certificate itself + +7. Tools --> Set Keystore Type --> JKS + - this changes from 'p12' filetype to Java Keystore format + +8. Save the keystore again (File --> Save or CTRL+S) + ++1. Don't forget to delete the new certificate from the old one's keystore! + +--------------------------------------------------------------------------- +How to extract crt and key files from jks files: (run as sudo/administrator) + +1. Get the alias from .jks file +keytool -v -list -keystore .jks + +2. Export the .der file +keytool -export -alias "alias with the () part included" -file sample.der -keystore my.jks + +3.Convert the .der file to unencrypted PEM (crt file) +openssl x509 -inform der -in sample.der -out sample.crt + +4.Export the .p12 file +keytool -importkeystore -srckeystore my.jks -destkeystore keystore.p12 -deststoretype PKCS12 -destkeypass pass + +5.Convert the .p12 file to unencrypted PEM (key file) +openssl pkcs12 -in keystore.p12 -nodes -nocerts -out server.key + +--------------------------------------------------------------------------- +How to export certificate public key in Base64 with the ArrowheadProvider client skeleton: +1. Edit the app.config file with the proper client and trust store keystores +2. Start the skeleton with "-tls" command line arguments. +3. The skeleton will print out the Base64 encoded string on the console during start. (This is the 2nd print, which starts as: "Server PublicKey Base64: ") + +--------------------------------------------------------------------------- +How to export certificate public key in Base64 for the database manually: +1. Open the jks in Keystore Explorer +2. Right click on certificate +3. Export -> Export Public Key +4. Remove --BEGIN-- and --END-- from .openssl file +5. Insert into Auth_info field in Arrowhead database +--------------------------------------------------------------------------- + +How to add our root certificate to the JDK +1. Go to the ~/jdk1.8.0_92/jre/bin +2. Copy the master_arrowhead_cert.crt here +3. Open a command line as administrator here +4. Type: keytool -import -alias "arrowhead.eu" -keystore "$JAVA_HOME/lib/security/cacerts" -file master_arrowhead_cert.crt + (JAVA_HOME can be the JRE or JDK/jre/lib version of cacert, used truststore may depend on individual setup) +5. The default password is "changeit", then type "y" and hit enter. The certificate is added to the JDK truststore now. + +How to delete an alias from java cacerts keystore +1. keytool -delete -alias "aliasname" -keystore "file_path_to_cacerts" + - password: changeit \ No newline at end of file diff --git a/certificates/master.crt b/certificates/master.crt new file mode 100644 index 00000000..66e5b00f --- /dev/null +++ b/certificates/master.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDITCCAgmgAwIBAgIJAIiy+gkhV/z6MA0GCSqGSIb3DQEBCwUAMEcxITAfBgNV +BAoMGEFJVElBIEludGVybmF0aW9uYWwgSW5jLjELMAkGA1UEBhMCSFUxFTATBgNV +BAMMDGFycm93aGVhZC5ldTAeFw0xODAxMTAxMjM1MTFaFw0yODAxMDgxMjM1MTFa +MEcxITAfBgNVBAoMGEFJVElBIEludGVybmF0aW9uYWwgSW5jLjELMAkGA1UEBhMC +SFUxFTATBgNVBAMMDGFycm93aGVhZC5ldTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMMLdPYaRBqhIQFaIaq4icCKTGeviBYl8UDK4hWarO6Qrp1twh5I +6JLKEjpu4bfsAcF/q6ExuOWOdQVOWqxnHIvLK2CxTWMf14TH1W08sLExidr/7LyE +cgBKzlVBwBaLrrU0jPHsMeMgCaUfQJJ2dEiSNXD8uSeUON+uIA5K0P4LqVfLELjr +IN3oIMhUxzICmxFx+Ek0gdi8PsFaOKnYH0OOginXY2EtMkWSvTwZpEmdZVkm229m +MgDylBeqLWTonawCO2tbTSm0rJgDpfCiST9Zf2Ppu6zJ1H0BPTcT0/hvXSnZnkxF +dEWTSRV5xstCHn5VOGv4JdSB7T06J4uhTwsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEAiG/hAl69bJYeqFbi5lUElmCWHGatCnAz2zAj +ddGi8DHL3sEDgzSYy7UUiDKEhJvLOoJwq+m7RfCr3LyEXCCw3F0CLMAfXa+u5ZmQ +sNJiz43dvZ/TvBxMuyZNbqzspVVD0t8li+bmDMqAjVVfgmtV9fVygBMYlLzCmjvF +3ZmkU+1yuQhgTMe8/WY90vaMzB7pciScaVXVjF/AcCWu8NC6cG6rTbRtBUVgNb4I +NuHTc72XfbgvhGeVGstpvnJlFqCUvWprgQeglJkZSxtgmrwUC1PSzeUPlIbuKhuy +i+ZCHmiIPrBNMbwm56IspNVHwrDYC9BAm2MEn0VKgm0YdMrXyg== +-----END CERTIFICATE----- diff --git a/certificates/master.p12 b/certificates/master.p12 new file mode 100644 index 00000000..daf24358 Binary files /dev/null and b/certificates/master.p12 differ diff --git a/certificates/testcloud1/authorization.p12 b/certificates/testcloud1/authorization.p12 new file mode 100644 index 00000000..bcb8f5a4 Binary files /dev/null and b/certificates/testcloud1/authorization.p12 differ diff --git a/certificates/testcloud1/authorization.pub b/certificates/testcloud1/authorization.pub new file mode 100644 index 00000000..493666c8 --- /dev/null +++ b/certificates/testcloud1/authorization.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApOc3Yju2sak4Y/gkYihw +ajmTVCyx+DMUiuo1GX6AbrcrOjLqbdgHM5YFk31tCfRX3kjF/IS70GchZ9Qq1t/M +Ixu65Uf0Y7M7+mv27WcbgzvUY7mq6YrFgNp8kuS/A8QSa724cS2krRdk477kRqOi +F/h6aEWUSNNJlg8NkcA/EK8DiVvPaMAqNNY/eFgRvCSmhBo2iwwt5TxP2pftZ5DW +zXsogsLlb7bWIm79/cdxC+xeLRMt7lty8fM/Cyq3RKn5k8ldUp5396hxaM3MqBok +lSXNQeIMOzor5YIdlDLhyHmg1Oxe5oXTjpVagn1W8dUPCoPYnjvUhUZ/4ue9XJuQ +LQIDAQAB +-----END PUBLIC KEY----- diff --git a/certificates/testcloud1/event_handler.p12 b/certificates/testcloud1/event_handler.p12 new file mode 100644 index 00000000..b7336e1c Binary files /dev/null and b/certificates/testcloud1/event_handler.p12 differ diff --git a/certificates/testcloud1/gatekeeper.p12 b/certificates/testcloud1/gatekeeper.p12 new file mode 100644 index 00000000..87782d2f Binary files /dev/null and b/certificates/testcloud1/gatekeeper.p12 differ diff --git a/certificates/testcloud1/gateway.p12 b/certificates/testcloud1/gateway.p12 new file mode 100644 index 00000000..e75ab482 Binary files /dev/null and b/certificates/testcloud1/gateway.p12 differ diff --git a/certificates/testcloud1/orchestrator.p12 b/certificates/testcloud1/orchestrator.p12 new file mode 100644 index 00000000..2911609d Binary files /dev/null and b/certificates/testcloud1/orchestrator.p12 differ diff --git a/certificates/testcloud1/service_registry_sql.p12 b/certificates/testcloud1/service_registry_sql.p12 new file mode 100644 index 00000000..04eda620 Binary files /dev/null and b/certificates/testcloud1/service_registry_sql.p12 differ diff --git a/certificates/testcloud1/testcloud1.p12 b/certificates/testcloud1/testcloud1.p12 new file mode 100644 index 00000000..c981877b Binary files /dev/null and b/certificates/testcloud1/testcloud1.p12 differ diff --git a/certificates/testcloud1/truststore.p12 b/certificates/testcloud1/truststore.p12 new file mode 100644 index 00000000..aa386178 Binary files /dev/null and b/certificates/testcloud1/truststore.p12 differ diff --git a/certificates/testcloud2/authorization.p12 b/certificates/testcloud2/authorization.p12 new file mode 100644 index 00000000..2df97ce5 Binary files /dev/null and b/certificates/testcloud2/authorization.p12 differ diff --git a/certificates/testcloud2/authorization.pub b/certificates/testcloud2/authorization.pub new file mode 100644 index 00000000..55265b07 --- /dev/null +++ b/certificates/testcloud2/authorization.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmIyFSDfvJu4JuNtloPnA +jHSjE4xkkPWCVPUpmK0h9GNdwMRC0WeR1orXp3a8CBjB7f6kpTA3OsdaOuzdfdLt +0zhzPRHa7mSM/4UYwy+/5sp49sPCjIutDBIk3X0NSAAp7g859sSVoVjCBo358z83 +QrQ+049IPk9sYYQL8CCfLgITAlnUnx2oos++oMlvXyiekHmpmRObIxD6+mu9Psta +9U5QauWqv0lTniJm0QWUDKn6XdQAJAIV+tASn0kN4L0CfdLWbZoy99B2OAQ1JXxn +yjELR6jFNTXqJ6F1+7inoirNMlrCHAVFKO6w5fH1mW3iRQu2eFcnXV08/nOdTPzq +fQIDAQAB +-----END PUBLIC KEY----- diff --git a/certificates/testcloud2/event_handler.p12 b/certificates/testcloud2/event_handler.p12 new file mode 100644 index 00000000..42ac3c8e Binary files /dev/null and b/certificates/testcloud2/event_handler.p12 differ diff --git a/certificates/testcloud2/gatekeeper.p12 b/certificates/testcloud2/gatekeeper.p12 new file mode 100644 index 00000000..ee984184 Binary files /dev/null and b/certificates/testcloud2/gatekeeper.p12 differ diff --git a/certificates/testcloud2/gateway.p12 b/certificates/testcloud2/gateway.p12 new file mode 100644 index 00000000..a719f54b Binary files /dev/null and b/certificates/testcloud2/gateway.p12 differ diff --git a/certificates/testcloud2/orchestrator.p12 b/certificates/testcloud2/orchestrator.p12 new file mode 100644 index 00000000..5ff6d3bb Binary files /dev/null and b/certificates/testcloud2/orchestrator.p12 differ diff --git a/certificates/testcloud2/service_registry_sql.p12 b/certificates/testcloud2/service_registry_sql.p12 new file mode 100644 index 00000000..8ebcbb31 Binary files /dev/null and b/certificates/testcloud2/service_registry_sql.p12 differ diff --git a/certificates/testcloud2/testcloud2.p12 b/certificates/testcloud2/testcloud2.p12 new file mode 100644 index 00000000..0d97c27e Binary files /dev/null and b/certificates/testcloud2/testcloud2.p12 differ diff --git a/certificates/testcloud2/truststore.p12 b/certificates/testcloud2/truststore.p12 new file mode 100644 index 00000000..363b824f Binary files /dev/null and b/certificates/testcloud2/truststore.p12 differ diff --git a/core-common/pom.xml b/core-common/pom.xml new file mode 100644 index 00000000..3fcf840c --- /dev/null +++ b/core-common/pom.xml @@ -0,0 +1,323 @@ + + + + 4.0.0 + + + eu.arrowhead + core + ${revision} + + + arrowhead-core-common + jar + + + + + 1.8 + 1.8 + 3.8.0 + 2.10 + 3.0.2 + 2.7 + + + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + com.zaxxer + HikariCP + + + + io.swagger.core.v3 + swagger-jaxrs2 + + + + javax.servlet + javax.servlet-api + + + + log4j + log4j + + + + org.glassfish.jersey.containers + jersey-container-grizzly2-http + + + + org.glassfish.jersey.containers + jersey-container-servlet-core + + + + org.glassfish.jersey.ext + jersey-bean-validation + + + + org.glassfish.jersey.inject + jersey-hk2 + + + + com.sun.xml.bind + jaxb-impl + + + + com.sun.istack + istack-commons-runtime + + + + com.sun.xml.txw2 + txw2 + + + + org.hibernate + hibernate-core + + + + org.hibernate + hibernate-hikaricp + + + + org.hibernate + hibernate-java8 + + + + + + mysql + mysql-connector-java + + + + + + org.slf4j + slf4j-log4j12 + + + + + org.eclipse.milo + sdk-client + 0.3.0 + + + + org.eclipse.milo + sdk-server + 0.3.0 + + + + com.google.guava + guava + 27.1-jre + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + + default-compile + + true + true + + ${maven.compiler.target} + ${maven.compiler.source} + + + + + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + copy-dependencies + package + + copy-dependencies + + + + ${basedir}/target/lib/ + + + + + + jdeb + org.vafer + 1.7 + + + package + + jdeb + + + true + [YYMMddHHmm].${git.commit.id.abbrev} + target/${project.artifactId}_${revision}.deb + true + ${basedir}/src/deb/control + + + + file + ${project.build.directory}/${project.build.finalName}.jar + + perm + /usr/share/arrowhead/lib + + + + + link + /usr/share/arrowhead/lib/${project.artifactId}.jar + /usr/share/arrowhead/lib/${project.build.finalName}.jar + + + + file + ${project.basedir}/src/deb/ahconf.sh + + perm + /usr/share/arrowhead/conf + + + + + file + ${project.basedir}/src/deb/ah_gen_system.sh + ah_gen_system + + perm + /usr/bin + 775 + + + + + file + ${project.basedir}/src/deb/ah_gen_cloud.sh + ah_gen_cloud + + perm + /usr/bin + 775 + + + + + file + ${project.basedir}/src/deb/ah_add_neighbor.sh + ah_add_neighbor + + perm + /usr/bin + 775 + + + + + file + ${project.basedir}/src/deb/ah_gen_quickstart.sh + ah_gen_quickstart + + perm + /usr/bin + 775 + + + + + file + ${project.basedir}/src/deb/ah_mysql_public.sh + ah_mysql_public + + perm + /usr/bin + 775 + + + + + ${project.build.directory}/lib + directory + *.jar + + perm + /usr/share/arrowhead/lib + + + + + + + + + + + + \ No newline at end of file diff --git a/core-common/src/deb/ah_add_neighbor.sh b/core-common/src/deb/ah_add_neighbor.sh new file mode 100644 index 00000000..74b9cc83 --- /dev/null +++ b/core-common/src/deb/ah_add_neighbor.sh @@ -0,0 +1,37 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +. /usr/share/arrowhead/conf/ahconf.sh + +if [ "$#" -lt 4 ]; then + echo "Syntax: ${0} OPERATOR CLOUD_NAME HOST AUTH_INFO" + exit 1 +fi + +AH_OPERATOR=${1} +CLOUD_NAME=${2} +CLOUD_HOST=${3} +CLOUD_64PUB=${4} + +echo "Registering cloud '${CLOUD_NAME}' in database" >&2 +mysql --defaults-extra-file="${AH_MYSQL_CONF}" -u arrowhead arrowhead <&2 +systemctl restart arrowhead-gateway.service arrowhead-gatekeeper.service diff --git a/core-common/src/deb/ah_gen_cloud.sh b/core-common/src/deb/ah_gen_cloud.sh new file mode 100644 index 00000000..ef21606c --- /dev/null +++ b/core-common/src/deb/ah_gen_cloud.sh @@ -0,0 +1,64 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +. /usr/share/arrowhead/conf/ahconf.sh + +if [ "$#" -lt 2 ]; then + echo "Syntax: ${0} CLOUD_NAME HOST" + exit 1 +fi + +CLOUD_NAME=${1} +CLOUD_HOST=${2} + +CLOUD_STORE="${AH_CLOUDS_DIR}/${CLOUD_NAME}.p12" +CLOUD_INIT="${AH_CLOUDS_DIR}/${CLOUD_NAME}.sh" + +if [ ! -f "${AH_CONF_DIR}/master.p12" ]; then + echo "Keystore for master certificate not found." >&2 + echo "Generating new clouds only works when existing cloud have been installed in detached mode." >&2 + exit 1; +fi + +if [ -f "${AH_CLOUDS_DIR}/${CLOUD_NAME}.p12" ]; then + echo "'${CLOUD_NAME}' already exist, please remove cloud or use a different name." >&2 + exit 1; +fi + +echo "Generating certificate for '${CLOUD_NAME}'" >&2 +ah_cert_signed "${AH_CLOUDS_DIR}" ${CLOUD_NAME} "${CLOUD_NAME}.${AH_OPERATOR}.arrowhead.eu" ${AH_CONF_DIR} master + +CLOUD_64PUB=$(\ + sudo keytool -exportcert -rfc -keystore "${CLOUD_STORE}" -storepass ${AH_PASS_CERT} -v -alias "${CLOUD_NAME}" \ + | openssl x509 -pubkey -noout \ + | sed '1d;$d' \ + | tr -d '\n'\ +) + +# This restarts gateway and gatekeeper implicitly +ah_add_neighbor ${AH_OPERATOR} ${CLOUD_NAME} ${CLOUD_HOST} ${CLOUD_64PUB} + +echo >&2 +echo "Certificate stored in '${AH_CLOUDS_DIR}'" >&2 +echo "Password for certificate stores: ${AH_PASS_CERT}" >&2 + +db_get arrowhead-gatekeeper/address; OWN_HOST="$RET" + +OWN_64PUB=$(\ + sudo keytool -exportcert -rfc -keystore "${AH_SYSTEMS_DIR}/gatekeeper/gatekeeper.p12" -storepass ${AH_PASS_CERT} -v -alias "gatekeeper" \ + | openssl x509 -pubkey -noout \ + | sed '1d;$d' \ + | tr -d '\n'\ +) + +echo "!/bin/sh" > "${CLOUD_INIT}" +echo "ah_add_neighbor ${AH_OPERATOR} ${AH_CLOUD_NAME} ${OWN_HOST} ${OWN_64PUB}" > "${CLOUD_INIT}" +chown :arrowhead ${CLOUD_INIT} +chmod 640 ${CLOUD_INIT} + +echo >&2 +echo "You should call '${CLOUD_INIT}' on your new cloud, after it is installed" >&2 + +echo >&2 +echo "Helper to copy required files:" >&2 +echo "sudo scp \"${CLOUD_STORE}\" \"${AH_CONF_DIR}/master.crt\" \"${CLOUD_INIT}\" ${CLOUD_HOST}:~" >&2 \ No newline at end of file diff --git a/core-common/src/deb/ah_gen_quickstart.sh b/core-common/src/deb/ah_gen_quickstart.sh new file mode 100644 index 00000000..92431bb6 --- /dev/null +++ b/core-common/src/deb/ah_gen_quickstart.sh @@ -0,0 +1,27 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +. /usr/share/arrowhead/conf/ahconf.sh + +if [ "$#" -lt 1 ]; then + echo "Syntax: ${0} HOST" + exit 1 +fi + +HOST=${1} + +ah_gen_system client1 ${HOST} 8080 +echo >&2 +ah_gen_system SecureTemperatureSensor ${HOST} 8461 IndoorTemperature +echo >&2 +echo "WARNING: No authorization/orchestration entries will be generated by this version of the script" >&2 + +# TODO Autogenerated ids +#mysql --defaults-extra-file="${AH_MYSQL_CONF}" -u arrowhead arrowhead <&2 + exit 1; +fi + +mkdir -p "${SYSTEM_DIR}" + +echo "Generating certificate for '${SYSTEM_NAME}'" >&2 +ah_cert_signed_system ${SYSTEM_NAME} + +if [ ! -z "${SERVICE}" ]; then + ah_cert_export_pub "${AH_SYSTEMS_DIR}/authorization" "authorization" "${SYSTEM_DIR}" +fi + +SYSTEM_64PUB=$(\ + sudo keytool -exportcert -rfc -keystore "${SYSTEM_STORE}" -storepass ${AH_PASS_CERT} -v -alias "${SYSTEM_NAME}" \ + | openssl x509 -pubkey -noout \ + | sed '1d;$d' \ + | tr -d '\n'\ +) + +echo "Registering system '${SYSTEM_NAME}' in database" >&2 +db_cmd=" + LOCK TABLES arrowhead_system WRITE, table_generator WRITE, arrowhead_service WRITE, arrowhead_service_interfaces WRITE; + INSERT INTO arrowhead_system (id, address, authentication_info, port, system_name) + SELECT next_val, '${SYSTEM_HOST}', '${SYSTEM_64PUB}', '${PORT}', '${SYSTEM_NAME}' FROM table_generator; + UPDATE table_generator SET next_val = next_val + 1; +" + +if [ ! -z "${SERVICE}" ]; then + if [ $(mysql --defaults-extra-file="${AH_MYSQL_CONF}" -u arrowhead arrowhead -sse "SELECT EXISTS(SELECT 1 FROM arrowhead_service WHERE service_definition = '${SERVICE}')") != 1 ]; then + echo "Registering service '${SERVICE}' in database" >&2 + db_cmd="${db_cmd} + INSERT INTO arrowhead_service (id, service_definition) + SELECT next_val, '${SERVICE}' FROM table_generator; + INSERT INTO arrowhead_service_interfaces (arrowhead_service_id, interfaces) + SELECT next_val, 'JSON' FROM table_generator; + UPDATE table_generator SET next_val = next_val + 1; + " + fi +fi + +db_cmd="${db_cmd} UNLOCK TABLES;" + +mysql --defaults-extra-file="${AH_MYSQL_CONF}" -u arrowhead arrowhead -e "${db_cmd}" + +if [ -z "${SERVICE}" ]; then + echo "Generating consumer-only properties file" >&2 + echo "" > "${SYSTEM_DIR}/default.conf" +else + echo "Generating full provider properties file" >&2 + echo " +###################### +# MANDATORY PARAMETERS +###################### + +# Parameters of the offered service which will be registered in the SR +service_name=${SERVICE} +# Resource path where the service will be offered (address:port/service_uri) +service_uri=${SERVICE} +# Interfaces the service is offered through (comma separated list) +interfaces=JSON, XML +# Metadata key-value pairs (key1-value1, key2-value2) +metadata=unit-celsius + +# Provider system name to be registered into the SR +insecure_system_name=${SYSTEM_NAME} +secure_system_name=${SYSTEM_NAME} +fi +" > "${SYSTEM_DIR}/default.conf" +fi + +echo " +################################################ +# NON-MANDATORY PARAMETERS (defaults are showed) +################################################ + +# Webserver parameters +address=0.0.0.0 +insecure_port=8460 +secure_port=8461 + +# Service Registry +sr_address=0.0.0.0 # ${ARROWHEAD_IPS} +sr_insecure_port=8442 +sr_secure_port=8443 + +# Orchestrator +orch_address=0.0.0.0 # ${ARROWHEAD_IPS} +orch_insecure_port=8440 +orch_secure_port=8441 + +##################################################################### +# MANDATORY PARAMETERS ONLY IN SECURE MODE (invoked w/ -tls argument) +##################################################################### + +# Certificate related paths and passwords +keystore=${SYSTEM_STORE} +keystorepass=${AH_PASS_CERT} +keypass=${AH_PASS_CERT} +truststore=${SYSTEM_STORE} +truststorepass=${AH_PASS_CERT} +authorization_cert=${SYSTEM_DIR}/authorization.pub +" >> "${SYSTEM_DIR}/default.conf" + +chown root:arrowhead "${SYSTEM_DIR}/default.conf" +chmod 640 "${SYSTEM_DIR}/default.conf" + +echo >&2 +echo "System files stored in '${SYSTEM_DIR}'" >&2 +echo "Please verify that 'default.conf' is correct" >&2 diff --git a/core-common/src/deb/ah_mysql_public.sh b/core-common/src/deb/ah_mysql_public.sh new file mode 100644 index 00000000..41438bab --- /dev/null +++ b/core-common/src/deb/ah_mysql_public.sh @@ -0,0 +1,38 @@ +#!/bin/bash -e + +. /usr/share/debconf/confmodule +. /usr/share/arrowhead/conf/ahconf.sh + +do_mysql_conf() { + BAK="$(dirname "$1")/$(basename "$1").ah_bak" + + if [[ -e "${BAK}" ]]; then + echo "'${BAK}' exists, please remove it and try again" >&2 + exit 1 + fi + + cp -v "$1" "${BAK}" + + echo "Setting bind-address = 0.0.0.0 in '$1'" >&2 + sed -i 's/^\(bind-address[ \t]*=[ \t]*\).*$/\10.0.0.0/' "$1" +} + +MYSQL_CONF_UBUNTU="/etc/mysql/mysql.conf.d/mysqld.cnf" +MARIADB_CONF_PI="/etc/mysql/mariadb.conf.d/50-server.cnf" + +if [[ -r "${MYSQL_CONF_UBUNTU}" ]]; then + do_mysql_conf "${MYSQL_CONF_UBUNTU}" +elif [[ -r "${MARIADB_CONF_PI}" ]]; then + do_mysql_conf "${MARIADB_CONF_PI}" +fi + +if [ $(mysql --defaults-extra-file="${AH_MYSQL_CONF}" -u arrowhead -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'arrowhead' AND host = '%')") != 1 ]; then + mysql -e "CREATE USER arrowhead@'%' IDENTIFIED BY '${AH_PASS_DB}';" + mysql -e "GRANT ALL PRIVILEGES ON arrowhead.* TO arrowhead@'%';" + mysql -e "FLUSH PRIVILEGES;" +fi +systemctl restart mysql + +echo >&2 +echo "Use MySQL user 'arrowhead' with password '${AH_PASS_DB}'" >&2 +echo >&2 diff --git a/core-common/src/deb/ahconf.sh b/core-common/src/deb/ahconf.sh new file mode 100644 index 00000000..5133baea --- /dev/null +++ b/core-common/src/deb/ahconf.sh @@ -0,0 +1,287 @@ +#!/bin/sh + +AH_CONF_DIR="/etc/arrowhead" +AH_CLOUDS_DIR="${AH_CONF_DIR}/clouds" +AH_SYSTEMS_DIR="${AH_CONF_DIR}/systems" +AH_MYSQL_CONF="${AH_CONF_DIR}/mysql.cnf" + +db_get arrowhead-core-common/mysql_password; AH_PASS_DB=$RET +db_get arrowhead-core-common/cert_password; AH_PASS_CERT=$RET +db_get arrowhead-core-common/cloudname; AH_CLOUD_NAME=$RET +db_get arrowhead-core-common/operator; AH_OPERATOR=$RET +db_get arrowhead-core-common/company; AH_COMPANY=$RET +db_get arrowhead-core-common/country; AH_COUNTRY=$RET + +ah_cert () { + dst_path=${1} + dst_name=${2} + cn=${3} + + file="${dst_path}/${dst_name}.p12" + + # The command has been renamed in newer versions of keytool + gen_cmd="-genkeypair" + keytool ${gen_cmd} --help >/dev/null 2>&1 || gen_cmd='-genkey' + + if [ ! -f "${file}" ]; then + keytool ${gen_cmd} \ + -alias ${dst_name} \ + -keyalg RSA \ + -keysize 2048 \ + -dname "CN=${cn}, OU=${AH_OPERATOR}, O=${AH_COMPANY}, C=${AH_COUNTRY}" \ + -validity 3650 \ + -keypass ${AH_PASS_CERT} \ + -keystore ${file} \ + -storepass ${AH_PASS_CERT} \ + -storetype PKCS12 \ + -ext BasicConstraints:"Subject is a CA\nPath Length Constraint: None" + + chown :arrowhead ${file} + chmod 640 ${file} + fi +} + +ah_cert_export () { + src_path=${1} + dst_name=${2} + dst_path=${3} + + src_file="${src_path}/${dst_name}.p12" + dst_file="${dst_path}/${dst_name}.crt" + + if [ ! -f "${dst_file}" ]; then + keytool -exportcert \ + -rfc \ + -alias ${dst_name} \ + -storepass ${AH_PASS_CERT} \ + -keystore ${src_file} \ + | openssl x509 \ + -out ${dst_file} + + chown :arrowhead ${dst_file} + chmod 640 ${dst_file} + fi +} + +ah_cert_export_pub () { + src_path=${1} + dst_name=${2} + dst_path=${3} + + src_file="${src_path}/${dst_name}.p12" + dst_file="${dst_path}/${dst_name}.pub" + + if [ ! -f "${dst_file}" ]; then + keytool -exportcert \ + -rfc \ + -alias ${dst_name} \ + -storepass ${AH_PASS_CERT} \ + -keystore ${src_file} \ + | openssl x509 \ + -out ${dst_file} \ + -noout \ + -pubkey + + chown :arrowhead ${dst_file} + chmod 640 ${dst_file} + fi +} + +ah_cert_import () { + src_path=${1} + src_name=${2} + dst_path=${3} + dst_name=${4} + + src_file="${src_path}/${src_name}.crt" + dst_file="${dst_path}/${dst_name}.p12" + + keytool -import \ + -trustcacerts \ + -file ${src_file} \ + -alias ${src_name} \ + -keystore ${dst_file} \ + -keypass ${AH_PASS_CERT} \ + -storepass ${AH_PASS_CERT} \ + -storetype PKCS12 \ + -noprompt +} + +ah_cert_signed () { + dst_path=${1} + dst_name=${2} + cn=${3} + src_path=${4} + src_name=${5} + + src_file="${src_path}/${src_name}.p12" + dst_file="${dst_path}/${dst_name}.p12" + + if [ ! -f "${dst_file}" ]; then + ah_cert ${dst_path} ${dst_name} ${cn} + + keytool -export \ + -alias ${src_name} \ + -storepass ${AH_PASS_CERT} \ + -keystore ${src_file} \ + | keytool -import \ + -trustcacerts \ + -alias ${src_name} \ + -keystore ${dst_file} \ + -keypass ${AH_PASS_CERT} \ + -storepass ${AH_PASS_CERT} \ + -storetype PKCS12 \ + -noprompt + + keytool -certreq \ + -alias ${dst_name} \ + -keypass ${AH_PASS_CERT} \ + -keystore ${dst_file} \ + -storepass ${AH_PASS_CERT} \ + | keytool -gencert \ + -alias ${src_name} \ + -keypass ${AH_PASS_CERT} \ + -keystore ${src_file} \ + -storepass ${AH_PASS_CERT} \ + | keytool -importcert \ + -alias ${dst_name} \ + -keypass ${AH_PASS_CERT} \ + -keystore ${dst_file} \ + -storepass ${AH_PASS_CERT} \ + -noprompt + fi +} + +ah_cert_signed_system () { + name=${1} + + path="${AH_SYSTEMS_DIR}/${name}" + file="${path}/${name}.p12" + + if [ ! -f "${file}" ]; then + ah_cert_signed \ + "${path}" \ + ${name} \ + "${name}.${AH_CLOUD_NAME}.${AH_OPERATOR}.arrowhead.eu" \ + ${AH_CLOUDS_DIR} \ + ${AH_CLOUD_NAME} + + ah_cert_import "${AH_CONF_DIR}" "master" "${path}" ${name} + fi +} + +ah_cert_trust () { + dst_path=${1} + src_path=${2} + src_name=${3} + + src_file="${src_path}/${src_name}.p12" + dst_file="${dst_path}/truststore.p12" + + if [ ! -f "${dst_file}" ]; then + keytool -export \ + -alias ${src_name} \ + -storepass ${AH_PASS_CERT} \ + -keystore ${src_file} \ + | keytool -import \ + -trustcacerts \ + -alias ${src_name} \ + -keystore ${dst_file} \ + -keypass ${AH_PASS_CERT} \ + -storepass ${AH_PASS_CERT} \ + -storetype PKCS12 \ + -noprompt + + chown :arrowhead ${dst_file} + chmod 640 ${dst_file} + fi +} + +ah_db_user () { + if [ ! -f "${AH_MYSQL_CONF}" ]; then + touch "${AH_MYSQL_CONF}" + chmod 0600 "${AH_MYSQL_CONF}" + cat >"${AH_MYSQL_CONF}" </dev/null 2>/dev/null; then + if mysql -u root -e "SHOW DATABASES" >/dev/null 2>/dev/null; then + mysql -u root <"${OPT_FILE}" <${file} +# Define the root logger with appender file +log4j.rootLogger=INFO, DB, FILE + +# Database related config +# Define the DB appender +log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender +# Set Database URL +log4j.appender.DB.URL=jdbc:mysql://127.0.0.1:3306/arrowhead +# Set database user name and password +log4j.appender.DB.user=arrowhead +log4j.appender.DB.password=${AH_PASS_DB} +# Set the SQL statement to be executed. +log4j.appender.DB.sql=INSERT INTO logs(id, date, origin, level, message) VALUES(DEFAULT,'%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m') +# Define the layout for file appender +log4j.appender.DB.layout=org.apache.log4j.PatternLayout +# Disable Hibernate verbose logging +log4j.logger.org.hibernate=fatal + +# File related config +# Define the file appender +log4j.appender.FILE=org.apache.log4j.FileAppender +# Set the name of the file +log4j.appender.FILE.File=/var/log/arrowhead/${system_name}.log +# Set the immediate flush to true (default) +log4j.appender.FILE.ImmediateFlush=true +# Set the threshold to debug mode +log4j.appender.FILE.Threshold=debug +# Set the append to false, overwrite +log4j.appender.FILE.Append=false +# Define the layout for file appender +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss}, %C, %p, %m%n +EOF + chown root:arrowhead ${file} + chmod 640 ${file} + fi +} diff --git a/core-common/src/deb/control/config b/core-common/src/deb/control/config new file mode 100644 index 00000000..fe6a85ac --- /dev/null +++ b/core-common/src/deb/control/config @@ -0,0 +1,161 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +. /usr/share/arrowhead/conf/ahconf.sh + +if logname 2>/dev/null; then + USER=$(logname) + HOME=$(eval echo "~$USER") +else + USER="root" + HOME="/root" +fi + +# Get installation type +db_input high arrowhead-core-common/install_type || true +db_go || true +db_get arrowhead-core-common/install_type; INSTALL_TYPE="$RET" + +# User dialog +case ${INSTALL_TYPE} in + "Authorized" ) + if [ ! -f ${AH_CLOUDS_DIR}/${AH_CLOUD_NAME}.p12 ]; then + # Set default path for cloud certificate + db_get arrowhead-core-common/cloud_cert; + if [ -z "$RET" ]; then + db_set arrowhead-core-common/cloud_cert "${HOME}/cloud.p12" + fi + + # Set default path for master certificate + db_get arrowhead-core-common/master_cert; master_cert=$RET + if [ -z "$RET" ]; then + db_set arrowhead-core-common/master_cert "${HOME}/master.crt" + fi + + # User dialogs + db_input critical arrowhead-core-common/cloud_cert || true + db_input critical arrowhead-core-common/cloud_alias || true + db_input critical arrowhead-core-common/cloud_password || true + db_input critical arrowhead-core-common/master_cert || true + fi + ;; + + "Detached" ) + # User dialogs + db_input high arrowhead-core-common/cloudname || true + db_input high arrowhead-core-common/operator || true + db_input high arrowhead-core-common/company || true + db_input high arrowhead-core-common/country || true + ;; + + * ) + echo "Unexpected install type" + exit 1 + ;; +esac + +# Get certificate password +if [ -z "${AH_PASS_CERT}" ]; then + db_input high arrowhead-core-common/cert_password || true +fi + +# Get database password +if [ -z "${AH_PASS_DB}" ]; then + db_input high arrowhead-core-common/mysql_password || true +fi + +# Run dialogs +db_go || true + +# Create user +adduser --system --no-create-home --group arrowhead + +# Create directories +mkdir -p ${AH_CLOUDS_DIR}/ +if [ ! -d /var/log/arrowhead ]; then + mkdir -p /var/log/arrowhead + chown arrowhead:adm /var/log/arrowhead + chmod 750 /var/log/arrowhead +fi + +# Reload global variables +. /usr/share/arrowhead/conf/ahconf.sh + +# Generate certificate password (if required) +if [ -z "${AH_PASS_CERT}" ]; then + PASS="$(openssl rand -base64 12)" + db_set arrowhead-core-common/cert_password ${PASS} +fi + +# Generate database password (if required) +if [ -z "${AH_PASS_DB}" ]; then + PASS="$(openssl rand -base64 12)" + db_set arrowhead-core-common/mysql_password ${PASS} +fi + +case ${INSTALL_TYPE} in + "Authorized" ) + # Get additional variables + db_get arrowhead-core-common/cloud_cert; cloud_cert=$RET + db_get arrowhead-core-common/cloud_alias; cloud_alias=$RET + db_get arrowhead-core-common/cloud_password; cloud_password=$RET + db_get arrowhead-core-common/master_cert; master_cert=$RET + + # Extract data from CN field + subject=$(keytool -list -keystore "${cloud_cert}" -storepass "${cloud_password}" -v -alias "${cloud_alias}" | grep "Owner:" | head -n1 | sed 's|Owner: ||') + cloud_name=$(echo "${subject}" | sed 's|^.*CN=\([^.]*\)\.\([^.]*\)\.\([^.]*\)\.\([^,]*\),.*$|\1|') + operator=$(echo "${subject}" | sed 's|^.*CN=\([^.]*\)\.\([^.]*\)\.\([^.]*\)\.\([^,]*\),.*$|\2|') + company=$(echo "${subject}" | sed 's|^.*CN=\([^.]*\)\.\([^.]*\)\.\([^.]*\)\.\([^,]*\),.*$|\3|') + country=$(echo "${subject}" | sed 's|^.*CN=\([^.]*\)\.\([^.]*\)\.\([^.]*\)\.\([^,]*\),.*$|\4|') + + # Store data + db_set arrowhead-core-common/cloudname ${cloud_name} + db_set arrowhead-core-common/operator ${operator} + db_set arrowhead-core-common/company ${company} + db_set arrowhead-core-common/country ${country} + ;; +esac + +# Reload global variables +. /usr/share/arrowhead/conf/ahconf.sh + +# Do actions based on install type +case ${INSTALL_TYPE} in + "Authorized" ) + if [ ! -f ${AH_CLOUDS_DIR}/${AH_CLOUD_NAME}.p12 ]; then + # Store cloud certificate + keytool -importkeystore \ + -srckeypass ${cloud_password} \ + -destkeypass ${AH_PASS_CERT} \ + -srcstorepass ${cloud_password} \ + -deststorepass ${AH_PASS_CERT} \ + -srcalias ${cloud_alias} \ + -destalias ${AH_CLOUD_NAME} \ + -srckeystore "${cloud_cert}" \ + -destkeystore "${AH_CLOUDS_DIR}/${AH_CLOUD_NAME}.p12" \ + -deststoretype PKCS12 + chown :arrowhead "${AH_CLOUDS_DIR}/${AH_CLOUD_NAME}.p12" + chmod 640 "${AH_CLOUDS_DIR}/${AH_CLOUD_NAME}.p12" + + # Store master certificate + cp "${master_cert}" "${AH_CONF_DIR}/master.crt" + chown :arrowhead "${AH_CONF_DIR}/master.crt" + chmod 640 "${AH_CONF_DIR}/master.crt" + fi + ;; + + "Detached" ) + # Generate master and cloud certificates + ah_cert ${AH_CONF_DIR} master "arrowhead.eu" + ah_cert_export "${AH_CONF_DIR}" master "${AH_CONF_DIR}" + ah_cert_signed ${AH_CLOUDS_DIR} ${AH_CLOUD_NAME} "${AH_CLOUD_NAME}.${AH_OPERATOR}.arrowhead.eu" ${AH_CONF_DIR} master + ;; + + * ) + echo "Unexpected install type" + exit 1 + ;; +esac + +# Trust the cloud certificate +ah_cert_trust ${AH_CONF_DIR} ${AH_CLOUDS_DIR} ${AH_CLOUD_NAME} diff --git a/core-common/src/deb/control/control b/core-common/src/deb/control/control new file mode 100644 index 00000000..76a9ee34 --- /dev/null +++ b/core-common/src/deb/control/control @@ -0,0 +1,10 @@ +Package: [[name]] +Version: [[version]] +Section: contrib/java +Priority: optional +Architecture: all +Maintainer: Thomas Pedersen +Homepage: http://www.arrowhead.eu +Description: Arrowhead Common +Distribution: development +Depends: java-runtime-headless, openssl, debconf diff --git a/core-common/src/deb/control/postinst b/core-common/src/deb/control/postinst new file mode 100644 index 00000000..b52d9c7a --- /dev/null +++ b/core-common/src/deb/control/postinst @@ -0,0 +1,41 @@ +#!/bin/sh +# postinst script for arrowhead-core-common +# +# see: dh_installdeb(1) + +set -e + +. /usr/share/debconf/confmodule + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/core-common/src/deb/control/postrm b/core-common/src/deb/control/postrm new file mode 100644 index 00000000..a3a10bc3 --- /dev/null +++ b/core-common/src/deb/control/postrm @@ -0,0 +1,91 @@ +#!/bin/sh +# postrm script for arrowhead-core-common +# +# see: dh_installdeb(1) + +set -e + +. /usr/share/debconf/confmodule + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + db_get arrowhead-core-common/cloudname; AH_CLOUD_NAME=$RET + AH_CONF_DIR="/etc/arrowhead" + AH_CLOUDS_DIR="${AH_CONF_DIR}/clouds" + AH_SYSTEMS_DIR="${AH_CONF_DIR}/systems" + AH_MYSQL_CONF="${AH_CONF_DIR}/mysql.cnf" + + if command -v mysql 2>&1 >/dev/null; then + if mysql -u root -e "SHOW DATABASES" >/dev/null 2>/dev/null; then + mysql -u root -p <"${OPT_FILE}" </dev/null || true + rmdir ${AH_SYSTEMS_DIR} 2>/dev/null || true + rmdir ${AH_CONF_DIR} 2>/dev/null || true + rmdir /var/log/arrowhead 2>/dev/null || true + + deluser arrowhead || true + db_purge + + if [ -d ${AH_CONF_DIR} ]; then + echo "Directory '${AH_CONF_DIR}' is not empty, please remove manually" >&2 + fi + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/core-common/src/deb/control/preinst b/core-common/src/deb/control/preinst new file mode 100644 index 00000000..c3054905 --- /dev/null +++ b/core-common/src/deb/control/preinst @@ -0,0 +1,35 @@ +#!/bin/sh +# preinst script for arrowhead-core-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/core-common/src/deb/control/prerm b/core-common/src/deb/control/prerm new file mode 100644 index 00000000..56d6beab --- /dev/null +++ b/core-common/src/deb/control/prerm @@ -0,0 +1,38 @@ +#!/bin/sh +# prerm script for arrowhead-core-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + remove|upgrade|deconfigure) + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/core-common/src/deb/control/templates b/core-common/src/deb/control/templates new file mode 100644 index 00000000..63da5ac4 --- /dev/null +++ b/core-common/src/deb/control/templates @@ -0,0 +1,63 @@ +Template: arrowhead-core-common/install_type +Type: select +Choices: Detached, Authorized +Description: Do you want to perform (1) a detached, standalone install or (2) authorized install with externally issued cloud cert? + +Template: arrowhead-core-common/cloud_cert +Type: string +Default: +Description: Enter the path for the externally issued cloud cert + +Template: arrowhead-core-common/master_cert +Type: string +Default: +Description: Enter the path for the externally issued cloud cert + +Template: arrowhead-core-common/cloud_password +Type: password +Default: +Description: Enter the password for the externally issued cloud cert + +Template: arrowhead-core-common/cloud_alias +Type: string +Default: +Description: Enter the alias for the externally issued cloud cert + +Template: arrowhead-core-common/cloudname +Type: string +Default: testcloud +Description: The name of the Arrowhead local cloud + +Template: arrowhead-core-common/operator +Type: string +Default: user +Description: The name of the operator of the local cloud + +Template: arrowhead-core-common/company +Type: string +Default: arrowhead +Description: The name of your company + +Template: arrowhead-core-common/country +Type: string +Default: eu +Description: The name of your country + +Template: arrowhead-core-common/mysql_password_root +Type: password +Default: +Description: Password for the MySQL root user. Note that password is stored by debconf in '/var/cache/debconf/passwords.dat'. + +Template: arrowhead-core-common/mysql_password +Type: password +Default: +Description: Password for the generated MySQL arrowhead user (leave empty for random). Note that password is stored by debconf in '/var/cache/debconf/passwords.dat'. + +Template: arrowhead-core-common/cert_password +Type: password +Default: +Description: Password for the certificates and stores (leave empty for random). Note that password is stored by debconf in '/var/cache/debconf/passwords.dat'. + +Template: arrowhead-core-common/reconf_warning +Type: note +Description: You have to manually reconfigure all Arrowhead systems when changing passwords diff --git a/core-common/src/main/java/eu/arrowhead/common/ArrowheadMain.java b/core-common/src/main/java/eu/arrowhead/common/ArrowheadMain.java new file mode 100644 index 00000000..7df6b351 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/ArrowheadMain.java @@ -0,0 +1,329 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import javax.net.ssl.SSLContext; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.core.UriBuilder; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; +import org.eclipse.milo.opcua.sdk.server.UaNodeManager; +import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext; +import org.glassfish.grizzly.http.server.CLStaticHttpHandler; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.ssl.SSLContextConfigurator; +import org.glassfish.grizzly.ssl.SSLContextConfigurator.GenericStoreException; +import org.glassfish.grizzly.ssl.SSLEngineConfigurator; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.server.ResourceConfig; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.ServiceRegistryEntry; +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.AuthException; +import eu.arrowhead.common.exception.ExceptionType; +import eu.arrowhead.common.misc.CoreSystem; +import eu.arrowhead.common.misc.CoreSystemService; +import eu.arrowhead.common.misc.SecurityUtils; +import eu.arrowhead.common.misc.TypeSafeProperties; +import eu.arrowhead.common.opcua.ArrowheadOpcUaServer; +//import eu.arrowhead.common.opcua.ArrowheadOpcUaServer; + +public abstract class ArrowheadMain { + + public static final List dbFields = Collections + .unmodifiableList(Arrays.asList("db_user", "db_password", "db_address")); + public static final List certFields = Collections + .unmodifiableList(Arrays.asList("keystore", "keystorepass", "keypass", "truststore", "truststorepass")); + public static final Map secureServerMetadata = Collections.singletonMap("security", "certificate"); + + protected String srBaseUri; + protected final TypeSafeProperties props = Utility.getProp(); + + private boolean daemon = false; + private CoreSystem coreSystem; + private HttpServer server; + protected ArrowheadOpcUaServer arrowheaduaserver; + private String baseUri; + private String base64PublicKey; + private int registeringTries = 1; + + private static final Logger log = Logger.getLogger(ArrowheadMain.class.getName()); + + { + DatabaseManager.init(); + PropertyConfigurator.configure(props); + } + + protected void init(CoreSystem coreSystem, String[] args, Set> classes, String[] packages) { + System.out.println("Working directory: " + System.getProperty("user.dir")); + packages = addSwaggerToPackages(packages); + this.coreSystem = coreSystem; + + boolean isSecure = false; + // Read in command line arguments + for (String arg : args) { + switch (arg) { + case "-daemon": + daemon = true; + System.out.println("Starting server as daemon!"); + break; + case "-d": + System.setProperty("debug_mode", "true"); + System.out.println("Starting server in debug mode!"); + break; + case "-tls": + System.setProperty("is_secure", "true"); + isSecure = true; + break; + } + } + + // Get the URL where the web-server will bind to + String address = props.getProperty("address", "0.0.0.0"); + int port = isSecure ? props.getIntProperty("secure_port", coreSystem.getSecurePort()) + : props.getIntProperty("insecure_port", coreSystem.getInsecurePort()); + baseUri = Utility.getUri(address, port, null, isSecure, true); + + // Start the web-server + if (isSecure) { + List allMandatoryProperties = new ArrayList<>(coreSystem.getAlwaysMandatoryFields()); + allMandatoryProperties.addAll(coreSystem.getSecureMandatoryFields()); + Utility.checkProperties(props.stringPropertyNames(), allMandatoryProperties); + startSecureServer(classes, packages); + } else { + Utility.checkProperties(props.stringPropertyNames(), coreSystem.getAlwaysMandatoryFields()); + startServer(classes, packages); + } + + // Register the core system services to the Service Registry + if (!coreSystem.equals(CoreSystem.SERVICE_REGISTRY_DNS) + && !coreSystem.equals(CoreSystem.SERVICE_REGISTRY_SQL)) { + String srAddress = props.getProperty("sr_address", "0.0.0.0"); + int srPort = isSecure + ? props.getIntProperty("sr_secure_port", CoreSystem.SERVICE_REGISTRY_SQL.getSecurePort()) + : props.getIntProperty("sr_insecure_port", CoreSystem.SERVICE_REGISTRY_SQL.getInsecurePort()); + srBaseUri = Utility.getUri(srAddress, srPort, "serviceregistry", isSecure, true); + Utility.setServiceRegistryUri(srBaseUri); + useSRService(true); + } + } + + protected void listenForInput() { + log.info(coreSystem + " startup completed."); + if (daemon) { + System.out.println("In daemon mode, process will terminate for TERM signal..."); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + System.out.println("Received TERM signal, shutting down..."); + shutdown(); + })); + } else { + System.out.println("Type \"stop\" to shutdown " + coreSystem.name() + " Server..."); + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + String input = ""; + try { + while (!input.equals("stop")) { + input = br.readLine(); + } + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + shutdown(); + } + } + + private void startServer(Set> classes, String[] packages) { + final ResourceConfig config = new ResourceConfig(); + config.registerClasses(classes); + config.packages(packages); + + URI uri = UriBuilder.fromUri(baseUri).build(); + try { + server = GrizzlyHttpServerFactory.createHttpServer(uri, config, false); + configureServer(server); + server.start(); + log.info("Started server at: " + baseUri); + System.out.println("Started insecure server at: " + baseUri); + } catch (IOException | ProcessingException e) { + throw new ServiceConfigurationError( + "Make sure you gave a valid address in the config file! (Assignable to this JVM and not in use already)", + e); + } + } + + protected void startSecureServer(Set> classes, String[] packages) { + final ResourceConfig config = new ResourceConfig(); + config.registerClasses(classes); + config.packages(packages); + + String keystorePath = props.getProperty("keystore"); + String keystorePass = props.getProperty("keystorepass"); + String keyPass = props.getProperty("keypass"); + String truststorePath = props.getProperty("truststore"); + String truststorePass = props.getProperty("truststorepass"); + + SSLContextConfigurator sslCon = new SSLContextConfigurator(); + sslCon.setKeyStoreFile(keystorePath); + sslCon.setKeyStorePass(keystorePass); + sslCon.setKeyPass(keyPass); + sslCon.setTrustStoreFile(truststorePath); + sslCon.setTrustStorePass(truststorePass); + SSLContext sslContext; + try { + sslContext = sslCon.createSSLContext(true); + } catch (GenericStoreException e) { + log.fatal("SSL Context is not valid, check the certificate or the config files!"); + throw new AuthException("SSL Context is not valid, check the certificate or the config files!", e); + } + Utility.setSSLContext(sslContext); + + KeyStore keyStore = SecurityUtils.loadKeyStore(keystorePath, keystorePass); + X509Certificate serverCert = SecurityUtils.getFirstCertFromKeyStore(keyStore); + base64PublicKey = Base64.getEncoder().encodeToString(serverCert.getPublicKey().getEncoded()); + System.out.println("Server PublicKey Base64: " + base64PublicKey); + String serverCN = SecurityUtils.getCertCNFromSubject(serverCert.getSubjectDN().getName()); + if (!SecurityUtils.isKeyStoreCNArrowheadValid(serverCN)) { + log.fatal("Server CN is not compliant with the Arrowhead cert structure"); + throw new AuthException("Server CN ( " + serverCN + + ") is not compliant with the Arrowhead cert structure, since it does not have 5 " + + "parts, or does not end with" + " \"arrowhead.eu\""); + } + log.info("Certificate of the secure server: " + serverCN); + config.property("server_common_name", serverCN); + + URI uri = UriBuilder.fromUri(baseUri).build(); + try { + server = GrizzlyHttpServerFactory.createHttpServer(uri, config, true, + new SSLEngineConfigurator(sslCon).setClientMode(false).setNeedClientAuth(true), false); + configureServer(server); + server.start(); + log.info("Started server at: " + baseUri); + System.out.println("Started secure server at: " + baseUri); + } catch (IOException | ProcessingException e) { + throw new ServiceConfigurationError( + "Make sure you gave a valid address in the config file! (Assignable to this JVM and not in use already)", + e); + } + } + + private void configureServer(HttpServer server) { + // Add swagger UI to the server + final HttpHandler httpHandler = new CLStaticHttpHandler(HttpServer.class.getClassLoader(), "/swagger/"); + server.getServerConfiguration().addHttpHandler(httpHandler, "/api"); + // Allow message payload for GET and DELETE requests - ONLY to provide custom + // error message for them + server.getServerConfiguration().setAllowPayloadForUndefinedHttpMethods(true); + } + + private void shutdown() { + useSRService(false); + DatabaseManager.closeSessionFactory(); + if (server != null) { + log.info("Stopping server at: " + baseUri); + server.shutdownNow(); + } + System.out.println(coreSystem + " Server stopped"); + System.exit(0); + } + + protected void startUaServer (String endpoint) { + try { + int port = props.getIntProperty("opcua_port", coreSystem.getOpcUaPort()); + arrowheaduaserver = ArrowheadOpcUaServer.getInstance(port, endpoint); + arrowheaduaserver.startup().get(); + final CompletableFuture future = new CompletableFuture<>(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> future.complete(null))); + // future.get(); + System.out.println("Starting an opc ua server"); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void useSRService(boolean registering) { + // Preparing the payload + final URI uri = UriBuilder.fromUri(baseUri).build(); + final boolean isSecure = uri.getScheme().equals("https"); + final String interfaceName = isSecure ? "HTTP-SECURE-JSON" : "HTTP-INSECURE-JSON"; + final ArrowheadSystem provider = new ArrowheadSystem(coreSystem.name(), uri.getHost(), uri.getPort(), + base64PublicKey); + + for (CoreSystemService service : coreSystem.getServices()) { + ArrowheadService providedService = new ArrowheadService(Utility.createSD(service.getServiceDef(), isSecure), + Collections.singleton(interfaceName), null); + if (isSecure) { + providedService.setServiceMetadata(ArrowheadMain.secureServerMetadata); + } + ServiceRegistryEntry srEntry = new ServiceRegistryEntry(providedService, provider, service.getServiceURI()); + + if (registering) { + try { + Utility.sendRequest(UriBuilder.fromUri(srBaseUri).path("register").build().toString(), "POST", + srEntry); + } catch (ArrowheadException e) { + if (e.getExceptionType() == ExceptionType.DUPLICATE_ENTRY) { + Utility.sendRequest(UriBuilder.fromUri(srBaseUri).path("remove").build().toString(), "PUT", + srEntry); + Utility.sendRequest(UriBuilder.fromUri(srBaseUri).path("register").build().toString(), "POST", + srEntry); + } else if (e.getExceptionType() == ExceptionType.UNAVAILABLE) { + System.out.println("Service Registry is unavailable at the moment, retrying in 15 seconds..."); + try { + Thread.sleep(15000); + if (registeringTries == 3) { + throw e; + } else { + registeringTries++; + // noinspection ConstantConditions + useSRService(registering); + } + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + } else { + throw new ArrowheadException(service.getServiceDef() + " service registration failed.", e); + } + } + registeringTries = 1; + } else { + Utility.sendRequest(UriBuilder.fromUri(srBaseUri).path("remove").build().toString(), "PUT", srEntry); + } + } + } + + private String[] addSwaggerToPackages(String[] packages) { + packages = Arrays.copyOf(packages, packages.length + 1); + packages[packages.length - 1] = "io.swagger.v3.jaxrs2.integration.resources"; + return packages; + } + + public ArrowheadOpcUaServer getArrowheadOpcUaServer() { + return arrowheaduaserver; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/DatabaseManager.java b/core-common/src/main/java/eu/arrowhead/common/DatabaseManager.java new file mode 100644 index 00000000..f87dda70 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/DatabaseManager.java @@ -0,0 +1,680 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common; + +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.DuplicateEntryException; +import eu.arrowhead.common.misc.TypeSafeProperties; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.Set; +import javax.persistence.PersistenceException; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.Configuration; +import org.hibernate.criterion.Disjunction; +import org.hibernate.criterion.Restrictions; +import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.query.Query; + +//NOTE should move to EntityManager from Sessions, using the JPA criteria API (Hibernate criteria will be removed in +// Hibernate 6) +public class DatabaseManager { + + private static volatile DatabaseManager instance; + private static SessionFactory sessionFactory; + private static TypeSafeProperties prop = Utility.getProp(); + private static String dbAddress; + private static String dbUser; + private static String dbPassword; + private static final Logger log = Logger.getLogger(DatabaseManager.class.getName()); + + static { + if (prop.containsKey("db_address") || prop.containsKey("log4j.appender.DB.URL")) { + if (prop.containsKey("db_address")) { + dbAddress = prop.getProperty("db_address"); + dbUser = prop.getProperty("db_user"); + dbPassword = prop.getProperty("db_password"); + } else { + dbAddress = prop.getProperty("log4j.appender.DB.URL", "jdbc:mysql://127.0.0.1:3306/log"); + dbUser = prop.getProperty("log4j.appender.DB.user", "root"); + dbPassword = prop.getProperty("log4j.appender.DB.password", "root"); + } + + try { + Configuration configuration = new Configuration().configure("hibernate.cfg.xml") + .setProperty("hibernate.connection.url", dbAddress) + .setProperty("hibernate.connection.username", dbUser) + .setProperty("hibernate.connection.password", dbPassword); + sessionFactory = configuration.buildSessionFactory(); + } catch (Exception e) { + throw new ServiceConfigurationError( + "Database connection could not be established, check default.conf/app.conf files!", e); + } + } + } + + private DatabaseManager() { + } + + public static synchronized void init() { + if (instance == null) { + instance = new DatabaseManager(); + } + } + + public static synchronized DatabaseManager getInstance() { + if (instance == null) { + instance = new DatabaseManager(); + } + return instance; + } + + private synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + Configuration configuration = new Configuration().configure("hibernate.cfg.xml") + .setProperty("hibernate.connection.url", dbAddress) + .setProperty("hibernate.connection.username", dbUser) + .setProperty("hibernate.connection.password", dbPassword); + sessionFactory = configuration.buildSessionFactory(); + } + return sessionFactory; + } + + public static synchronized void closeSessionFactory() { + if (sessionFactory != null) { + sessionFactory.close(); + } + instance = null; + } + + public Optional get(Class queryClass, long id) { + T object; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + object = session.get(queryClass, id); + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return Optional.ofNullable(object); + } + + public List get(Class queryClass, Set ids) { + List retrievedList = new ArrayList<>(); + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (Long id : ids) { + retrievedList.add(session.get(queryClass, id)); + } + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + @SuppressWarnings("unchecked") + public T get(Class queryClass, Map restrictionMap) { + T object; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + //NOTE session.createCriteria will be removed in Hibernate 6 + //noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + for (Entry entry : restrictionMap.entrySet()) { + criteria.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + } + object = (T) criteria.uniqueResult(); + transaction.commit(); + } catch (Exception e) { + log.error("get throws exception: " + e.getMessage(), e); + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return object; + } + + @SuppressWarnings("unchecked") + public List getAll(Class queryClass, Map restrictionMap) { + List retrievedList; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + //NOTE session.createCriteria will be removed in Hibernate 6 + //noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + for (Entry entry : restrictionMap.entrySet()) { + criteria.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + } + retrievedList = (List) criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); + transaction.commit(); + } catch (Exception e) { + log.error("getAll throws exception: " + e.getMessage(), e); + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + @SuppressWarnings("unchecked") + public List getAllOfEither(Class queryClass, Map restrictionMap) { + List retrievedList; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + //NOTE session.createCriteria will be removed in Hibernate 6 + //noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + Disjunction disjunction = Restrictions.disjunction(); + for (Entry entry : restrictionMap.entrySet()) { + disjunction.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + criteria.add(disjunction); + } + retrievedList = (List) criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + + @SafeVarargs + public final T save(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.save(object); + } + transaction.commit(); + } catch (PersistenceException e) { + if (transaction != null) { + transaction.rollback(); + } + Throwable cause = e.getCause(); + if (cause instanceof ConstraintViolationException && cause.getMessage().equals("could not execute statement")) { + log.error("DatabaseManager:save throws DuplicateEntryException", e); + throw new DuplicateEntryException( + "There is already an entry in the database with these parameters. Please check the unique fields of the " + + objects.getClass(), Status.BAD_REQUEST.getStatusCode(), e); + } else { + Throwable rootCause = Utility.getExceptionRootCause(e); + log.error("Unknown exception during database save: " + rootCause.getClass() + " - " + rootCause.getMessage(), + e); + throw new ArrowheadException( + "Unknown exception during database save: " + rootCause.getClass() + " - " + rootCause.getMessage(), e); + } + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + log.error("Unknown exception during database save", e); + e.printStackTrace(); + throw e; + } + + return objects[0]; + } + + + @SafeVarargs + public final T merge(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.merge(object); + } + transaction.commit(); + } catch (PersistenceException e) { + if (transaction != null) { + transaction.rollback(); + } + + Throwable cause = e.getCause(); + if (cause instanceof ConstraintViolationException && cause.getMessage().equals("could not execute statement")) { + log.error("DatabaseManager:save merge DuplicateEntryException", e); + throw new DuplicateEntryException( + "There is already an entry in the database with these parameters. Please check the unique fields of the " + + objects.getClass(), Status.BAD_REQUEST.getStatusCode(), e); + } else { + Throwable rootCause = Utility.getExceptionRootCause(e); + log.error("Unknown exception during database merge: " + rootCause.getClass() + " - " + rootCause.getMessage(), + e); + throw new ArrowheadException( + "Unknown exception during database merge: " + rootCause.getClass() + " - " + rootCause.getMessage(), e); + } + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + log.error("Unknown exception during database merge", e); + throw e; + } + + return objects[0]; + } + + @SafeVarargs + public final void delete(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.delete(object); + } + transaction.commit(); + } catch (PersistenceException e) { + if (transaction != null) { + transaction.rollback(); + } + + Throwable cause = e.getCause(); + if (cause instanceof ConstraintViolationException && cause.getMessage().equals("could not execute statement")) { + log.error("DatabaseManager:delete throws ConstraintViolationException", e); + throw new ArrowheadException( + "There is a reference to this object in another table, which prevents the delete operation. (" + objects + .getClass() + ")", Status.BAD_REQUEST.getStatusCode(), e); + } else { + Throwable rootCause = Utility.getExceptionRootCause(e); + log.error("Unknown exception during database delete: " + rootCause.getClass() + " - " + rootCause.getMessage(), + e); + throw new ArrowheadException( + "Unknown exception during database delete: " + rootCause.getClass() + " - " + rootCause.getMessage(), e); + } + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + log.error("Unknown exception during database delete", e); + throw e; + } + } + + // NOTE this only works well on tables which dont have any connection to any other tables (HQL does not do cascading) + @SuppressWarnings("unused") + public void deleteAll(String tableName) { + Session session = getSessionFactory().openSession(); + String stringQuery = "DELETE * FROM " + tableName; + Query query = session.createQuery(stringQuery); + query.executeUpdate(); + } + +} + + +// ----OLD VERSION---- + +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ +/* +package eu.arrowhead.common; + +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.DuplicateEntryException; +import eu.arrowhead.common.misc.TypeSafeProperties; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.Set; +import javax.persistence.PersistenceException; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.Configuration; +import org.hibernate.criterion.Disjunction; +import org.hibernate.criterion.Restrictions; +import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.query.Query; + +//NOTE should move to EntityManager from Sessions, using the JPA criteria API (Hibernate criteria will be removed in Hibernate 6) +public class DatabaseManager { + + private static volatile DatabaseManager instance; + private static SessionFactory sessionFactory; + private static TypeSafeProperties prop = Utility.getProp(); + private static String dbAddress; + private static String dbUser; + private static String dbPassword; + private static final Logger log = Logger.getLogger(DatabaseManager.class.getName()); + + static { + if (prop.containsKey("db_address") || prop.containsKey("log4j.appender.DB.URL")) { + if (prop.containsKey("db_address")) { + dbAddress = prop.getProperty("db_address"); + dbUser = prop.getProperty("db_user"); + dbPassword = prop.getProperty("db_password"); + } else { + dbAddress = prop.getProperty("log4j.appender.DB.URL", "jdbc:mysql://127.0.0.1:3306/log"); + dbUser = prop.getProperty("log4j.appender.DB.user", "root"); + dbPassword = prop.getProperty("log4j.appender.DB.password", "root"); + } + + try { + Configuration configuration = new Configuration().configure("hibernate.cfg.xml") + .setProperty("hibernate.connection.url", dbAddress) + .setProperty("hibernate.connection.username", dbUser) + .setProperty("hibernate.connection.password", dbPassword); + sessionFactory = configuration.buildSessionFactory(); + } catch (Exception e) { + throw new ServiceConfigurationError( + "Database connection could not be established, check default.conf/app.conf files!", e); + } + } + } + + private DatabaseManager() { + } + + public static synchronized void init() { + if (instance == null) { + instance = new DatabaseManager(); + } + } + + public static synchronized DatabaseManager getInstance() { + if (instance == null) { + instance = new DatabaseManager(); + } + return instance; + } + + private synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + Configuration configuration = new Configuration().configure("hibernate.cfg.xml") + .setProperty("hibernate.connection.url", dbAddress) + .setProperty("hibernate.connection.username", dbUser) + .setProperty("hibernate.connection.password", dbPassword); + sessionFactory = configuration.buildSessionFactory(); + } + return sessionFactory; + } + + public static synchronized void closeSessionFactory() { + if (sessionFactory != null) { + sessionFactory.close(); + } + instance = null; + } + + public Optional get(Class queryClass, long id) { + T object; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + object = session.get(queryClass, id); + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return Optional.ofNullable(object); + } + + public List get(Class queryClass, Set ids) { + List retrievedList = new ArrayList<>(); + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (Long id : ids) { + retrievedList.add(session.get(queryClass, id)); + } + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + @SuppressWarnings("unchecked") + public T get(Class queryClass, Map restrictionMap) { + T object; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + // NOTE session.createCriteria will be removed in Hibernate 6 + // noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + for (Entry entry : restrictionMap.entrySet()) { + criteria.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + } + object = (T) criteria.uniqueResult(); + transaction.commit(); + } catch (Exception e) { + log.error("get throws exception: " + e.getMessage(), e); + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return object; + } + + @SuppressWarnings("unchecked") + public List getAll(Class queryClass, Map restrictionMap) { + List retrievedList; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + // NOTE session.createCriteria will be removed in Hibernate 6 + // noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + for (Entry entry : restrictionMap.entrySet()) { + criteria.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + } + retrievedList = (List) criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); + transaction.commit(); + } catch (Exception e) { + log.error("getAll throws exception: " + e.getMessage(), e); + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + @SuppressWarnings("unchecked") + public List getAllOfEither(Class queryClass, Map restrictionMap) { + List retrievedList; + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + // NOTE session.createCriteria will be removed in Hibernate 6 + // noinspection deprecation + Criteria criteria = session.createCriteria(queryClass); + if (restrictionMap != null && !restrictionMap.isEmpty()) { + Disjunction disjunction = Restrictions.disjunction(); + for (Entry entry : restrictionMap.entrySet()) { + disjunction.add(Restrictions.eq(entry.getKey(), entry.getValue())); + } + criteria.add(disjunction); + } + retrievedList = (List) criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list(); + transaction.commit(); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return retrievedList; + } + + @SafeVarargs + public final T save(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.save(object); + } + transaction.commit(); + } catch (PersistenceException e) { + if (transaction != null) { + transaction.rollback(); + } + // NOTE root cause of persistenceException can be other reasons too + log.error("DatabaseManager:save throws DuplicateEntryException", e); + throw new DuplicateEntryException( + "There is already an entry in the database with these parameters. Please check the unique fields of the " + + objects.getClass(), + Status.BAD_REQUEST.getStatusCode(), e); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return objects[0]; + } + + @SafeVarargs + public final T merge(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.merge(object); + } + transaction.commit(); + } catch (PersistenceException e) { + if (transaction != null) { + transaction.rollback(); + } + log.error("DatabaseManager:merge throws DuplicateEntryException", e); + throw new DuplicateEntryException( + "There is already an entry in the database with these parameters. Please check the unique fields of the " + + objects.getClass(), + Status.BAD_REQUEST.getStatusCode(), e); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + + return objects[0]; + } + + @SafeVarargs + public final void delete(T... objects) { + Transaction transaction = null; + + try (Session session = getSessionFactory().openSession()) { + transaction = session.beginTransaction(); + for (T object : objects) { + session.delete(object); + } + transaction.commit(); + } catch (ConstraintViolationException e) { + if (transaction != null) { + transaction.rollback(); + } + log.error("DatabaseManager:delete throws ConstraintViolationException"); + throw new ArrowheadException( + "There is a reference to this object in another table, which prevents the delete operation. (" + + objects.getClass() + ")", + Status.BAD_REQUEST.getStatusCode(), e); + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw e; + } + } + + // NOTE this only works well on tables which dont have any connection to any + // other tables (HQL does not do cascading) + @SuppressWarnings("unused") + public void deleteAll(String tableName) { + Session session = getSessionFactory().openSession(); + String stringQuery = "DELETE * FROM " + tableName; + Query query = session.createQuery(stringQuery); + query.executeUpdate(); + } +} + +*/ \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/Utility.java b/core-common/src/main/java/eu/arrowhead/common/Utility.java new file mode 100644 index 00000000..d0acb328 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/Utility.java @@ -0,0 +1,576 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.NeighborCloud; +import eu.arrowhead.common.database.OwnCloud; +import eu.arrowhead.common.database.ServiceRegistryEntry; +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.AuthException; +import eu.arrowhead.common.exception.BadPayloadException; +import eu.arrowhead.common.exception.DataNotFoundException; +import eu.arrowhead.common.exception.DnsException; +import eu.arrowhead.common.exception.DuplicateEntryException; +import eu.arrowhead.common.exception.ErrorMessage; +import eu.arrowhead.common.exception.UnavailableServerException; +import eu.arrowhead.common.json.JacksonJsonProviderAtRest; +import eu.arrowhead.common.messages.ServiceQueryForm; +import eu.arrowhead.common.messages.ServiceQueryResult; +import eu.arrowhead.common.misc.CoreSystemService; +import eu.arrowhead.common.misc.TypeSafeProperties; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.Set; +import java.util.stream.Collectors; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.Invocation.Builder; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.Response.Status.Family; +import javax.ws.rs.core.UriBuilder; +import org.apache.log4j.Logger; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; + +public final class Utility { + + private static Client client = createClient(null); + private static Client sslClient; + private static SSLContext sslContext; + private static String SR_QUERY_URI; + + private static final ObjectMapper mapper = JacksonJsonProviderAtRest.getMapper(); + private static final Logger log = Logger.getLogger(Utility.class.getName()); + private static final HostnameVerifier allHostsValid = (hostname, session) -> { + // Decide whether to allow the connection... + return true; + }; + + private static final String DEFAULT_CONF = "default.conf"; + private static final String DEFAULT_CONF_DIR = "config" + File.separator + "default.conf"; + private static final String APP_CONF = "app.conf"; + private static final String APP_CONF_DIR = "config" + File.separator + "app.conf"; + + private Utility() throws AssertionError { + throw new AssertionError("Arrowhead Common:Utility is a non-instantiable class"); + } + + private static Client createClient(SSLContext context) { + ClientConfig configuration = new ClientConfig(); + configuration.property(ClientProperties.CONNECT_TIMEOUT, 30000); + configuration.property(ClientProperties.READ_TIMEOUT, 30000); + + Client client; + if (context != null) { + client = ClientBuilder.newBuilder().sslContext(context).withConfig(configuration).hostnameVerifier(allHostsValid) + .build(); + } else { + client = ClientBuilder.newClient(configuration); + } + client.register(JacksonJsonProviderAtRest.class); + return client; + } + + public static void setSSLContext(SSLContext context) { + sslContext = context; + sslClient = createClient(sslContext); + } + + public static void setServiceRegistryUri(String uri) { + if (uri == null) { + throw new AssertionError("Arrowhead Common:Utility has no Service Registry URL."); + } + SR_QUERY_URI = UriBuilder.fromUri(uri).path("query").build().toString(); + } + + public static Response sendRequest(String uri, String method, T payload, SSLContext givenContext) { + log.info("Sending " + method + " request to: " + uri); + + boolean isSecure = false; + if (uri == null) { + log.error("sendRequest received null uri"); + throw new NullPointerException( + "send (HTTP) request method received null URL. This most likely means the invoking Core System could not " + + "fetch the service" + + " of another Core System from the Service Registry!"); + } + if (uri.startsWith("https")) { + isSecure = true; + } + + if (isSecure && sslClient == null) { + throw new AuthException( + "SSL Context is not set, but secure request sending was invoked. An insecure module can not send requests " + + "to secure modules.", + Status.UNAUTHORIZED.getStatusCode()); + } + Client usedClient = isSecure ? givenContext != null ? createClient(givenContext) : sslClient : client; + + Builder request = usedClient.target(UriBuilder.fromUri(uri).build()).request() + .header("Content-type", "application/json"); + Response response; // will not be null after the switch-case + try { + switch (method) { + case "GET": + response = request.get(); + break; + case "POST": + response = request.post(Entity.json(payload)); + break; + case "PUT": + response = request.put(Entity.json(payload)); + break; + case "DELETE": + response = request.delete(); + break; + default: + throw new NotAllowedException("Invalid method type was given to the Utility.sendRequest() method"); + } + } catch (ProcessingException e) { + if (e.getCause().getMessage().contains("PKIX path")) { + log.error("The system at " + uri + " is not part of the same certificate chain of trust!"); + throw new AuthException("The system at " + uri + " is not part of the same certificate chain of trust!", + Status.UNAUTHORIZED.getStatusCode(), e); + } else { + log.error("UnavailableServerException occurred at " + uri, e); + throw new UnavailableServerException("Could not get any response from: " + uri, + Status.SERVICE_UNAVAILABLE.getStatusCode(), e); + } + } + + // If the response status code does not start with 2 the request was not successful + if (!(response.getStatusInfo().getFamily() == Family.SUCCESSFUL)) { + handleException(response, uri); + } + + return response; + } + + public static Response sendRequest(String uri, String method, T payload) { + return sendRequest(uri, method, payload, null); + } + + //TODO option for async request sending which can be used by the event handler + + private static void handleException(Response response, String uri) { + //The response body has to be extracted before the stream closes + String errorMessageBody = toPrettyJson(null, response.getEntity()); + if (errorMessageBody == null || errorMessageBody.equals("null")) { + response.bufferEntity(); + errorMessageBody = response.readEntity(String.class); + if (errorMessageBody.length() > 250) { + errorMessageBody = errorMessageBody.substring(0, 250); + } + } + + ErrorMessage errorMessage; + try { + errorMessage = response.readEntity(ErrorMessage.class); + } catch (RuntimeException e) { + log.error("Unknown reason for RuntimeException at the sendRequest() method.", e); + log.info("Request failed, response status code: " + response.getStatus()); + log.info("Request failed, response body: " + errorMessageBody); + throw new RuntimeException("Unknown error occurred at " + uri + ". Check log for possibly more information.", e); + } + if (errorMessage == null) { + log.error("Unknown reason for RuntimeException at the sendRequest() method."); + log.info("Request failed, response status code: " + response.getStatus()); + log.info("Request failed, response body: " + errorMessageBody); + throw new RuntimeException("Unknown error occurred at " + uri + ". Check log for possibly more information."); + } else if (errorMessage.getExceptionType() == null) { + log.info("Request failed, response status code: " + response.getStatus()); + log.info("Request failed, response body: " + errorMessageBody); + throw new RuntimeException("Unknown error occurred at " + uri + ". Check log for possibly more information."); + } else { + log.error("Request returned with " + errorMessage.getExceptionType().toString() + ": " + errorMessage + .getErrorMessage()); + switch (errorMessage.getExceptionType()) { + case ARROWHEAD: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case AUTH: + throw new AuthException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case BAD_MEDIA_TYPE: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode()); + case BAD_METHOD: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case BAD_PAYLOAD: + throw new BadPayloadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case BAD_URI: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case DATA_NOT_FOUND: + throw new DataNotFoundException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case DNS_SD: + throw new DnsException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), errorMessage.getOrigin()); + case DUPLICATE_ENTRY: + throw new DuplicateEntryException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case GENERIC: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case JSON_PROCESSING: + throw new ArrowheadException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + case UNAVAILABLE: + throw new UnavailableServerException(errorMessage.getErrorMessage(), errorMessage.getErrorCode(), + errorMessage.getOrigin()); + } + } + } + + public static String getUri(String address, int port, String serviceURI, boolean isSecure, boolean serverStart) { + if (address == null) { + log.error("Address can not be null (Utility:getUri throws NPE)"); + throw new NullPointerException("Address can not be null (Utility:getUri throws NPE)"); + } + + UriBuilder ub = UriBuilder.fromPath("").host(address); + if (isSecure) { + ub.scheme("https"); + } else { + ub.scheme("http"); + } + if (port > 0) { + ub.port(port); + } + if (serviceURI != null) { + ub.path(serviceURI); + } + + String url = ub.toString(); + try { + new URI(url); + } catch (URISyntaxException e) { + if (serverStart) { + throw new ServiceConfigurationError( + url + " is not a valid URL to start a HTTP server! Please fix the address field in the properties file."); + } else { + log.error("Bad URL components passed to getUri() method"); + throw new ArrowheadException(url + " is not a valid URL!"); + } + } + + log.info("Utility:getUri returning this: " + url); + return url; + } + + public static Optional getServiceInfo(String serviceId) { + ArrowheadService service = sslContext == null ? new ArrowheadService(createSD(serviceId, false), + Collections.singleton("HTTP-INSECURE-JSON"), + null) + : new ArrowheadService(createSD(serviceId, true), + Collections.singleton("HTTP-SECURE-JSON"), + ArrowheadMain.secureServerMetadata); + ServiceQueryForm sqf = new ServiceQueryForm(service, true, false); + Response response = sendRequest(SR_QUERY_URI, "PUT", sqf); + ServiceQueryResult result = response.readEntity(ServiceQueryResult.class); + if (result != null && result.isValid()) { + ServiceRegistryEntry entry = result.getServiceQueryData().get(0); + ArrowheadSystem coreSystem = entry.getProvider(); + boolean isSecure = false; + if (!entry.getProvidedService().getServiceMetadata().isEmpty()) { + isSecure = entry.getProvidedService().getServiceMetadata().containsKey("security"); + } + String serviceURI = getUri(coreSystem.getAddress(), coreSystem.getPort(), entry.getServiceURI(), isSecure, false); + if (serviceId.equals(CoreSystemService.GW_CONSUMER_SERVICE.getServiceDef()) || serviceId + .equals(CoreSystemService.GW_PROVIDER_SERVICE.getServiceDef())) { + return Optional.of(new String[]{serviceURI, coreSystem.getSystemName(), coreSystem.getAddress(), + coreSystem.getAuthenticationInfo()}); + } + return Optional.of(new String[]{serviceURI}); + } + return Optional.empty(); + } + + public static List getNeighborCloudURIs(boolean isSecure) { + List cloudList = new ArrayList<>(DatabaseManager.getInstance().getAll(NeighborCloud.class, null)); + + List uriList = new ArrayList<>(); + for (NeighborCloud cloud : cloudList) { + if (isSecure == cloud.getCloud().isSecure()) { + uriList.add(getUri(cloud.getCloud().getAddress(), cloud.getCloud().getPort(), + cloud.getCloud().getGatekeeperServiceURI(), cloud.getCloud().isSecure(), false)); + } + } + + return uriList; + } + + public static ArrowheadCloud getOwnCloud(boolean isSecure) { + List cloudList = DatabaseManager.getInstance().getAll(OwnCloud.class, null); + if (cloudList.isEmpty()) { + log.error("Utility:getOwnCloud not found in the database."); + throw new DataNotFoundException( + "Own Cloud information not found in the database. This information is needed for the Gatekeeper System.", + Status.NOT_FOUND.getStatusCode()); + } + if (cloudList.size() > 2) { + log.warn("own_cloud table should NOT have more than 2 rows."); + } + if (isSecure) { + for (OwnCloud cloud : cloudList) { + if (cloud.getCloud().isSecure()) { + return cloud.getCloud(); + } + } + log.error("Utility:getOwnCloud finds no secure own cloud!"); + throw new DataNotFoundException("Could not find secure own cloud information in the database!", + Status.NOT_FOUND.getStatusCode()); + } else { + for (OwnCloud cloud : cloudList) { + if (!cloud.getCloud().isSecure()) { + return cloud.getCloud(); + } + } + log.error("Utility:getOwnCloud finds no insecure own cloud!"); + throw new DataNotFoundException("Could not find insecure own cloud information in the database!", + Status.NOT_FOUND.getStatusCode()); + } + } + + public static String stripEndSlash(String uri) { + if (uri != null && uri.endsWith("/")) { + return uri.substring(0, uri.length() - 1); + } + return uri; + } + + public static String getRequestPayload(InputStream is) { + StringBuilder sb = new StringBuilder(); + String line; + try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + while ((line = br.readLine()) != null) { + sb.append(line); + } + } catch (UnsupportedEncodingException e) { + log.fatal("getRequestPayload ISReader has unsupported charset set!"); + throw new AssertionError( + "getRequestPayload InputStreamReader has unsupported character set! Code needs to be changed!", e); + } catch (IOException e) { + log.error("IOException while reading the request payload"); + throw new RuntimeException("IOException occured while reading an incoming request payload", e); + } + + if (!sb.toString().isEmpty()) { + String payload = toPrettyJson(sb.toString(), null); + return payload != null ? payload : ""; + } else { + return ""; + } + } + + public static String toPrettyJson(String jsonString, Object obj) { + try { + if (jsonString != null) { + jsonString = jsonString.trim(); + if (jsonString.startsWith("{")) { + Object tempObj = mapper.readValue(jsonString, Object.class); + return mapper.writeValueAsString(tempObj); + } else { + Object[] tempObj = mapper.readValue(jsonString, Object[].class); + return mapper.writeValueAsString(tempObj); + } + } + if (obj != null) { + return mapper.writeValueAsString(obj); + } + } catch (IOException e) { + throw new ArrowheadException( + "Jackson library threw IOException during JSON serialization! Wrapping it in RuntimeException. Exception " + + "message: " + e.getMessage(), e); + } + return null; + } + + public static T fromJson(String json, Class parsedClass) { + try { + return mapper.readValue(json, parsedClass); + } catch (IOException e) { + throw new ArrowheadException("Jackson library threw exception during JSON parsing!", e); + } + } + + public static String createSD(String baseSD, boolean isSecure) { + if (isSecure) { + return "Secure" + baseSD; + } else { + return "Insecure" + baseSD; + } + } + + public static TypeSafeProperties getProp(String fileName) { + TypeSafeProperties prop = new TypeSafeProperties(); + try { + File file; + if (new File(fileName).exists()) { + file = new File(fileName); + } else { + file = new File("config" + File.separator + fileName); + } + FileInputStream inputStream = new FileInputStream(file); + prop.load(inputStream); + } catch (FileNotFoundException ex) { + throw new ServiceConfigurationError( + fileName + " file not found, make sure you have the correct working directory " + + "set! (directory where the config folder can be found)", ex); + } catch (Exception ex) { + ex.printStackTrace(); + } + return prop; + } + + public static TypeSafeProperties getProp() { + TypeSafeProperties prop = new TypeSafeProperties(); + + try { + if (Files.isReadable(Paths.get(DEFAULT_CONF))) { + prop.load(new FileInputStream(DEFAULT_CONF)); + } else if (Files.isReadable(Paths.get(DEFAULT_CONF_DIR))) { + prop.load(new FileInputStream(DEFAULT_CONF_DIR)); + } else { + throw new ServiceConfigurationError( + "default.conf file not found in the working directory! (" + System.getProperty("user.dir") + ")"); + } + + if (Files.isReadable(Paths.get(APP_CONF))) { + prop.load(new FileInputStream(APP_CONF)); + } else if (Files.isReadable(Paths.get(APP_CONF_DIR))) { + prop.load(new FileInputStream(APP_CONF_DIR)); + } + } catch (IOException e) { + throw new AssertionError("File loading failed...", e); + } + + //If MySQL based JDBC URLs are used, we append the system default time zone to the URL + //This is for a bug fix with certain MySQL JDBC driver versions: https://github.com/arrowhead-f/core-java/issues/30 + String timeZoneQueryParam = "serverTimezone=" + ZoneId.systemDefault().getId(); + + String dbAddress = prop.getProperty("db_address"); + if (dbAddress != null && dbAddress.contains("mysql")) { + if (dbAddress.contains("?")) { + dbAddress = dbAddress + "&" + timeZoneQueryParam; + } else { + dbAddress = dbAddress + "?" + timeZoneQueryParam; + } + prop.setProperty("db_address", dbAddress); + } + + String logAddress = prop.getProperty("log4j.appender.DB.URL"); + if (logAddress != null && logAddress.contains("mysql")) { + if (logAddress.contains("?")) { + logAddress = logAddress + "&" + timeZoneQueryParam; + } else { + logAddress = logAddress + "?" + timeZoneQueryParam; + } + prop.setProperty("log4j.appender.DB.URL", logAddress); + } + + return prop; + } + + public static void checkProperties(Set propertyNames, List mandatoryProperties) { + if (mandatoryProperties == null || mandatoryProperties.isEmpty()) { + return; + } + //Arrays.asList() returns immutable lists, so we have to copy it first + List properties = new ArrayList<>(mandatoryProperties); + if (!propertyNames.containsAll(mandatoryProperties)) { + properties.removeIf(propertyNames::contains); + throw new ServiceConfigurationError("Missing field(s) from config file: " + properties.toString()); + } + } + + /* If needed, this method can be used to get the IPv4 address of the host machine. Public point-to-point IP + addresses are prioritized over private + (site local) IP addresses */ + @SuppressWarnings("unused") + public static String getIpAddress() throws SocketException { + List addresses = new ArrayList<>(); + + Enumeration e = NetworkInterface.getNetworkInterfaces(); + while (e.hasMoreElements()) { + NetworkInterface inf = (NetworkInterface) e.nextElement(); + Enumeration ee = inf.getInetAddresses(); + while (ee.hasMoreElements()) { + addresses.add((InetAddress) ee.nextElement()); + } + } + + addresses = addresses.stream().filter(current -> !current.getHostAddress().contains(":")) + .filter(current -> !current.isLoopbackAddress()) + .filter(current -> !current.isMulticastAddress()) + .filter(current -> !current.isLinkLocalAddress()).collect(Collectors.toList()); + if (addresses.isEmpty()) { + throw new SocketException("No valid addresses left after filtering"); + } + for (InetAddress address : addresses) { + if (!address.isSiteLocalAddress()) { + return address.getHostAddress(); + } + } + return addresses.get(0).getHostAddress(); + } + + public static boolean isBeanValid(T bean) { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + Validator validator = factory.getValidator(); + Set> violations = validator.validate(bean); + return violations.isEmpty(); + } + + @SuppressWarnings("WeakerAccess") + public static Throwable getExceptionRootCause(Throwable e) { + Throwable cause = null; + Throwable result = e; + + while (null != (cause = result.getCause()) && (result != cause)) { + result = cause; + } + return result; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadCloud.java b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadCloud.java new file mode 100644 index 00000000..23b86fe6 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadCloud.java @@ -0,0 +1,186 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotBlank; + +@Entity +@Table(name = "arrowhead_cloud", uniqueConstraints = {@UniqueConstraint(columnNames = {"operator", "cloud_name"})}) +public class ArrowheadCloud { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Length(max = 255, message = "Cloud operator must be 255 character at max") + @Pattern(regexp = "[\\p{L}0-9-_:]+", message = + "Cloud operator can only contain alphanumerical characters and some special characters (dash, " + + "underscore and colon)") + private String operator; + + @NotBlank + @Column(name = "cloud_name") + @Size(max = 255, message = "Cloud name must be 255 character at max") + @Pattern(regexp = "[\\p{L}0-9-_:]+", message = + "Cloud name can only contain alphanumerical characters and some special characters (dash, " + + "underscore and colon)") + private String cloudName; + + @NotBlank + @Size(min = 3, max = 255, message = "Cloud address must be between 3 and 255 characters") + private String address; + + @NotNull + @Min(value = 1, message = "Port can not be less than 1") + @Max(value = 65535, message = "Port can not be greater than 65535") + private Integer port; + + @NotBlank + @Column(name = "gatekeeper_service_uri") + private String gatekeeperServiceURI; + + @Column(name = "authentication_info") + @Size(max = 2047, message = "Authentication information must be 2047 character at max") + private String authenticationInfo; + + @Column(name = "is_secure") + @Type(type = "yes_no") + private Boolean secure = false; + + public ArrowheadCloud() { + } + + public ArrowheadCloud(String operator, String cloudName, String address, Integer port, String gatekeeperServiceURI, String authenticationInfo, + Boolean secure) { + this.operator = operator; + this.cloudName = cloudName; + this.address = address; + this.port = port; + this.gatekeeperServiceURI = gatekeeperServiceURI; + this.authenticationInfo = authenticationInfo; + this.secure = secure; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getOperator() { + return operator; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + public String getCloudName() { + return cloudName; + } + + public void setCloudName(String cloudName) { + this.cloudName = cloudName; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getGatekeeperServiceURI() { + return gatekeeperServiceURI; + } + + public void setGatekeeperServiceURI(String gatekeeperServiceURI) { + this.gatekeeperServiceURI = gatekeeperServiceURI; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } + + + public Boolean isSecure() { + return secure; + } + + public void setSecure(Boolean secure) { + this.secure = secure; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ArrowheadCloud)) { + return false; + } + ArrowheadCloud that = (ArrowheadCloud) o; + return Objects.equals(operator, that.operator) && Objects.equals(cloudName, that.cloudName) && Objects.equals(address, that.address) && Objects + .equals(port, that.port) && Objects.equals(gatekeeperServiceURI, that.gatekeeperServiceURI) && Objects.equals(secure, that.secure); + } + + @Override + public int hashCode() { + return Objects.hash(operator, cloudName, address, port, gatekeeperServiceURI, secure); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("operator", operator).add("cloudName", cloudName).add("address", address).add("port", port) + .add("gatekeeperServiceURI", gatekeeperServiceURI).add("secure", secure).toString(); + } + + public void partialUpdate(ArrowheadCloud other) { + this.operator = other.getOperator() != null ? other.getOperator() : this.operator; + this.cloudName = other.getCloudName() != null ? other.getCloudName() : this.cloudName; + this.address = other.getAddress() != null ? other.getAddress() : this.address; + this.port = other.getPort() != null ? other.getPort() : this.port; + this.gatekeeperServiceURI = other.getGatekeeperServiceURI() != null ? other.getGatekeeperServiceURI() : this.gatekeeperServiceURI; + this.authenticationInfo = other.getAuthenticationInfo() != null ? other.getAuthenticationInfo() : this.authenticationInfo; + this.secure = other.isSecure() != null ? other.isSecure() : this.secure; + } +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadDevice.java b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadDevice.java new file mode 100644 index 00000000..ec8a9928 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadDevice.java @@ -0,0 +1,80 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.validator.constraints.NotBlank; + +@Entity +@Table(name = "arrowhead_device") +public class ArrowheadDevice { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Column(name = "device_name") + @Size(max = 255, message = "System name must be 255 character at max") + private String deviceName; + + public ArrowheadDevice() { + } + + public ArrowheadDevice(String deviceName) { + this.deviceName = deviceName; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ArrowheadDevice)) { + return false; + } + ArrowheadDevice that = (ArrowheadDevice) o; + return Objects.equals(deviceName, that.deviceName); + } + + @Override + public int hashCode() { + return Objects.hash(deviceName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("deviceName", deviceName).toString(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadService.java b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadService.java new file mode 100644 index 00000000..86421543 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadService.java @@ -0,0 +1,182 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.Utility; +import eu.arrowhead.common.exception.BadPayloadException; +import eu.arrowhead.common.json.support.ArrowheadServiceSupport; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.Table; +import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.validator.constraints.NotBlank; + +/** + * Representation of a service within Arrowhead. + * + * @author uzoltan + * @since 4.2 + */ +@Entity +@Table(name = "arrowhead_service", uniqueConstraints = {@UniqueConstraint(columnNames = {"service_definition"})}) +public class ArrowheadService { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Size(max = 255, message = "Service serviceDefinition must be 255 character at max") + @Column(name = "service_definition") + private String serviceDefinition; + + @Size(max = 100, message = "Service can only have 100 interfaces at max") + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "arrowhead_service_interfaces", joinColumns = @JoinColumn(name = "arrowhead_service_id")) + private Set interfaces = new HashSet<>(); + + @Transient + @JsonInclude(Include.NON_EMPTY) + @Size(max = 100, message = "Service can only have 100 serviceMetadata key-value pairs at max") + private Map serviceMetadata = new HashMap<>(); + + public ArrowheadService() { + } + + /** + * Constructor with all the fields of the ArrowheadService class. + * + * @param serviceDefinition A descriptive name for the service + * @param interfaces The set of interfaces that can be used to consume this service (helps interoperability between + * ArrowheadSystems). Concrete meaning of what is an interface is service specific (e.g. JSON, I2C) + * @param serviceMetadata Arbitrary additional serviceMetadata belonging to the service, stored as key-value pairs. + */ + public ArrowheadService(String serviceDefinition, Set interfaces, Map serviceMetadata) { + this.serviceDefinition = serviceDefinition; + this.interfaces = interfaces; + this.serviceMetadata = serviceMetadata; + } + + public ArrowheadService(ArrowheadServiceSupport service) { + this.serviceDefinition = service.getServiceGroup() + "_" + service.getServiceDefinition(); + this.interfaces = new HashSet<>(service.getInterfaces()); + this.serviceMetadata = service.getServiceMetadata(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getServiceDefinition() { + return serviceDefinition; + } + + public void setServiceDefinition(String serviceDefinition) { + this.serviceDefinition = serviceDefinition; + } + + public Set getInterfaces() { + return interfaces; + } + + @JsonSetter + public void setInterfaces(Set interfaces) { + for (String serviceInterface : interfaces) { + if (serviceInterface == null || serviceInterface.trim().isEmpty()) { + throw new BadPayloadException("ArrowheadService interface can not be blank!"); + } + } + this.interfaces = interfaces; + } + + public Map getServiceMetadata() { + return serviceMetadata; + } + + @JsonSetter + public void setServiceMetadata(Map serviceMetadata) { + for (Map.Entry entry : serviceMetadata.entrySet()) { + String key = entry.getKey(); + if (key == null || key.trim().isEmpty()) { + throw new BadPayloadException("ArrowheadService metadata key can not be blank!"); + } + String value = entry.getValue(); + if (value == null || value.trim().isEmpty()) { + throw new BadPayloadException("ArrowheadService metadata value can not be blank!"); + } + } + this.serviceMetadata = serviceMetadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ArrowheadService)) { + return false; + } + ArrowheadService that = (ArrowheadService) o; + + if (!Objects.equals(serviceDefinition, that.serviceDefinition)) { + return false; + } + + //2 services can be equal if they have at least 1 common interface + if (interfaces == null || that.interfaces == null) { + return true; + } else { + Set intersection = new HashSet<>(interfaces); + intersection.retainAll(that.interfaces); + return !intersection.isEmpty(); + } + } + + @Override + public int hashCode() { + return Objects.hash(serviceDefinition); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("serviceDefinition", serviceDefinition).toString(); + } + + @SuppressWarnings("unchecked") + public void partialUpdate(ArrowheadService other) { + this.serviceDefinition = other.getServiceDefinition() != null ? other.getServiceDefinition() : this.serviceDefinition; + //Making deep copies of the collections with the help of JSON (de)serialization + this.interfaces = other.getInterfaces().isEmpty() ? this.interfaces : Utility + .fromJson(Utility.toPrettyJson(null, other.getInterfaces()), Set.class); + this.serviceMetadata = other.getServiceMetadata().isEmpty() ? this.serviceMetadata : Utility.fromJson(Utility.toPrettyJson(null, other.getServiceMetadata()), Map.class); + } +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadSystem.java b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadSystem.java new file mode 100644 index 00000000..78441b02 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/ArrowheadSystem.java @@ -0,0 +1,171 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import eu.arrowhead.common.json.support.ArrowheadSystemSupport; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.validator.constraints.NotBlank; + +@Entity +@Table(name = "arrowhead_system", uniqueConstraints = {@UniqueConstraint(columnNames = {"system_name", "address", "port"})}) +public class ArrowheadSystem { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Size(max = 255, message = "System name must be 255 character at max") + @Pattern(regexp = "[\\p{L}0-9-_:]+", message = + "System name can only contain alphanumerical characters and some special characters (dash, " + + "underscore and colon)") + @Column(name = "system_name") + private String systemName; + + @NotBlank + @Size(min = 3, max = 255, message = "Address must be between 3 and 255 characters") + private String address; + + @NotNull + @Min(value = 1, message = "Port can not be less than 1") + @Max(value = 65535, message = "Port can not be greater than 65535") + private Integer port; + + @Column(name = "authentication_info") + @Size(max = 2047, message = "Authentication information must be 2047 character at max") + private String authenticationInfo; + + public ArrowheadSystem() { + } + + public ArrowheadSystem(String systemName, String address, Integer port, String authenticationInfo) { + this.systemName = systemName; + this.address = address; + this.port = port; + this.authenticationInfo = authenticationInfo; + } + + public ArrowheadSystem(String json) { + String[] fields = json.split(","); + this.systemName = fields[0].equals("null") ? null : fields[0]; + this.address = fields[1].equals("null") ? null : fields[1]; + this.port = Integer.valueOf(fields[2]); + + if (fields.length == 4) { + this.authenticationInfo = fields[3].equals("null") ? null : fields[3]; + } + } + + public ArrowheadSystem(ArrowheadSystemSupport system) { + this.systemName = system.getSystemGroup() + "_" + system.getSystemName(); + this.address = system.getAddress(); + this.port = system.getPort(); + this.authenticationInfo = system.getAuthenticationInfo(); + } + + @SuppressWarnings("CopyConstructorMissesField") + public ArrowheadSystem(ArrowheadSystem system) { + this.systemName = system.systemName; + this.address = system.address; + this.port = system.port; + this.authenticationInfo = system.authenticationInfo; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getSystemName() { + return systemName; + } + + public void setSystemName(String systemName) { + this.systemName = systemName; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } + + public String toArrowheadCommonName(String operator, String cloudName) { + if (systemName.contains(".") || operator.contains(".") || cloudName.contains(".")) { + throw new IllegalArgumentException("The string fields can not contain dots!"); + } + //throws NPE if any of the fields are null + return systemName.concat(".").concat(cloudName).concat(".").concat(operator).concat(".").concat("arrowhead.eu"); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ArrowheadSystem)) { + return false; + } + ArrowheadSystem that = (ArrowheadSystem) o; + return Objects.equals(systemName, that.systemName) && Objects.equals(address, that.address) && Objects.equals(port, that.port); + } + + @Override + public int hashCode() { + return Objects.hash(systemName, address, port); + } + + //NOTE ArrowheadSystemKeyDeserializer relies on this implementation, do not change it without changing the (String json) constructor + @Override + public String toString() { + return systemName + "," + address + "," + port + "," + authenticationInfo; + } + + public void partialUpdate(ArrowheadSystem other) { + this.systemName = other.getSystemName() != null ? other.getSystemName() : this.systemName; + this.address = other.getAddress() != null ? other.getAddress() : this.address; + this.port = other.getPort() != null ? other.getPort() : this.port; + this.authenticationInfo = other.getAuthenticationInfo() != null ? other.getAuthenticationInfo() : this.authenticationInfo; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/Broker.java b/core-common/src/main/java/eu/arrowhead/common/database/Broker.java new file mode 100644 index 00000000..c96359f4 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/Broker.java @@ -0,0 +1,107 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Type; +import org.hibernate.validator.constraints.NotBlank; + +@Entity +@Table(name = "broker") +public class Broker { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Size(min = 3, max = 255, message = "Address must be between 3 and 255 characters") + private String address; + + @NotNull + @Min(value = 1, message = "Port can not be less than 1") + @Max(value = 65535, message = "Port can not be greater than 65535") + private Integer port; + + @Column(name = "is_secure") + @Type(type = "yes_no") + private Boolean secure = false; + + public Broker() { + } + + public Broker(String address, Integer port, Boolean secure) { + this.address = address; + this.port = port; + this.secure = secure; + } + + public Long getId() { + return id; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + + public Boolean isSecure() { + return secure; + } + + public void setSecure(Boolean secure) { + this.secure = secure; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Broker)) { + return false; + } + Broker broker = (Broker) o; + return Objects.equals(address, broker.address) && Objects.equals(port, broker.port) && Objects.equals(secure, broker.secure); + } + + @Override + public int hashCode() { + return Objects.hash(address, port, secure); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("address", address).add("port", port).add("secure", secure).toString(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/DeviceRegistryEntry.java b/core-common/src/main/java/eu/arrowhead/common/database/DeviceRegistryEntry.java new file mode 100644 index 00000000..774bab8f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/DeviceRegistryEntry.java @@ -0,0 +1,139 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import eu.arrowhead.common.json.constraint.LDTInFuture; +import java.time.LocalDateTime; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +@Entity +@Table(name = "device_registry", uniqueConstraints = {@UniqueConstraint(columnNames = {"arrowhead_device_id"})}) +public class DeviceRegistryEntry { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull(message = "Provided ArrowheadDevice cannot be null") + @JoinColumn(name = "arrowhead_device_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadDevice providedDevice; + + @Column(name = "mac_address") + @Size(max = 255, message = "macAddress must be 255 character at max") + private String macAddress; + + @Column(name = "end_of_validity") + @LDTInFuture(message = "End of validity date cannot be in the past") + private LocalDateTime endOfValidity; + + public DeviceRegistryEntry() { + super(); + } + + public DeviceRegistryEntry(final Long id, @Valid @NotNull(message = "Provided ArrowheadDevice cannot be null") final ArrowheadDevice providedDevice, + @Size(max = 255, message = "macAddress must be 255 character at max") final String macAddress, + @LDTInFuture(message = "End of validity date cannot be in the past") final LocalDateTime endOfValidity) { + super(); + this.id = id; + this.providedDevice = providedDevice; + this.macAddress = macAddress; + this.endOfValidity = endOfValidity; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadDevice getProvidedDevice() { + return providedDevice; + } + + public void setProvidedDevice(ArrowheadDevice providedDevice) { + this.providedDevice = providedDevice; + } + + public String getMacAddress() { + return macAddress; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public LocalDateTime getEndOfValidity() { + return endOfValidity; + } + + public void setEndOfValidity(LocalDateTime endOfValidity) { + this.endOfValidity = endOfValidity; + } + + protected void append(final StringBuilder builder) { + builder.append("id=").append(id); + builder.append(", providedDevice=").append(providedDevice); + builder.append(", macAddress=").append(macAddress); + builder.append(", endOfValidity=").append(endOfValidity); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append(" ["); + append(builder); + builder.append("]"); + return builder.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DeviceRegistryEntry other = (DeviceRegistryEntry) obj; + + return Objects.equals(this.providedDevice, other.providedDevice) && Objects.equals(this.macAddress, other.macAddress) && Objects + .equals(this.endOfValidity, other.endOfValidity); + } + + @Override + public int hashCode() { + return Objects.hash(providedDevice, macAddress, endOfValidity); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/EventFilter.java b/core-common/src/main/java/eu/arrowhead/common/database/EventFilter.java new file mode 100644 index 00000000..f24ff2b4 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/EventFilter.java @@ -0,0 +1,223 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.fasterxml.jackson.annotation.JsonSetter; +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.exception.BadPayloadException; +import eu.arrowhead.common.json.constraint.ZDTInFuture; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import org.hibernate.annotations.Type; +import org.hibernate.validator.constraints.NotBlank; + +@Entity +@Table(name = "event_filter", uniqueConstraints = { + @UniqueConstraint(columnNames = {"event_type", "consumer_system_id"})}) +public class EventFilter { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @NotBlank + @Column(name = "event_type") + @Size(max = 255, message = "Event type must be 255 character at max") + private String eventType; + + @Valid + @NotNull(message = "Consumer ArrowheadSystem cannot be null") + @JoinColumn(name = "consumer_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem consumer; + + @Valid + @Size(max = 100, message = "Event filter can only have 100 sources at max") + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "event_filter_sources_list", joinColumns = @JoinColumn(name = "filter_id")) + private Set sources = new HashSet<>(); + + @Column(name = "start_date") + private ZonedDateTime startDate; + + @Column(name = "end_date") + @ZDTInFuture(message = "Filter end date must be in the future") + private ZonedDateTime endDate; + + @ElementCollection(fetch = FetchType.EAGER) + @MapKeyColumn(name = "metadata_key") + @Column(name = "metadata_value", length = 2047) + @CollectionTable(name = "event_filter_metadata", joinColumns = @JoinColumn(name = "filter_id")) + private Map filterMetadata = new HashMap<>(); + + @Column(name = "notify_uri") + private String notifyUri; + + //TODO provide a REST interface to easily switch this + @Column(name = "match_metadata") + @Type(type = "yes_no") + private Boolean matchMetadata = false; + + public EventFilter() { + } + + public EventFilter(String eventType, ArrowheadSystem consumer, Set sources, ZonedDateTime startDate, + ZonedDateTime endDate, Map filterMetadata, String notifyUri, + boolean matchMetadata) { + this.eventType = eventType; + this.consumer = consumer; + this.sources = sources; + this.startDate = startDate; + this.endDate = endDate; + this.filterMetadata = filterMetadata; + this.notifyUri = notifyUri; + this.matchMetadata = matchMetadata; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public Set getSources() { + return sources; + } + + public void setSources(Set sources) { + this.sources = sources; + } + + public ZonedDateTime getStartDate() { + return startDate; + } + + public void setStartDate(ZonedDateTime startDate) { + this.startDate = startDate; + } + + public ZonedDateTime getEndDate() { + return endDate; + } + + public void setEndDate(ZonedDateTime endDate) { + this.endDate = endDate; + } + + public Map getFilterMetadata() { + return filterMetadata; + } + + @JsonSetter + public void setFilterMetadata(Map filterMetadata) { + for (Map.Entry entry : filterMetadata.entrySet()) { + String key = entry.getKey(); + if (key == null || key.trim().isEmpty()) { + throw new BadPayloadException("EventFilter metadata key can not be blank!"); + } + String value = entry.getValue(); + if (value == null || value.trim().isEmpty()) { + throw new BadPayloadException("EventFilter metadata value can not be blank!"); + } + } + this.filterMetadata = filterMetadata; + } + + public String getNotifyUri() { + return notifyUri; + } + + public void setNotifyUri(String notifyUri) { + this.notifyUri = notifyUri; + } + + + public Boolean isMatchMetadata() { + return matchMetadata; + } + + public void setMatchMetadata(Boolean matchMetadata) { + this.matchMetadata = matchMetadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EventFilter)) { + return false; + } + EventFilter that = (EventFilter) o; + return Objects.equals(eventType, that.eventType) && Objects.equals(consumer, that.consumer); + } + + @Override + public int hashCode() { + return Objects.hash(eventType, consumer); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("eventType", eventType).add("consumer", consumer).toString(); + } + + public void partialUpdateFilter(EventFilter other) { + this.eventType = other.eventType; + this.consumer = other.consumer; + this.startDate = other.startDate; + this.endDate = other.endDate; + this.filterMetadata = other.filterMetadata; + this.notifyUri = other.notifyUri; + this.matchMetadata = other.matchMetadata; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/InterCloudAuthorization.java b/core-common/src/main/java/eu/arrowhead/common/database/InterCloudAuthorization.java new file mode 100644 index 00000000..b9f14154 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/InterCloudAuthorization.java @@ -0,0 +1,117 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +/** + * JPA entity class for storing inter-cloud authorization rights in the database. The consumer_cloud_id and arrowhead_service_id columns + * must be unique together.

The table contains foreign keys to {@link ArrowheadCloud} and {@link ArrowheadService}. A particular + * ArrowheadCloud - ArrowheadService pair is authorized if there is a database entry for it in this table. The existence of the + * database entry means the given cloud can consume the given service from an {@link ArrowheadSystem} inside the Local Cloud. + * + * @author Umlauf Zoltán + */ +@Entity +@Table(name = "inter_cloud_authorization", uniqueConstraints = {@UniqueConstraint(columnNames = {"consumer_cloud_id", "arrowhead_service_id"})}) +public class InterCloudAuthorization { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull + @JoinColumn(name = "consumer_cloud_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadCloud cloud; + + @Valid + @NotNull + @JoinColumn(name = "arrowhead_service_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadService service; + + public InterCloudAuthorization() { + } + + public InterCloudAuthorization(ArrowheadCloud cloud, ArrowheadService service) { + this.cloud = cloud; + this.service = service; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadCloud getCloud() { + return cloud; + } + + public void setCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof InterCloudAuthorization)) { + return false; + } + InterCloudAuthorization that = (InterCloudAuthorization) o; + return Objects.equals(cloud, that.cloud) && Objects.equals(service, that.service); + } + + @Override + public int hashCode() { + return Objects.hash(cloud, service); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("cloud", cloud).add("service", service).toString(); + } + + public void updateEntryWith(InterCloudAuthorization other) { + this.cloud = other.cloud; + this.service = other.service; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/IntraCloudAuthorization.java b/core-common/src/main/java/eu/arrowhead/common/database/IntraCloudAuthorization.java new file mode 100644 index 00000000..365b4a05 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/IntraCloudAuthorization.java @@ -0,0 +1,136 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +/** + * JPA entity class for storing intra-cloud (within the cloud) authorization rights in the database. The consumer_system_id, + * provider_system_id and arrowhead_service_id columns must be unique together.

The table contains foreign keys to {@link + * ArrowheadSystem} and {@link ArrowheadService}. A particular Consumer System/Provider System/Arrowhead Service trio is authorized if there is a + * database entry for it in this table. The existence of the database entry means the given Consumer System is authorized to consume the given + * Arrowhead Serice from the given Provider System inside the Local Cloud. The reverse of it is not authorized. + * + * @author Umlauf Zoltán + */ +@Entity +@Table(name = "intra_cloud_authorization", uniqueConstraints = { + @UniqueConstraint(columnNames = {"consumer_system_id", "provider_system_id", "arrowhead_service_id"})}) +public class IntraCloudAuthorization { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull + @JoinColumn(name = "consumer_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem consumer; + + @Valid + @NotNull + @JoinColumn(name = "provider_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem provider; + + @Valid + @NotNull + @JoinColumn(name = "arrowhead_service_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadService service; + + public IntraCloudAuthorization() { + } + + public IntraCloudAuthorization(ArrowheadSystem consumer, ArrowheadSystem provider, ArrowheadService service) { + this.consumer = consumer; + this.provider = provider; + this.service = service; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem providers) { + this.provider = providers; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof IntraCloudAuthorization)) { + return false; + } + IntraCloudAuthorization that = (IntraCloudAuthorization) o; + return Objects.equals(consumer, that.consumer) && Objects.equals(provider, that.provider) && Objects.equals(service, that.service); + } + + @Override + public int hashCode() { + return Objects.hash(consumer, provider, service); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("consumer", consumer).add("provider", provider).add("service", service).toString(); + } + + public void updateEntryWith(IntraCloudAuthorization other) { + this.consumer = other.consumer; + this.provider = other.provider; + this.service = other.service; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/Logs.java b/core-common/src/main/java/eu/arrowhead/common/database/Logs.java new file mode 100644 index 00000000..907c3d50 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/Logs.java @@ -0,0 +1,112 @@ +package eu.arrowhead.common.database; + +import java.sql.Timestamp; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "logs") +public class Logs { + + private int id; + private Timestamp date; + private String origin; + private String level; + private String message; + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Basic + @Column(name = "date") + public Timestamp getDate() { + return date; + } + + public void setDate(Timestamp date) { + this.date = date; + } + + @Basic + @Column(name = "origin") + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + @Basic + @Column(name = "level") + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + @Basic + @Column(name = "message", length = 2047) + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Logs logs = (Logs) o; + + if (id != logs.id) { + return false; + } + if (date != null ? !date.equals(logs.date) : logs.date != null) { + return false; + } + if (origin != null ? !origin.equals(logs.origin) : logs.origin != null) { + return false; + } + if (level != null ? !level.equals(logs.level) : logs.level != null) { + return false; + } + if (message != null ? !message.equals(logs.message) : logs.message != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = id; + result = 31 * result + (date != null ? date.hashCode() : 0); + result = 31 * result + (origin != null ? origin.hashCode() : 0); + result = 31 * result + (level != null ? level.hashCode() : 0); + result = 31 * result + (message != null ? message.hashCode() : 0); + return result; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/NeighborCloud.java b/core-common/src/main/java/eu/arrowhead/common/database/NeighborCloud.java new file mode 100644 index 00000000..d097b456 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/NeighborCloud.java @@ -0,0 +1,85 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.messages.GSDPoll; +import eu.arrowhead.common.messages.ICNProposal; +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +/** + * JPA entity class for storing NeighborCloud information in the database. The cloud_id column must be unique.

The database table + * belonging to this class is a subset of the {@link ArrowheadCloud} table. If an ArrowheadCloud can also be found in the + * NeighborCloud table, that means it is a trusted ArrowheadCloud, which can be queried during a Global Service Discovery, + * Inter-Cloud Negotiations (by the Gatekeeper) and token generation (by the Authorization). + * + * @author Umlauf Zoltán + * @see ArrowheadCloud + * @see GSDPoll + * @see ICNProposal + */ +@Entity +@Table(name = "neighbor_cloud", uniqueConstraints = {@UniqueConstraint(columnNames = {"cloud_id"})}) +public class NeighborCloud implements Serializable { + + @Id + @Valid + @JoinColumn(name = "cloud_id") + @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) //NOTE this still does not have the expected effect, maybe orphanRemoval = true on OneToX annotations? + private ArrowheadCloud cloud; + + public NeighborCloud() { + } + + public NeighborCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + public ArrowheadCloud getCloud() { + return cloud; + } + + public void setCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof NeighborCloud)) { + return false; + } + NeighborCloud that = (NeighborCloud) o; + return Objects.equals(cloud, that.cloud); + } + + @Override + public int hashCode() { + return Objects.hash(cloud); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("cloud", cloud).toString(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/OrchestrationStore.java b/core-common/src/main/java/eu/arrowhead/common/database/OrchestrationStore.java new file mode 100644 index 00000000..45cda536 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/OrchestrationStore.java @@ -0,0 +1,288 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.exception.BadPayloadException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import org.hibernate.annotations.Check; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import org.hibernate.annotations.Type; + +/** + * JPA entity class for storing OrchestrationStore information in the database. The arrowhead_service_id, consumer_system_id, + * priority and is_default columns must be unique together. The priority integer can not be negative.

The class implements + * the Comparable interface based on the priority field (but does not override the equals() method). + */ +@Entity +@Table(name = "orchestration_store", uniqueConstraints = { + @UniqueConstraint(columnNames = {"arrowhead_service_id", "consumer_system_id", "priority", "is_default"})}) +@Check(constraints = "provider_cloud_id IS NULL OR is_default = false") +public class OrchestrationStore implements Comparable { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull + @JoinColumn(name = "arrowhead_service_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadService service; + + @Valid + @NotNull + @JoinColumn(name = "consumer_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem consumer; + + @Valid + @NotNull + @JoinColumn(name = "provider_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem providerSystem; + + @Valid + @JoinColumn(name = "provider_cloud_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadCloud providerCloud; + + @Min(1) + private Integer priority = 0; + + @Column(name = "is_default") + @Type(type = "yes_no") + private Boolean defaultEntry = false; + + private String name; + + @Column(name = "last_updated") + private LocalDateTime lastUpdated; + + private String instruction; + + @JsonInclude(Include.NON_EMPTY) + @ElementCollection(fetch = FetchType.EAGER) + @MapKeyColumn(name = "attribute_key") + @Column(name = "attribute_value", length = 2047) + @CollectionTable(name = "orchestration_store_attributes", joinColumns = @JoinColumn(name = "store_entry_id")) + private Map attributes = new HashMap<>(); + + //Only to convert ServiceRegistryEntries to Store entries without data loss + @Transient + private String serviceURI; + + public OrchestrationStore() { + } + + public OrchestrationStore(ArrowheadService service, ArrowheadSystem consumer, ArrowheadSystem providerSystem, ArrowheadCloud providerCloud, + int priority) { + this.service = service; + this.consumer = consumer; + this.providerSystem = providerSystem; + this.providerCloud = providerCloud; + this.priority = priority; + } + + public OrchestrationStore(ArrowheadService service, ArrowheadSystem consumer, ArrowheadSystem providerSystem, ArrowheadCloud providerCloud, + int priority, boolean defaultEntry, String name, LocalDateTime lastUpdated, String instruction, + Map attributes, String serviceURI) { + this.service = service; + this.consumer = consumer; + this.providerSystem = providerSystem; + this.providerCloud = providerCloud; + this.priority = priority; + this.defaultEntry = defaultEntry; + this.name = name; + this.lastUpdated = lastUpdated; + this.instruction = instruction; + this.attributes = attributes; + this.serviceURI = serviceURI; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadSystem getProviderSystem() { + return providerSystem; + } + + public void setProviderSystem(ArrowheadSystem providerSystem) { + this.providerSystem = providerSystem; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + + public Boolean isDefaultEntry() { + return defaultEntry; + } + + public void setDefaultEntry(Boolean defaultEntry) { + this.defaultEntry = defaultEntry; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LocalDateTime getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(LocalDateTime lastUpdated) { + this.lastUpdated = lastUpdated; + } + + public String getInstruction() { + return instruction; + } + + public void setInstruction(String instruction) { + this.instruction = instruction; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + /** + * Note: This class has a natural ordering that is inconsistent with equals().

The field priority is used to sort instances of this class + * in a collection. Priority is non-negative. If this.priority < other.priority that means this is more ahead in a collection than + * other and therefore has a higher priority. This means priority = 0 is the highest priority for a Store entry. + */ + @Override + public int compareTo(OrchestrationStore other) { + return this.priority - other.priority; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OrchestrationStore)) { + return false; + } + OrchestrationStore that = (OrchestrationStore) o; + return Objects.equals(service, that.service) && Objects.equals(consumer, that.consumer) && Objects.equals(providerSystem, that.providerSystem) + && Objects.equals(providerCloud, that.providerCloud) && Objects.equals(priority, that.priority) && Objects + .equals(defaultEntry, that.defaultEntry); + } + + @Override + public int hashCode() { + return Objects.hash(service, consumer, providerSystem, providerCloud, priority, defaultEntry); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("service", service).add("consumer", consumer).add("providerSystem", providerSystem) + .add("providerCloud", providerCloud).add("priority", priority).add("defaultEntry", defaultEntry).toString(); + } + + public void validateCrossParameterConstraints() { + if (defaultEntry && providerCloud != null) { + throw new BadPayloadException("Default store entries can only have intra-cloud providers!"); + } + } + + public void updateEntryWith(OrchestrationStore other) { + this.service = other.service; + this.consumer = other.consumer; + this.providerSystem = other.providerSystem; + this.providerCloud = other.providerCloud; + this.priority = other.priority; + this.defaultEntry = other.defaultEntry; + this.name = other.name; + this.lastUpdated = other.lastUpdated; + this.instruction = other.instruction; + this.attributes = other.attributes; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/OwnCloud.java b/core-common/src/main/java/eu/arrowhead/common/database/OwnCloud.java new file mode 100644 index 00000000..e65bceea --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/OwnCloud.java @@ -0,0 +1,84 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.messages.GSDPoll; +import eu.arrowhead.common.messages.ICNProposal; +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +/** + * JPA entity class for storing OwnCloud information in the database. The cloud_id column must be unique.

The database table + * belonging to this class stores the information about the Local Cloud, meaning this table should only have 1 entry at all times. The information in + * this table is used during Global Service Discovery, Inter-Cloud Negotiations (by the Gatekeeper) and token generation (by the Authorization). + * + * @author Umlauf Zoltán + * @see ArrowheadCloud + * @see GSDPoll + * @see ICNProposal + */ +@Entity +@Table(name = "own_cloud", uniqueConstraints = {@UniqueConstraint(columnNames = {"cloud_id"})}) +public class OwnCloud implements Serializable { + + @Id + @Valid + @JoinColumn(name = "cloud_id") + @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadCloud cloud; + + public OwnCloud() { + } + + public OwnCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + public ArrowheadCloud getCloud() { + return cloud; + } + + public void setCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof OwnCloud)) { + return false; + } + OwnCloud ownCloud = (OwnCloud) o; + return Objects.equals(cloud, ownCloud.cloud); + } + + @Override + public int hashCode() { + return Objects.hash(cloud); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("cloud", cloud).toString(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/ServiceRegistryEntry.java b/core-common/src/main/java/eu/arrowhead/common/database/ServiceRegistryEntry.java new file mode 100644 index 00000000..5fc05dee --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/ServiceRegistryEntry.java @@ -0,0 +1,220 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.base.MoreObjects; +import eu.arrowhead.common.json.constraint.LDTInFuture; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import org.hibernate.annotations.Type; + +@Entity +@Table(name = "service_registry", uniqueConstraints = {@UniqueConstraint(columnNames = {"arrowhead_service_id", "provider_system_id"})}) +public class ServiceRegistryEntry { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull(message = "Provided ArrowheadService cannot be null") + @JoinColumn(name = "arrowhead_service_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadService providedService; + + @Valid + @NotNull(message = "Provider ArrowheadSystem cannot be null") + @JoinColumn(name = "provider_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem provider; + + @Column(name = "service_uri") + @Size(max = 255, message = "Service URI must be 255 character at max") + private String serviceURI; + + @Type(type = "yes_no") + private Boolean udp = false; + + @Column(name = "end_of_validity") + @LDTInFuture(message = "End of validity date must be in the future") + private LocalDateTime endOfValidity; + + private Integer version = 1; + + //Takes the providedService metadata map + @JsonIgnore + private String metadata; + + public ServiceRegistryEntry() { + } + + public ServiceRegistryEntry(ArrowheadService providedService, ArrowheadSystem provider, String serviceURI) { + this.providedService = providedService; + this.provider = provider; + this.serviceURI = serviceURI; + } + + public ServiceRegistryEntry(ArrowheadService providedService, ArrowheadSystem provider, String serviceURI, boolean udp, LocalDateTime endOfValidity, + int version) { + this.providedService = providedService; + this.provider = provider; + this.serviceURI = serviceURI; + this.udp = udp; + this.endOfValidity = endOfValidity; + this.version = version; + } + + public ServiceRegistryEntry(ArrowheadService providedService, ArrowheadSystem provider, String serviceURI, Boolean udp, LocalDateTime endOfValidity, + Integer version, String metadata) { + this.providedService = providedService; + this.provider = provider; + this.serviceURI = serviceURI; + this.udp = udp; + this.endOfValidity = endOfValidity; + this.version = version; + this.metadata = metadata; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadService getProvidedService() { + return providedService; + } + + public void setProvidedService(ArrowheadService providedService) { + this.providedService = providedService; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + + public Boolean isUdp() { + return udp; + } + + public void setUdp(Boolean udp) { + this.udp = udp; + } + + public LocalDateTime getEndOfValidity() { + return endOfValidity; + } + + public void setEndOfValidity(LocalDateTime endOfValidity) { + this.endOfValidity = endOfValidity; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ServiceRegistryEntry)) { + return false; + } + ServiceRegistryEntry that = (ServiceRegistryEntry) o; + return Objects.equals(providedService, that.providedService) && Objects.equals(provider, that.provider) && Objects + .equals(serviceURI, that.serviceURI) && Objects.equals(version, that.version); + } + + @Override + public int hashCode() { + return Objects.hash(providedService, provider, serviceURI, version); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("providedService", providedService).add("provider", provider).add("serviceURI", serviceURI) + .add("version", version).toString(); + } + + public void toDatabase() { + if (providedService.getServiceMetadata() != null && !providedService.getServiceMetadata().isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : providedService.getServiceMetadata().entrySet()) { + sb.append(entry.getKey()).append("=").append(entry.getValue()).append(","); + } + metadata = sb.toString().substring(0, sb.length() - 1); + } + } + + public void fromDatabase(boolean removeId) { + if (removeId) { + providedService.setId(null); + } + if (metadata != null && metadata.trim().length() > 0) { + String[] parts = metadata.split(","); + providedService.getServiceMetadata().clear(); + for (String part : parts) { + String[] pair = part.split("="); + providedService.getServiceMetadata().put(pair[0], pair[1]); + } + } + } + + public void updateEntryWith(ServiceRegistryEntry other) { + this.providedService = other.providedService; + this.provider = other.provider; + this.serviceURI = other.serviceURI; + this.udp = other.udp; + this.endOfValidity = other.endOfValidity; + this.version = other.version; + this.metadata = other.metadata; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/database/SystemRegistryEntry.java b/core-common/src/main/java/eu/arrowhead/common/database/SystemRegistryEntry.java new file mode 100644 index 00000000..58d089b8 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/SystemRegistryEntry.java @@ -0,0 +1,155 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database; + +import eu.arrowhead.common.json.constraint.LDTInFuture; +import java.time.LocalDateTime; +import java.util.Objects; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +@Entity +@Table(name = "system_registry", uniqueConstraints = {@UniqueConstraint(columnNames = {"arrowhead_system_id", "provider_device_id"})}) +public class SystemRegistryEntry { + + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private Long id; + + @Valid + @NotNull(message = "Provided ArrowheadSystem cannot be null") + @JoinColumn(name = "arrowhead_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadSystem providedSystem; + + @Valid + @NotNull(message = "Provider ArrowheadDevice cannot be null") + @JoinColumn(name = "provider_device_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @OnDelete(action = OnDeleteAction.CASCADE) + private ArrowheadDevice provider; + + @Column(name = "service_uri") + @Size(max = 255, message = "Service URI must be 255 character at max") + private String serviceURI; + + @Column(name = "end_of_validity") + @LDTInFuture(message = "End of validity date cannot be in the past") + private LocalDateTime endOfValidity; + + public SystemRegistryEntry() { + } + + public SystemRegistryEntry(Long id, @Valid @NotNull(message = "Provided ArrowheadSystem cannot be null") ArrowheadSystem providedSystem, + @Valid @NotNull(message = "Provider ArrowheadDevice cannot be null") ArrowheadDevice provider, + @Size(max = 255, message = "Service URI must be 255 character at max") String serviceURI, + @LDTInFuture(message = "End of validity date cannot be in the past") LocalDateTime endOfValidity) { + this.id = id; + this.providedSystem = providedSystem; + this.provider = provider; + this.serviceURI = serviceURI; + this.endOfValidity = endOfValidity; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ArrowheadSystem getProvidedSystem() { + return providedSystem; + } + + public void setProvidedSystem(ArrowheadSystem providedSystem) { + this.providedSystem = providedSystem; + } + + public ArrowheadDevice getProvider() { + return provider; + } + + public void setProvider(ArrowheadDevice provider) { + this.provider = provider; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + public LocalDateTime getEndOfValidity() { + return endOfValidity; + } + + public void setEndOfValidity(LocalDateTime endOfValidity) { + this.endOfValidity = endOfValidity; + } + + @Override + public int hashCode() { + return Objects.hash(provider, providedSystem, serviceURI, endOfValidity); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SystemRegistryEntry other = (SystemRegistryEntry) obj; + + return Objects.equals(this.provider, other.provider) && Objects.equals(this.providedSystem, other.providedSystem) && Objects + .equals(this.serviceURI, other.serviceURI) && Objects.equals(this.endOfValidity, other.endOfValidity); + } + + protected void append(final StringBuilder builder) { + builder.append("id=").append(id); + builder.append(", providedSystem=").append(providedSystem); + builder.append(", provider=").append(provider); + builder.append(", serviceURI=").append(serviceURI); + builder.append(", endOfValidity=").append(endOfValidity); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append(" ["); + append(builder); + builder.append("]"); + return builder.toString(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/DeployedSystem.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/DeployedSystem.java new file mode 100644 index 00000000..0c6d9664 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/DeployedSystem.java @@ -0,0 +1,77 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import eu.arrowhead.common.database.ArrowheadSystem; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "deployed_system", uniqueConstraints = {@UniqueConstraint(columnNames = {"arrowhead_system_id"})}) +public class DeployedSystem { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + @JoinColumn(name = "arrowhead_system_id") + private ArrowheadSystem system; + + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + @JoinColumn(name = "network_device_id") + private NetworkDevice networkDevice; + + public DeployedSystem() { + } + + public DeployedSystem(ArrowheadSystem system, NetworkDevice networkDevice) { + this.system = system; + this.networkDevice = networkDevice; + } + + @XmlTransient + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public ArrowheadSystem getSystem() { + return system; + } + + public void setSystem(ArrowheadSystem system) { + this.system = system; + } + + public NetworkDevice getNetworkDevice() { + return networkDevice; + } + + public void setNetworkDevice(NetworkDevice networkDevice) { + this.networkDevice = networkDevice; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/MessageStream.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/MessageStream.java new file mode 100644 index 00000000..18aa43ea --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/MessageStream.java @@ -0,0 +1,138 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "message_stream", uniqueConstraints = { + @UniqueConstraint(columnNames = {"arrowhead_service_id", "consumer_system_id", "provider_system_id"})}) +public class MessageStream { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @JoinColumn(name = "arrowhead_service_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + private ArrowheadService service; + + @JoinColumn(name = "consumer_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + private ArrowheadSystem consumer; + + @JoinColumn(name = "provider_system_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + private ArrowheadSystem provider; + + @JoinColumn(name = "resource_reservation_id") + @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + private ResourceReservation qualityOfService; + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "config_key") + @Column(name = "config_value") + @CollectionTable(name = "message_stream_config_map", joinColumns = @JoinColumn(name = "id")) + private Map configuration = new HashMap<>(); + + @Column(name = "type") + private String type; + + public MessageStream() { + } + + public MessageStream(ArrowheadService service, ArrowheadSystem consumer, ArrowheadSystem provider, ResourceReservation qualityOfService, + Map configuration, String type) { + this.service = service; + this.consumer = consumer; + this.provider = provider; + this.qualityOfService = qualityOfService; + this.configuration = configuration; + this.type = type; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public ResourceReservation getQualityOfService() { + return qualityOfService; + } + + public void setQualityOfService(ResourceReservation qualityOfService) { + this.qualityOfService = qualityOfService; + } + + public Map getConfiguration() { + return configuration; + } + + public void setConfiguration(Map configuration) { + this.configuration = configuration; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + /** + * This method generates the code that also identifies the message stream. + * + * @return Returns String with the code. + */ + + public String getCode() { + return provider.getSystemName() + "," + consumer.getSystemName() + "," + service.getServiceDefinition(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/Network.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/Network.java new file mode 100644 index 00000000..abcb0f4f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/Network.java @@ -0,0 +1,103 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "network", uniqueConstraints = {@UniqueConstraint(columnNames = {"network_name"})}) +public class Network { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @Column(name = "network_name") + private String name; + + @Column(name = "network_ip") + private String networkIp; + + @Column(name = "network_type") + private String networkType; + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "config_key") + @Column(name = "config_value") + @CollectionTable(name = "network_config_map", joinColumns = @JoinColumn(name = "id")) + private Map networkConfigurations = new HashMap<>(); + + public Network() { + } + + public Network(String name, String networkIp, String networkType, Map networkConfigurations) { + this.name = name; + this.networkIp = networkIp; + this.networkType = networkType; + this.networkConfigurations = networkConfigurations; + } + + @XmlTransient + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNetworkIp() { + return networkIp; + } + + public void setNetworkIp(String networkIp) { + this.networkIp = networkIp; + } + + public String getNetworkType() { + return networkType; + } + + public void setNetworkType(String networkType) { + this.networkType = networkType; + } + + public Map getNetworkConfigurations() { + return networkConfigurations; + } + + public void setNetworkConfigurations(Map networkConfigurations) { + this.networkConfigurations = networkConfigurations; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/NetworkDevice.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/NetworkDevice.java new file mode 100644 index 00000000..e31d5c89 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/NetworkDevice.java @@ -0,0 +1,156 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "network_device", uniqueConstraints = {@UniqueConstraint(columnNames = {"mac_address"})}) +public class NetworkDevice { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @Column(name = "name") + private String name; + + @Column(name = "mac_address") + private String macAddress; + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "capability_key") + @Column(name = "capability_value") + @CollectionTable(name = "network_device_network_capabilities", joinColumns = @JoinColumn(name = "id")) + private Map networkCapabilities = new HashMap<>(); + + @JoinColumn(name = "network_id") + @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE}) + private Network network; + + public NetworkDevice() { + } + + public NetworkDevice(String name, String macAddress, Map networkCapabilities, Network network) { + this.name = name; + this.macAddress = macAddress; + this.networkCapabilities = networkCapabilities; + this.network = network; + } + + /** + * Get ID. + * + * @return Returns integer with ID. + */ + @XmlTransient + public int getId() { + return id; + } + + /** + * Set ID. + * + * @param id Integer with ID. + */ + public void setId(int id) { + this.id = id; + } + + /** + * Get Name of network device. + * + * @return Returns string with name. + */ + public String getName() { + return name; + } + + /** + * Set name of network device. + * + * @param name String with name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get MAC Address. + * + * @return Returns a String with a MAC Address. + */ + public String getMacAddress() { + return macAddress; + } + + /** + * Set MAC Address. + * + * @param macAddress String with MAC address. + */ + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + /** + * Get Network Device Capabilities (ex. bandwidth) + * + * @return Returns Map with capabilities. + */ + public Map getNetworkCapabilities() { + return networkCapabilities; + } + + /** + * Set Network Capabilities. + * + * @param networkCapabilities Map with network device capabilities. + */ + public void setNetworkCapabilities(Map networkCapabilities) { + this.networkCapabilities = networkCapabilities; + } + + /** + * Get network where the network device is included.. + * + * @return Returns network. + */ + public Network getNetwork() { + return network; + } + + /** + * Set Network. + * + * @param network Network. + */ + public void setNetwork(Network network) { + this.network = network; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/Node.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/Node.java new file mode 100644 index 00000000..99916574 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/Node.java @@ -0,0 +1,96 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "node", uniqueConstraints = {@UniqueConstraint(columnNames = {"device_model_code"})}) +public class Node { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @Column(name = "device_model_code") + private String deviceModelCode; + + @JoinColumn(name = "deployed_system_id") + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE) + private List deployedSystems = new ArrayList<>(); + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "capability_key") + @Column(name = "capability_value") + @CollectionTable(name = "node_processing_capabilities", joinColumns = @JoinColumn(name = "id")) + private Map processingCapabilities = new HashMap<>(); + + public Node() { + } + + public Node(String deviceModelCode, List deployedSystems, Map processingCapabilities) { + this.deviceModelCode = deviceModelCode; + this.deployedSystems = deployedSystems; + this.processingCapabilities = processingCapabilities; + } + + @XmlTransient + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDeviceModelCode() { + return deviceModelCode; + } + + public void setDeviceModelCode(String deviceModelCode) { + this.deviceModelCode = deviceModelCode; + } + + public List getDeployedSystems() { + return deployedSystems; + } + + public void setDeployedSystems(List deployedSystems) { + this.deployedSystems = deployedSystems; + } + + public Map getProcessingCapabilities() { + return processingCapabilities; + } + + public void setProcessingCapabilities(Map processingCapabilities) { + this.processingCapabilities = processingCapabilities; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/ResourceReservation.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/ResourceReservation.java new file mode 100644 index 00000000..3bad2b68 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/ResourceReservation.java @@ -0,0 +1,108 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "resource_reservation") +public class ResourceReservation { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private int id; + + @Column(name = "state") + private String state; + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "qos_key") + @Column(name = "qos_value") + @CollectionTable(name = "resource_reservation_qos_parameters", joinColumns = @JoinColumn(name = "id")) + private Map qosParameters = new HashMap<>(); + + public ResourceReservation() { + } + + public ResourceReservation(String state, Map qosParameters) { + this.state = state; + this.qosParameters = qosParameters; + } + + /** + * Get ID. + * + * @return returns integer with ID + */ + @XmlTransient + public int getId() { + return id; + } + + /** + * set ID. + * + * @param id integer ID + */ + public void setId(int id) { + this.id = id; + } + + /** + * get state of qos reservation. + * + * @return returns state + */ + public String getState() { + return state; + } + + /** + * set qos reservation state. + * + * @param state qos reservation state + */ + public void setState(String state) { + this.state = state; + } + + /** + * get qos parameters. + * + * @return returns map with qos parameters + */ + public Map getQosParameters() { + return qosParameters; + } + + /** + * set qos parameters. + * + * @param qosParameter map with the qos parameters + */ + public void setQosParameter(Map qosParameter) { + this.qosParameters = qosParameter; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/database/qos/Topology.java b/core-common/src/main/java/eu/arrowhead/common/database/qos/Topology.java new file mode 100644 index 00000000..1672f86a --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/database/qos/Topology.java @@ -0,0 +1,78 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.database.qos; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlTransient; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = "topology") +public class Topology { + + @Column(name = "id") + @Id + @GenericGenerator(name = "table_generator", strategy = "org.hibernate.id.enhanced.TableGenerator") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_generator") + private String id; + + @ElementCollection(fetch = FetchType.LAZY) + @MapKeyColumn(name = "config_key") + @Column(name = "config_value") + @CollectionTable(name = "topology_config_map", joinColumns = @JoinColumn(name = "id")) + private Map configurations = new HashMap<>(); + + @Column(name = "status") + private String status; + + public Topology() { + } + + public Topology(Map configurations, String status) { + this.configurations = configurations; + this.status = status; + } + + @XmlTransient + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Map getConfigurations() { + return configurations; + } + + public void setConfigurations(Map configurations) { + this.configurations = configurations; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadException.java b/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadException.java new file mode 100644 index 00000000..9441c8cb --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadException.java @@ -0,0 +1,69 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +public class ArrowheadException extends RuntimeException { + + private static final long serialVersionUID = 1L; + private ExceptionType exceptionType = ExceptionType.ARROWHEAD; + private final int errorCode; + private final String origin; + + public ArrowheadException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, cause); + this.errorCode = errorCode; + this.origin = origin; + } + + public ArrowheadException(final String msg, final int errorCode, final String origin) { + super(msg); + this.errorCode = errorCode; + this.origin = origin; + } + + public ArrowheadException(final String msg, final int errorCode, final Throwable cause) { + super(msg, cause); + this.errorCode = errorCode; + this.origin = null; + } + + public ArrowheadException(final String msg, final int errorCode) { + super(msg); + this.errorCode = errorCode; + this.origin = null; + } + + public ArrowheadException(final String msg, final Throwable cause) { + super(msg, cause); + this.errorCode = 0; + this.origin = null; + } + + public ArrowheadException(final String msg) { + super(msg); + this.errorCode = 0; + this.origin = null; + } + + public ExceptionType getExceptionType() { + return exceptionType; + } + + void setExceptionType(ExceptionType exceptionType) { + this.exceptionType = exceptionType; + } + + public int getErrorCode() { + return errorCode; + } + + public String getOrigin() { + return origin; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadExceptionMapper.java new file mode 100644 index 00000000..9db122f8 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ArrowheadExceptionMapper.java @@ -0,0 +1,65 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.apache.log4j.Logger; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; + +@Provider +public class ArrowheadExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + @Inject + private javax.inject.Provider responseContext; + + private static final Logger log = Logger.getLogger(ArrowheadExceptionMapper.class.getName()); + + @Override + public Response toResponse(ArrowheadException ex) { + ex.printStackTrace(); + String origin = + ex.getOrigin() != null ? ex.getOrigin() : (requestContext.get() != null ? requestContext.get().getAbsolutePath().toString() : "unknown"); + int errorCode = (ex.getErrorCode() == 0 && responseContext.get() != null) ? responseContext.get().getStatus() : ex.getErrorCode(); + if (errorCode == 0) { + switch (ex.getExceptionType()) { + case AUTH: + errorCode = Status.UNAUTHORIZED.getStatusCode(); + break; + case BAD_PAYLOAD: + errorCode = Status.BAD_REQUEST.getStatusCode(); + log.error("BadPayloadException at: " + origin); + break; + case DATA_NOT_FOUND: + errorCode = Status.NOT_FOUND.getStatusCode(); + break; + case DUPLICATE_ENTRY: + errorCode = Status.BAD_REQUEST.getStatusCode(); + break; + case JSON_PROCESSING: + errorCode = Status.BAD_REQUEST.getStatusCode(); + break; + case UNAVAILABLE: + errorCode = Status.GATEWAY_TIMEOUT.getStatusCode(); + break; + default: + errorCode = Status.INTERNAL_SERVER_ERROR.getStatusCode(); + } + } + + ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), errorCode, ex.getExceptionType(), origin); + return Response.status(errorCode).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/AuthException.java b/core-common/src/main/java/eu/arrowhead/common/exception/AuthException.java new file mode 100644 index 00000000..72780899 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/AuthException.java @@ -0,0 +1,48 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Used throughout the project to signal authentication and authorization related problems. Results in a 401 HTTP code if it happens during normal + * operation (after core systems servers started listening for requests).

If this exception is thrown at server startup, it signals a problem with + * a certificate file. Runtime example can be when a system tries to access an existing resource which is not allowed by the + * AccessControlFilter of the core system. + */ +public class AuthException extends ArrowheadException { + + public AuthException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.AUTH); + } + + public AuthException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.AUTH); + } + + public AuthException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.AUTH); + } + + public AuthException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.AUTH); + } + + public AuthException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.AUTH); + } + + public AuthException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.AUTH); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/BadMethodExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/BadMethodExceptionMapper.java new file mode 100644 index 00000000..7050e0b6 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/BadMethodExceptionMapper.java @@ -0,0 +1,37 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import javax.inject.Inject; +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; + +@Provider +public class BadMethodExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + + public Response toResponse(NotAllowedException ex) { + ex.printStackTrace(); + ErrorMessage errorMessage; + if (ex.getMessage() != null) { + errorMessage = new ErrorMessage(ex.getMessage(), 405, ExceptionType.BAD_METHOD, requestContext.get().getBaseUri().toString()); + } else { + errorMessage = new ErrorMessage(requestContext.get().getMethod() + " is not allowed at " + requestContext.get().getPath(true), 405, + ExceptionType.BAD_METHOD, requestContext.get().getBaseUri().toString()); + } + + return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/BadPayloadException.java b/core-common/src/main/java/eu/arrowhead/common/exception/BadPayloadException.java new file mode 100644 index 00000000..2a2dd958 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/BadPayloadException.java @@ -0,0 +1,45 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Thrown if a resource receives a HTTP payload which have missing mandatory fields. + */ +public class BadPayloadException extends ArrowheadException { + + public BadPayloadException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + + public BadPayloadException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + + public BadPayloadException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + + public BadPayloadException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + + public BadPayloadException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + + public BadPayloadException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.BAD_PAYLOAD); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/BadURIExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/BadURIExceptionMapper.java new file mode 100644 index 00000000..1e58e5f2 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/BadURIExceptionMapper.java @@ -0,0 +1,31 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import javax.inject.Inject; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; + +@Provider +public class BadURIExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + + public Response toResponse(NotFoundException ex) { + ex.printStackTrace(); + ErrorMessage errorMessage = new ErrorMessage(requestContext.get().getPath(true) + " is not a valid path!", 400, ExceptionType.BAD_URI, + requestContext.get().getBaseUri().toString()); + return Response.status(Status.BAD_REQUEST).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ConstraintViolationExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/ConstraintViolationExceptionMapper.java new file mode 100644 index 00000000..f0807f14 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ConstraintViolationExceptionMapper.java @@ -0,0 +1,37 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import javax.inject.Inject; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; + +@Provider +public class ConstraintViolationExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + + @Override + public Response toResponse(ConstraintViolationException ex) { + ex.printStackTrace(); + StringBuilder messageBuilder = new StringBuilder(); + for (ConstraintViolation cv : ex.getConstraintViolations()) { + messageBuilder.append(cv.getPropertyPath()).append(": ").append(cv.getMessage()).append(", "); + } + int errorCode = 400; //Bad Request + String origin = requestContext.get() != null ? requestContext.get().getAbsolutePath().toString() : "unknown"; + + ErrorMessage errorMessage = new ErrorMessage(messageBuilder.toString(), errorCode, ExceptionType.BAD_PAYLOAD, origin); + return Response.status(errorCode).entity(errorMessage).header("Content-type", "application/json").build(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/DataNotFoundException.java b/core-common/src/main/java/eu/arrowhead/common/exception/DataNotFoundException.java new file mode 100644 index 00000000..2c1a810a --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/DataNotFoundException.java @@ -0,0 +1,46 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Usually thrown by the Core System resources if a crucial database query (for example the query of the Service Registry or the Authorization) comes + * back empty during the servicing of the request. + */ +public class DataNotFoundException extends ArrowheadException { + + public DataNotFoundException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + + public DataNotFoundException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + + public DataNotFoundException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + + public DataNotFoundException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + + public DataNotFoundException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + + public DataNotFoundException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.DATA_NOT_FOUND); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/DnsException.java b/core-common/src/main/java/eu/arrowhead/common/exception/DnsException.java new file mode 100644 index 00000000..0bbb0563 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/DnsException.java @@ -0,0 +1,44 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Used by the legacy Service Registry, to handle DNS-SD related exceptions. + */ +public class DnsException extends ArrowheadException { + + public DnsException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.DNS_SD); + } + + public DnsException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.DNS_SD); + } + + public DnsException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.DNS_SD); + } + + public DnsException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.DNS_SD); + } + + public DnsException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.DNS_SD); + } + + public DnsException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.DNS_SD); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/DriverNotFoundException.java b/core-common/src/main/java/eu/arrowhead/common/exception/DriverNotFoundException.java new file mode 100644 index 00000000..af8c1e19 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/DriverNotFoundException.java @@ -0,0 +1,32 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. + * + * This work was supported by National Funds through FCT (Portuguese + * Foundation for Science and Technology) and by the EU ECSEL JU + * funding, within Arrowhead project, ref. ARTEMIS/0001/2012, + * JU grant nr. 332987. + * ISEP, Polytechnic Institute of Porto. + */ + +/** + * Related to the drivers of each communication protocol. Whenever the DriverFactory doesn't recognize the type, this exception will be launched. + * + * @author CISTER/INESC-TEC + */ +public class DriverNotFoundException extends RuntimeException { + + public DriverNotFoundException(String message) { + super(message); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/DuplicateEntryException.java b/core-common/src/main/java/eu/arrowhead/common/exception/DuplicateEntryException.java new file mode 100644 index 00000000..e59391d1 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/DuplicateEntryException.java @@ -0,0 +1,47 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Used by the DatabaseManager class when Hibernate ConstraintViolationExceptions happen. This can happen when trying to save a new + * object with the same unique constraint fields as an already existing entry, or when trying to delete an entry which has foreign key constraints in + * other tables. + */ +public class DuplicateEntryException extends ArrowheadException { + + public DuplicateEntryException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + + public DuplicateEntryException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + + public DuplicateEntryException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + + public DuplicateEntryException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + + public DuplicateEntryException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + + public DuplicateEntryException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.DUPLICATE_ENTRY); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ErrorMessage.java b/core-common/src/main/java/eu/arrowhead/common/exception/ErrorMessage.java new file mode 100644 index 00000000..308def19 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ErrorMessage.java @@ -0,0 +1,68 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +public class ErrorMessage { + + private String errorMessage; + private int errorCode; + private ExceptionType exceptionType; + private String origin; + private String documentation = "https://github.com/arrowhead-f/core-java/tree/master/documentation"; + + public ErrorMessage() { + } + + public ErrorMessage(String errorMessage, int errorCode, ExceptionType exceptionType, String origin) { + this.errorMessage = errorMessage; + this.errorCode = errorCode; + this.exceptionType = exceptionType; + this.origin = origin; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public ExceptionType getExceptionType() { + return exceptionType; + } + + public void setExceptionType(ExceptionType exceptionType) { + this.exceptionType = exceptionType; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public String getDocumentation() { + return documentation; + } + + public void setDocumentation(String documentation) { + this.documentation = documentation; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ExceptionType.java b/core-common/src/main/java/eu/arrowhead/common/exception/ExceptionType.java new file mode 100644 index 00000000..8b47fa42 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ExceptionType.java @@ -0,0 +1,23 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +public enum ExceptionType { + ARROWHEAD, + AUTH, + BAD_MEDIA_TYPE, + BAD_METHOD, + BAD_PAYLOAD, + BAD_URI, + DATA_NOT_FOUND, + DNS_SD, + DUPLICATE_ENTRY, + GENERIC, + JSON_PROCESSING, + UNAVAILABLE +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/GenericExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/GenericExceptionMapper.java new file mode 100644 index 00000000..63fffb13 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/GenericExceptionMapper.java @@ -0,0 +1,39 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status.Family; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; + +@Provider +public class GenericExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + @Inject + private javax.inject.Provider responseContext; + + @Override + public Response toResponse(Throwable t) { + t.printStackTrace(); + int errorCode = 500; //Internal Server Error + String origin = requestContext.get() != null ? requestContext.get().getAbsolutePath().toString() : "unknown"; + if (responseContext.get() != null && responseContext.get().getStatusInfo().getFamily() != Family.OTHER) { + errorCode = responseContext.get().getStatus(); + } + + ErrorMessage errorMessage = new ErrorMessage(t.getClass().toString() + ": " + t.getMessage(), errorCode, ExceptionType.GENERIC, origin); + return Response.status(errorCode).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/JsonMappingExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/JsonMappingExceptionMapper.java new file mode 100644 index 00000000..3b538086 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/JsonMappingExceptionMapper.java @@ -0,0 +1,42 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import com.fasterxml.jackson.databind.JsonMappingException; +import javax.annotation.Priority; +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status.Family; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; + +@Provider +@Priority(1) //This is needed in order to give this Mapper higher priority over Jackson's own implementation +public class JsonMappingExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + @Inject + private javax.inject.Provider responseContext; + + @Override + public Response toResponse(JsonMappingException ex) { + ex.printStackTrace(); + int errorCode = 400; //Bad Request + String origin = requestContext.get() != null ? requestContext.get().getAbsolutePath().toString() : "unknown"; + if (responseContext.get() != null && responseContext.get().getStatusInfo().getFamily() != Family.OTHER) { + errorCode = responseContext.get().getStatus(); + } + + ErrorMessage errorMessage = new ErrorMessage("JsonMappingException: " + ex.getMessage(), errorCode, ExceptionType.JSON_PROCESSING, origin); + return Response.status(errorCode).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/JsonParseExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/JsonParseExceptionMapper.java new file mode 100644 index 00000000..26231805 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/JsonParseExceptionMapper.java @@ -0,0 +1,43 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +import com.fasterxml.jackson.core.JsonParseException; +import javax.annotation.Priority; +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status.Family; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; +import org.glassfish.jersey.server.ContainerResponse; + +@Provider +@Priority(1) //This is needed in order to give this Mapper higher priority over Jackson's own implementation +public class JsonParseExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + @Inject + private javax.inject.Provider responseContext; + + @Override + public Response toResponse(JsonParseException ex) { + ex.printStackTrace(); + int errorCode = 400; //Bad Request + String origin = requestContext.get() != null ? requestContext.get().getAbsolutePath().toString() : "unknown"; + if (responseContext.get() != null && responseContext.get().getStatusInfo().getFamily() != Family.OTHER) { + errorCode = responseContext.get().getStatus(); + } + + ErrorMessage errorMessage = new ErrorMessage("JsonParseException: " + ex.getMessage(), errorCode, ExceptionType.JSON_PROCESSING, origin); + return Response.status(errorCode).entity(errorMessage).header("Content-type", "application/json").build(); + } + +} + diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/NotSupportedMediaTypeExceptionMapper.java b/core-common/src/main/java/eu/arrowhead/common/exception/NotSupportedMediaTypeExceptionMapper.java new file mode 100644 index 00000000..c6cd05b3 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/NotSupportedMediaTypeExceptionMapper.java @@ -0,0 +1,35 @@ +package eu.arrowhead.common.exception; + +import java.util.List; +import javax.inject.Inject; +import javax.ws.rs.NotSupportedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import org.glassfish.jersey.server.ContainerRequest; + +@Provider +public class NotSupportedMediaTypeExceptionMapper implements ExceptionMapper { + + @Inject + private javax.inject.Provider requestContext; + + @Override + public Response toResponse(NotSupportedException ex) { + ex.printStackTrace(); + ErrorMessage errorMessage; + if (ex.getMessage() != null) { + errorMessage = new ErrorMessage(ex.getMessage(), 405, ExceptionType.BAD_MEDIA_TYPE, + requestContext.get().getBaseUri().toString()); + } else { + List requestContentType = requestContext.get().getHeaders().get("Content-Type"); + errorMessage = new ErrorMessage( + requestContentType.toString() + " is not allowed at " + requestContext.get().getPath(true), 405, + ExceptionType.BAD_MEDIA_TYPE, requestContext.get().getBaseUri().toString()); + } + + return Response.status(Status.UNSUPPORTED_MEDIA_TYPE).entity(errorMessage) + .header("Content-type", "application/json").build(); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/ReservationException.java b/core-common/src/main/java/eu/arrowhead/common/exception/ReservationException.java new file mode 100644 index 00000000..6db3ca43 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/ReservationException.java @@ -0,0 +1,23 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Used by the QoS core system when a problem occurs during the resource reservation. + */ +public class ReservationException extends RuntimeException { + + public ReservationException() { + super(); + } + + public ReservationException(String message) { + super(message); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/exception/UnavailableServerException.java b/core-common/src/main/java/eu/arrowhead/common/exception/UnavailableServerException.java new file mode 100644 index 00000000..a58475dc --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/exception/UnavailableServerException.java @@ -0,0 +1,45 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.exception; + +/** + * Thrown when a HTTP request times out because the endpoint is not available. + */ +public class UnavailableServerException extends ArrowheadException { + + public UnavailableServerException(final String msg, final int errorCode, final String origin, final Throwable cause) { + super(msg, errorCode, origin, cause); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + + public UnavailableServerException(final String msg, final int errorCode, final String origin) { + super(msg, errorCode, origin); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + + public UnavailableServerException(String msg, int errorCode, Throwable cause) { + super(msg, errorCode, cause); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + + public UnavailableServerException(String msg, int errorCode) { + super(msg, errorCode); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + + public UnavailableServerException(String msg, Throwable cause) { + super(msg, cause); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + + public UnavailableServerException(String msg) { + super(msg); + this.setExceptionType(ExceptionType.UNAVAILABLE); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/AccessControlFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/AccessControlFilter.java new file mode 100644 index 00000000..14856809 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/AccessControlFilter.java @@ -0,0 +1,53 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import eu.arrowhead.common.Utility; +import eu.arrowhead.common.exception.AuthException; +import eu.arrowhead.common.misc.SecurityUtils; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.SecurityContext; +import org.apache.log4j.Logger; + +public abstract class AccessControlFilter implements ContainerRequestFilter { + + protected static final Logger log = Logger.getLogger(AccessControlFilter.class.getName()); + @Context + protected Configuration configuration; + + @Override + public void filter(ContainerRequestContext requestContext) { + SecurityContext sc = requestContext.getSecurityContext(); + if (sc.isSecure()) { + String requestTarget = Utility.stripEndSlash(requestContext.getUriInfo().getRequestUri().toString()); + String requestJson = Utility.getRequestPayload(requestContext.getEntityStream()); + String commonName = SecurityUtils.getCertCNFromSubject(sc.getUserPrincipal().getName()); + if (!isClientAuthorized(commonName, requestContext.getMethod(), requestTarget, requestJson)) { + log.error(commonName + " is unauthorized to access " + requestTarget); + throw new AuthException(commonName + " is unauthorized to access " + requestTarget); + } + InputStream in = new ByteArrayInputStream(requestJson.getBytes(StandardCharsets.UTF_8)); + requestContext.setEntityStream(in); + } + } + + public boolean isClientAuthorized(String clientCN, String method, String requestTarget, String requestJson) { + String serverCN = (String) configuration.getProperty("server_common_name"); + String[] serverFields = serverCN.split("\\.", 2); + // serverFields contains: coreSystemName, cloudName.operator.arrowhead.eu + + //All requests from the local cloud are allowed + return SecurityUtils.isKeyStoreCNArrowheadValid(clientCN, serverFields[1]); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/CORSFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/CORSFilter.java new file mode 100644 index 00000000..79e10f54 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/CORSFilter.java @@ -0,0 +1,26 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.ext.Provider; + +@Provider +public class CORSFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext request, ContainerResponseContext response) { + response.getHeaders().add("Access-Control-Allow-Origin", "*"); + response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); + response.getHeaders().add("Access-Control-Allow-Credentials", "true"); + response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + response.getHeaders().add("Access-Control-Max-Age", 600); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/EmptyPayloadFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/EmptyPayloadFilter.java new file mode 100644 index 00000000..ef0a4d2a --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/EmptyPayloadFilter.java @@ -0,0 +1,42 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import eu.arrowhead.common.exception.ErrorMessage; +import eu.arrowhead.common.exception.ExceptionType; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.ENTITY_CODER) +public class EmptyPayloadFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) { + String method = requestContext.getMethod(); + int contentLength = requestContext.getLength(); + if ((method.equals("POST") || method.equals("PUT")) && contentLength == 0) { + ErrorMessage em = new ErrorMessage( + "Message body is null (unusual for POST/PUT request)! If you truly want to send an empty payload, try sending empty brackets: {} for " + + "JSONObject, [] for JSONArray.", 400, ExceptionType.BAD_PAYLOAD, requestContext.getUriInfo().getAbsolutePath().toString()); + requestContext.abortWith(Response.status(Status.BAD_REQUEST).entity(em).build()); + } + if ((method.equals(("GET")) || method.equals("DELETE")) && contentLength > 0) { + ErrorMessage em = new ErrorMessage("Message body is not null (unusual for GET/DELETE request)!", 400, ExceptionType.BAD_PAYLOAD, + requestContext.getUriInfo().getAbsolutePath().toString()); + requestContext.abortWith(Response.status(Status.BAD_REQUEST).entity(em).build()); + } + + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/InboundDebugFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/InboundDebugFilter.java new file mode 100644 index 00000000..268f4ca7 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/InboundDebugFilter.java @@ -0,0 +1,37 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import eu.arrowhead.common.Utility; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.USER) +public class InboundDebugFilter implements ContainerRequestFilter { + + @Override + public void filter(ContainerRequestContext requestContext) { + if (Boolean.valueOf(System.getProperty("debug_mode", "false"))) { + System.out.println("New " + requestContext.getMethod() + " request at: " + requestContext.getUriInfo().getRequestUri().toString()); + System.out.println("Timestamp: " + LocalDateTime.now()); + String prettyJson = Utility.getRequestPayload(requestContext.getEntityStream()); + System.out.println(prettyJson); + + InputStream in = new ByteArrayInputStream(prettyJson.getBytes(StandardCharsets.UTF_8)); + requestContext.setEntityStream(in); + } + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/OutboundDebugFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/OutboundDebugFilter.java new file mode 100644 index 00000000..1d079bf9 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/OutboundDebugFilter.java @@ -0,0 +1,33 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import eu.arrowhead.common.Utility; +import java.time.LocalDateTime; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.USER) +public class OutboundDebugFilter implements ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { + if (Boolean.valueOf(System.getProperty("debug_mode", "false"))) { + if (responseContext.getEntity() != null) { + System.out.println("Response to the request at: " + requestContext.getUriInfo().getRequestUri().toString()); + System.out.println("Timestamp: " + LocalDateTime.now()); + System.out.println(Utility.toPrettyJson(null, responseContext.getEntity())); + } + } + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/filter/SecurityFilter.java b/core-common/src/main/java/eu/arrowhead/common/filter/SecurityFilter.java new file mode 100644 index 00000000..cbf4354f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/filter/SecurityFilter.java @@ -0,0 +1,65 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.filter; + +import java.security.Principal; +import java.security.cert.X509Certificate; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.SecurityContext; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.Provider; + +@Provider +@Priority(Priorities.AUTHENTICATION) //Highest priority constant, this filter gets executed first +public class SecurityFilter implements ContainerRequestFilter { + + private UriInfo uriInfo; + + @Override + public void filter(ContainerRequestContext context) { + X509Certificate[] chain = (X509Certificate[]) context.getProperty("javax.servlet.request.X509Certificate"); + if (chain != null && chain.length > 0) { + uriInfo = context.getUriInfo(); + String subject = chain[0].getSubjectDN().getName(); + Authorizer securityContext = new Authorizer(subject); + context.setSecurityContext(securityContext); + } + } + + class Authorizer implements SecurityContext { + + private String user; + private Principal principal; + + Authorizer(final String user) { + this.user = user; + this.principal = () -> user; + } + + public Principal getUserPrincipal() { + return this.principal; + } + + public boolean isUserInRole(String role) { + return (role.equals(user)); + } + + public boolean isSecure() { + return uriInfo.getRequestUri().getScheme().equals("https"); + } + + public String getAuthenticationScheme() { + return SecurityContext.BASIC_AUTH; + } + } + + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/ArrowheadSystemKeyDeserializer.java b/core-common/src/main/java/eu/arrowhead/common/json/ArrowheadSystemKeyDeserializer.java new file mode 100644 index 00000000..f271fb85 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/ArrowheadSystemKeyDeserializer.java @@ -0,0 +1,20 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json; + +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; +import eu.arrowhead.common.database.ArrowheadSystem; + +public class ArrowheadSystemKeyDeserializer extends KeyDeserializer { + + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) { + return new ArrowheadSystem(key); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/JacksonJsonProviderAtRest.java b/core-common/src/main/java/eu/arrowhead/common/json/JacksonJsonProviderAtRest.java new file mode 100644 index 00000000..69145d9c --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/JacksonJsonProviderAtRest.java @@ -0,0 +1,52 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.ext.Provider; + +@Provider +@Produces(MediaType.APPLICATION_JSON) +public class JacksonJsonProviderAtRest extends JacksonJaxbJsonProvider { + + private static final ObjectMapper mapper = new ObjectMapper(); + + static { + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.registerModule(new JavaTimeModule()); + mapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(Include.ALWAYS, Include.NON_NULL)); + mapper.setSerializationInclusion(Include.NON_NULL); + mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + } + + public JacksonJsonProviderAtRest() { + super(); + setMapper(mapper); + } + + public static ObjectMapper getMapper() { + return mapper; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/constraint/LDTInFuture.java b/core-common/src/main/java/eu/arrowhead/common/json/constraint/LDTInFuture.java new file mode 100644 index 00000000..d36c93a3 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/constraint/LDTInFuture.java @@ -0,0 +1,43 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.constraint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.time.LocalDateTime; +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; + +//Constraint annotation: checks if a LocalDateTime value is in the future (isValid returns true if it's in the future) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = {LDTInFuture.Validator.class}) +public @interface LDTInFuture { + + String message() default "LocalDateTime must be in the future"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class Validator implements ConstraintValidator { + + @Override + public void initialize(final LDTInFuture LDTInFuture) { + } + + @Override + public boolean isValid(final LocalDateTime ldt, final ConstraintValidatorContext constraintValidatorContext) { + if (ldt == null) { + return true; + } + return ldt.isAfter(LocalDateTime.now()); + } + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/constraint/ZDTInFuture.java b/core-common/src/main/java/eu/arrowhead/common/json/constraint/ZDTInFuture.java new file mode 100644 index 00000000..e4349c14 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/constraint/ZDTInFuture.java @@ -0,0 +1,43 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.constraint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.time.ZonedDateTime; +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; + +//Constraint annotation: checks if a LocalDateTime value is in the future (isValid returns true if it's in the future) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = {ZDTInFuture.Validator.class}) +public @interface ZDTInFuture { + + String message() default "ZonedDateTime must be in the future"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class Validator implements ConstraintValidator { + + @Override + public void initialize(final ZDTInFuture ZDTInFuture) { + } + + @Override + public boolean isValid(final ZonedDateTime zdt, final ConstraintValidatorContext constraintValidatorContext) { + if (zdt == null) { + return true; + } + return zdt.isAfter(ZonedDateTime.now()); + } + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadServiceSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadServiceSupport.java new file mode 100644 index 00000000..ed016fb5 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadServiceSupport.java @@ -0,0 +1,78 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.database.ArrowheadService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +public class ArrowheadServiceSupport { + + private String serviceGroup; + private String serviceDefinition; + private List interfaces = new ArrayList<>(); + @XmlJavaTypeAdapter(StringMapAdapter.class) + private Map serviceMetadata = new HashMap<>(); + + public ArrowheadServiceSupport() { + } + + public ArrowheadServiceSupport(ArrowheadService service) { + if (service.getServiceDefinition().contains("_")) { + String[] parts = service.getServiceDefinition().split("_"); + this.serviceGroup = parts[0]; + this.serviceDefinition = parts[1]; + } else { + this.serviceDefinition = service.getServiceDefinition(); + } + this.interfaces = new ArrayList<>(service.getInterfaces()); + this.serviceMetadata = service.getServiceMetadata(); + } + + public ArrowheadServiceSupport(String serviceGroup, String serviceDefinition, List interfaces, Map serviceMetadata) { + this.serviceGroup = serviceGroup; + this.serviceDefinition = serviceDefinition; + this.interfaces = interfaces; + this.serviceMetadata = serviceMetadata; + } + + public String getServiceGroup() { + return serviceGroup; + } + + public void setServiceGroup(String serviceGroup) { + this.serviceGroup = serviceGroup; + } + + public String getServiceDefinition() { + return serviceDefinition; + } + + public void setServiceDefinition(String serviceDefinition) { + this.serviceDefinition = serviceDefinition; + } + + public List getInterfaces() { + return interfaces; + } + + public void setInterfaces(List interfaces) { + this.interfaces = interfaces; + } + + public Map getServiceMetadata() { + return serviceMetadata; + } + + public void setServiceMetadata(Map serviceMetadata) { + this.serviceMetadata = serviceMetadata; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadSystemSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadSystemSupport.java new file mode 100644 index 00000000..5b9c58da --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/ArrowheadSystemSupport.java @@ -0,0 +1,127 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import eu.arrowhead.common.database.ArrowheadSystem; + +public class ArrowheadSystemSupport { + + private String systemGroup; + private String systemName; + private String address; + private int port; + private String authenticationInfo; + + public ArrowheadSystemSupport() { + } + + public ArrowheadSystemSupport(ArrowheadSystem system) { + if (system.getSystemName().contains("_")) { + String[] parts = system.getSystemName().split("_"); + this.systemGroup = parts[0]; + this.systemName = parts[1]; + } else { + this.systemName = system.getSystemName(); + } + this.address = system.getAddress(); + this.port = system.getPort(); + this.authenticationInfo = system.getAuthenticationInfo(); + } + + public ArrowheadSystemSupport(String systemGroup, String systemName, String address, int port, String authenticationInfo) { + this.systemGroup = systemGroup; + this.systemName = systemName; + this.address = address; + this.port = port; + this.authenticationInfo = authenticationInfo; + } + + public String getSystemGroup() { + return systemGroup; + } + + public void setSystemGroup(String systemGroup) { + this.systemGroup = systemGroup; + } + + public String getSystemName() { + return systemName; + } + + public void setSystemName(String systemName) { + this.systemName = systemName; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } + + @JsonIgnore + public boolean isValid() { + return systemGroup != null && systemName != null && address != null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ArrowheadSystemSupport that = (ArrowheadSystemSupport) o; + + if (port != that.port) { + return false; + } + if (!systemGroup.equals(that.systemGroup)) { + return false; + } + if (!systemName.equals(that.systemName)) { + return false; + } + return address.equals(that.address); + } + + @Override + public int hashCode() { + int result = systemGroup.hashCode(); + result = 31 * result + systemName.hashCode(); + result = 31 * result + address.hashCode(); + result = 31 * result + port; + return result; + } + + @Override + public String toString() { + return "(" + systemGroup + ":" + systemName + ")"; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/BooleanMapAdapter.java b/core-common/src/main/java/eu/arrowhead/common/json/support/BooleanMapAdapter.java new file mode 100644 index 00000000..3df8bc3e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/BooleanMapAdapter.java @@ -0,0 +1,56 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.json.support.BooleanMapAdapter.MapLike; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.bind.annotation.adapters.XmlAdapter; + +public class BooleanMapAdapter extends XmlAdapter> { + + public static class Entry { + + public String key; + public Boolean value; + + public Entry() { + } + + public Entry(String key, Boolean value) { + this.key = key; + this.value = value; + } + } + + public static class MapLike { + + public List entry; + } + + @Override + public Map unmarshal(MapLike v) { + Map map = new HashMap<>(); + for (Entry entry : v.entry) { + map.put(entry.key, entry.value); + } + return map; + } + + @Override + public MapLike marshal(Map v) { + MapLike mapLike = new MapLike(); + mapLike.entry = new ArrayList<>(); + for (Map.Entry entry : v.entrySet()) { + mapLike.entry.add(new Entry(entry.getKey(), entry.getValue())); + } + return mapLike; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationFormSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationFormSupport.java new file mode 100644 index 00000000..c4a3ecf1 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationFormSupport.java @@ -0,0 +1,97 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.messages.OrchestrationForm; + +public class OrchestrationFormSupport { + + private ArrowheadServiceSupport service; + private ArrowheadSystemSupport provider; + private String serviceURI; + private String instruction; + private String authorizationToken; + private String signature; + + public OrchestrationFormSupport() { + } + + public OrchestrationFormSupport(ArrowheadServiceSupport service, ArrowheadSystemSupport provider, String serviceURI) { + this.service = service; + this.provider = provider; + this.serviceURI = serviceURI; + } + + public OrchestrationFormSupport(ArrowheadServiceSupport service, ArrowheadSystemSupport provider, String serviceURI, String instruction, + String authorizationToken, String signature) { + this.service = service; + this.provider = provider; + this.serviceURI = serviceURI; + this.instruction = instruction; + this.authorizationToken = authorizationToken; + this.signature = signature; + } + + public OrchestrationFormSupport(OrchestrationForm form) { + this.service = new ArrowheadServiceSupport(form.getService()); + this.provider = new ArrowheadSystemSupport(form.getProvider()); + this.serviceURI = form.getServiceURI(); + this.instruction = form.getInstruction(); + this.authorizationToken = form.getAuthorizationToken(); + this.signature = form.getSignature(); + } + + public ArrowheadServiceSupport getService() { + return service; + } + + public void setService(ArrowheadServiceSupport service) { + this.service = service; + } + + public ArrowheadSystemSupport getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystemSupport provider) { + this.provider = provider; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + public String getInstruction() { + return instruction; + } + + public void setInstruction(String instruction) { + this.instruction = instruction; + } + + public String getAuthorizationToken() { + return authorizationToken; + } + + public void setAuthorizationToken(String authorizationToken) { + this.authorizationToken = authorizationToken; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationResponseSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationResponseSupport.java new file mode 100644 index 00000000..11cc206e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/OrchestrationResponseSupport.java @@ -0,0 +1,40 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.messages.OrchestrationForm; +import eu.arrowhead.common.messages.OrchestrationResponse; +import java.util.ArrayList; +import java.util.List; + +public class OrchestrationResponseSupport { + + private List response = new ArrayList<>(); + + public OrchestrationResponseSupport() { + } + + public OrchestrationResponseSupport(OrchestrationResponse orchResponse) { + for (OrchestrationForm form : orchResponse.getResponse()) { + response.add(new OrchestrationFormSupport(form)); + } + } + + public OrchestrationResponseSupport(List response) { + this.response = response; + } + + public List getResponse() { + return response; + } + + public void setResponse(List response) { + this.response = response; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/PreferredProviderSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/PreferredProviderSupport.java new file mode 100644 index 00000000..f4215efa --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/PreferredProviderSupport.java @@ -0,0 +1,47 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.messages.PreferredProvider; + +public class PreferredProviderSupport { + + private ArrowheadSystemSupport providerSystem; + private ArrowheadCloud providerCloud; + + public PreferredProviderSupport() { + } + + public PreferredProviderSupport(PreferredProvider provider) { + this.providerSystem = new ArrowheadSystemSupport(provider.getProviderSystem()); + this.providerCloud = provider.getProviderCloud(); + } + + public PreferredProviderSupport(ArrowheadSystemSupport providerSystem, ArrowheadCloud providerCloud) { + this.providerSystem = providerSystem; + this.providerCloud = providerCloud; + } + + public ArrowheadSystemSupport getProviderSystem() { + return providerSystem; + } + + public void setProviderSystem(ArrowheadSystemSupport providerSystem) { + this.providerSystem = providerSystem; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRegistryEntrySupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRegistryEntrySupport.java new file mode 100644 index 00000000..ae61ac02 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRegistryEntrySupport.java @@ -0,0 +1,77 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import com.fasterxml.jackson.annotation.JsonProperty; +import eu.arrowhead.common.database.ServiceRegistryEntry; + +public class ServiceRegistryEntrySupport { + + private ArrowheadServiceSupport providedService; + private ArrowheadSystemSupport provider; + private String serviceURI; + + private int version = 1; + @JsonProperty("UDP") + private boolean UDP = false; + + public ServiceRegistryEntrySupport() { + } + + public ServiceRegistryEntrySupport(ServiceRegistryEntry entry) { + this.providedService = new ArrowheadServiceSupport(entry.getProvidedService()); + this.provider = new ArrowheadSystemSupport(entry.getProvider()); + this.serviceURI = entry.getServiceURI(); + } + + public ServiceRegistryEntrySupport(ArrowheadServiceSupport providedService, ArrowheadSystemSupport provider, String serviceURI) { + this.providedService = providedService; + this.provider = provider; + this.serviceURI = serviceURI; + } + + public ArrowheadServiceSupport getProvidedService() { + return providedService; + } + + public void setProvidedService(ArrowheadServiceSupport providedService) { + this.providedService = providedService; + } + + public ArrowheadSystemSupport getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystemSupport provider) { + this.provider = provider; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public boolean isUDP() { + return UDP; + } + + public void setUDP(boolean UDP) { + this.UDP = UDP; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRequestFormSupport.java b/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRequestFormSupport.java new file mode 100644 index 00000000..e2678d8f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/ServiceRequestFormSupport.java @@ -0,0 +1,109 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.messages.PreferredProvider; +import eu.arrowhead.common.messages.ServiceRequestForm; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +public class ServiceRequestFormSupport { + + private ArrowheadSystemSupport requesterSystem; + private ArrowheadCloud requesterCloud; + private ArrowheadServiceSupport requestedService; + @XmlJavaTypeAdapter(BooleanMapAdapter.class) + private Map orchestrationFlags = new HashMap<>(); + private List preferredProviders = new ArrayList<>(); + @XmlJavaTypeAdapter(StringMapAdapter.class) + private Map requestedQoS = new HashMap<>(); + @XmlJavaTypeAdapter(StringMapAdapter.class) + private Map commands = new HashMap<>(); + + public ServiceRequestFormSupport() { + } + + public ServiceRequestFormSupport(ServiceRequestForm srf) { + this.requesterSystem = new ArrowheadSystemSupport(srf.getRequesterSystem()); + this.requesterCloud = srf.getRequesterCloud(); + this.requestedService = new ArrowheadServiceSupport(srf.getRequestedService()); + this.orchestrationFlags = srf.getOrchestrationFlags(); + for (PreferredProvider provider : srf.getPreferredProviders()) { + preferredProviders.add(new PreferredProviderSupport(provider)); + } + } + + public ServiceRequestFormSupport(ArrowheadSystemSupport requesterSystem, ArrowheadCloud requesterCloud, ArrowheadServiceSupport requestedService, + Map orchestrationFlags, List preferredProviders) { + this.requesterSystem = requesterSystem; + this.requesterCloud = requesterCloud; + this.requestedService = requestedService; + this.orchestrationFlags = orchestrationFlags; + this.preferredProviders = preferredProviders; + } + + public ArrowheadSystemSupport getRequesterSystem() { + return requesterSystem; + } + + public void setRequesterSystem(ArrowheadSystemSupport requesterSystem) { + this.requesterSystem = requesterSystem; + } + + public ArrowheadCloud getRequesterCloud() { + return requesterCloud; + } + + public void setRequesterCloud(ArrowheadCloud requesterCloud) { + this.requesterCloud = requesterCloud; + } + + public ArrowheadServiceSupport getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadServiceSupport requestedService) { + this.requestedService = requestedService; + } + + public Map getOrchestrationFlags() { + return orchestrationFlags; + } + + public void setOrchestrationFlags(Map orchestrationFlags) { + this.orchestrationFlags = orchestrationFlags; + } + + public List getPreferredProviders() { + return preferredProviders; + } + + public void setPreferredProviders(List preferredProviders) { + this.preferredProviders = preferredProviders; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + + public Map getCommands() { + return commands; + } + + public void setCommands(Map commands) { + this.commands = commands; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/json/support/StringMapAdapter.java b/core-common/src/main/java/eu/arrowhead/common/json/support/StringMapAdapter.java new file mode 100644 index 00000000..20f706fb --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/json/support/StringMapAdapter.java @@ -0,0 +1,56 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.json.support; + +import eu.arrowhead.common.json.support.StringMapAdapter.MapLike; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.bind.annotation.adapters.XmlAdapter; + +public class StringMapAdapter extends XmlAdapter> { + + public static class Entry { + + public String key; + public String value; + + public Entry() { + } + + public Entry(String key, String value) { + this.key = key; + this.value = value; + } + } + + public static class MapLike { + + public List entry; + } + + @Override + public Map unmarshal(MapLike v) { + Map map = new HashMap<>(); + for (Entry entry : v.entry) { + map.put(entry.key, entry.value); + } + return map; + } + + @Override + public MapLike marshal(Map v) { + MapLike mapLike = new MapLike(); + mapLike.entry = new ArrayList<>(); + for (Map.Entry entry : v.entrySet()) { + mapLike.entry.add(new Entry(entry.getKey(), entry.getValue())); + } + return mapLike; + } +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ArrowheadToken.java b/core-common/src/main/java/eu/arrowhead/common/messages/ArrowheadToken.java new file mode 100644 index 00000000..13960a9f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ArrowheadToken.java @@ -0,0 +1,39 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class ArrowheadToken { + + private String token; + private String signature; + + public ArrowheadToken() { + } + + public ArrowheadToken(String token, String signature) { + this.token = token; + this.signature = signature; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerRequest.java b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerRequest.java new file mode 100644 index 00000000..c21f131c --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerRequest.java @@ -0,0 +1,145 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; + +public class ConnectToConsumerRequest { + + private String brokerHost; + private int brokerPort; + private String queueName; + private String controlQueueName; + private ArrowheadSystem consumer; + private ArrowheadSystem provider; + private ArrowheadCloud consumerCloud; + private ArrowheadCloud providerCloud; + private ArrowheadService service; + private Boolean isSecure; + private int timeout; + private String providerGWPublicKey; + + public ConnectToConsumerRequest() { + } + + public ConnectToConsumerRequest(String brokerHost, int brokerPort, String queueName, String controlQueueName, ArrowheadSystem consumer, + ArrowheadSystem provider, ArrowheadCloud consumerCloud, ArrowheadCloud providerCloud, ArrowheadService service, + Boolean isSecure, int timeout, String providerGWPublicKey) { + this.brokerHost = brokerHost; + this.brokerPort = brokerPort; + this.queueName = queueName; + this.controlQueueName = controlQueueName; + this.consumer = consumer; + this.provider = provider; + this.consumerCloud = consumerCloud; + this.providerCloud = providerCloud; + this.service = service; + this.isSecure = isSecure; + this.timeout = timeout; + this.providerGWPublicKey = providerGWPublicKey; + } + + public String getBrokerHost() { + return brokerHost; + } + + public void setBrokerHost(String brokerHost) { + this.brokerHost = brokerHost; + } + + public int getBrokerPort() { + return brokerPort; + } + + public void setBrokerPort(int brokerPort) { + this.brokerPort = brokerPort; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + public String getControlQueueName() { + return controlQueueName; + } + + public void setControlQueueName(String controlQueueName) { + this.controlQueueName = controlQueueName; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public ArrowheadCloud getConsumerCloud() { + return consumerCloud; + } + + public void setConsumerCloud(ArrowheadCloud consumerCloud) { + this.consumerCloud = consumerCloud; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public Boolean getIsSecure() { + return isSecure; + } + + public void setIsSecure(Boolean isSecure) { + this.isSecure = isSecure; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getProviderGWPublicKey() { + return providerGWPublicKey; + } + + public void setProviderGWPublicKey(String providerGWPublicKey) { + this.providerGWPublicKey = providerGWPublicKey; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerResponse.java new file mode 100644 index 00000000..436cb111 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToConsumerResponse.java @@ -0,0 +1,29 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class ConnectToConsumerResponse { + + private int serverSocketPort; + + public ConnectToConsumerResponse() { + } + + public ConnectToConsumerResponse(int serverSocketPort) { + this.serverSocketPort = serverSocketPort; + } + + public int getServerSocketPort() { + return serverSocketPort; + } + + public void setServerSocketPort(int serverSocketPort) { + this.serverSocketPort = serverSocketPort; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderRequest.java b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderRequest.java new file mode 100644 index 00000000..ce8d35e7 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderRequest.java @@ -0,0 +1,124 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; + +public class ConnectToProviderRequest { + + private String brokerHost; + private int brokerPort; + private ArrowheadSystem consumer; + private ArrowheadSystem provider; + private ArrowheadCloud consumerCloud; + private ArrowheadCloud providerCloud; + private ArrowheadService service; + private boolean isSecure; + private int timeout; + private String consumerGWPublicKey; + + public ConnectToProviderRequest() { + } + + public ConnectToProviderRequest(String brokerHost, int brokerPort, ArrowheadSystem consumer, ArrowheadSystem provider, ArrowheadCloud consumerCloud, + ArrowheadCloud providerCloud, ArrowheadService service, boolean isSecure, int timeout, String consumerGWPublicKey) { + this.brokerHost = brokerHost; + this.brokerPort = brokerPort; + this.consumer = consumer; + this.provider = provider; + this.consumerCloud = consumerCloud; + this.providerCloud = providerCloud; + this.service = service; + this.isSecure = isSecure; + this.timeout = timeout; + this.consumerGWPublicKey = consumerGWPublicKey; + } + + public String getBrokerHost() { + return brokerHost; + } + + public void setBrokerHost(String brokerHost) { + this.brokerHost = brokerHost; + } + + public int getBrokerPort() { + return brokerPort; + } + + public void setBrokerPort(int brokerPort) { + this.brokerPort = brokerPort; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public ArrowheadCloud getConsumerCloud() { + return consumerCloud; + } + + public void setConsumerCloud(ArrowheadCloud consumerCloud) { + this.consumerCloud = consumerCloud; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public boolean getIsSecure() { + return isSecure; + } + + public void setIsSecure(Boolean isSecure) { + this.isSecure = isSecure; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getConsumerGWPublicKey() { + return consumerGWPublicKey; + } + + public void setConsumerGWPublicKey(String consumerGWPublicKey) { + this.consumerGWPublicKey = consumerGWPublicKey; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderResponse.java new file mode 100644 index 00000000..980365d6 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ConnectToProviderResponse.java @@ -0,0 +1,39 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class ConnectToProviderResponse { + + private String queueName; + private String controlQueueName; + + public ConnectToProviderResponse() { + } + + public ConnectToProviderResponse(String queueName, String controlQueueName) { + this.queueName = queueName; + this.controlQueueName = controlQueueName; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + public String getControlQueueName() { + return controlQueueName; + } + + public void setControlQueueName(String controlQueueName) { + this.controlQueueName = controlQueueName; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/Event.java b/core-common/src/main/java/eu/arrowhead/common/messages/Event.java new file mode 100644 index 00000000..9ba5693a --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/Event.java @@ -0,0 +1,67 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; +import javax.validation.constraints.Size; +import org.hibernate.validator.constraints.NotBlank; + +public class Event { + + @NotBlank + @Size(max = 255, message = "Event type must be 255 character at max") + private String type; + private String payload; + private ZonedDateTime timestamp; + private Map eventMetadata = new HashMap<>(); + + public Event() { + } + + public Event(String type, String payload, ZonedDateTime timestamp, Map eventMetadata) { + this.type = type; + this.payload = payload; + this.timestamp = timestamp; + this.eventMetadata = eventMetadata; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public ZonedDateTime getTimestamp() { + return timestamp; + } + + public void setTimestamp(ZonedDateTime timestamp) { + this.timestamp = timestamp; + } + + public Map getEventMetadata() { + return eventMetadata; + } + + public void setEventMetadata(Map eventMetadata) { + this.eventMetadata = eventMetadata; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/GSDAnswer.java b/core-common/src/main/java/eu/arrowhead/common/messages/GSDAnswer.java new file mode 100644 index 00000000..2ac40d16 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/GSDAnswer.java @@ -0,0 +1,45 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import javax.validation.Valid; + +public class GSDAnswer { + + @Valid + private ArrowheadService requestedService; + @Valid + private ArrowheadCloud providerCloud; + + public GSDAnswer() { + } + + public GSDAnswer(ArrowheadService requestedService, ArrowheadCloud providerCloud) { + this.requestedService = requestedService; + this.providerCloud = providerCloud; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/GSDPoll.java b/core-common/src/main/java/eu/arrowhead/common/messages/GSDPoll.java new file mode 100644 index 00000000..65687250 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/GSDPoll.java @@ -0,0 +1,60 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import java.util.HashMap; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class GSDPoll { + + @Valid + @NotNull + private ArrowheadService requestedService; + @Valid + @NotNull + private ArrowheadCloud requesterCloud; + private Map registryFlags = new HashMap<>(); + + public GSDPoll() { + } + + public GSDPoll(ArrowheadService requestedService, ArrowheadCloud requesterCloud, Map registryFlags) { + this.requestedService = requestedService; + this.requesterCloud = requesterCloud; + this.registryFlags = registryFlags; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public ArrowheadCloud getRequesterCloud() { + return requesterCloud; + } + + public void setRequesterCloud(ArrowheadCloud requesterCloud) { + this.requesterCloud = requesterCloud; + } + + public Map getRegistryFlags() { + return registryFlags; + } + + public void setRegistryFlags(Map registryFlags) { + this.registryFlags = registryFlags; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/GSDRequestForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/GSDRequestForm.java new file mode 100644 index 00000000..5e2590cc --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/GSDRequestForm.java @@ -0,0 +1,60 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class GSDRequestForm { + + @Valid + @NotNull + private ArrowheadService requestedService; + private List searchPerimeter = new ArrayList<>(); + private Map registryFlags = new HashMap<>(); + + public GSDRequestForm() { + } + + public GSDRequestForm(ArrowheadService requestedService, List searchPerimeter, Map registryFlags) { + this.requestedService = requestedService; + this.searchPerimeter = searchPerimeter; + this.registryFlags = registryFlags; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public List getSearchPerimeter() { + return searchPerimeter; + } + + public void setSearchPerimeter(List searchPerimeter) { + this.searchPerimeter = searchPerimeter; + } + + public Map getRegistryFlags() { + return registryFlags; + } + + public void setRegistryFlags(Map registryFlags) { + this.registryFlags = registryFlags; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/GSDResult.java b/core-common/src/main/java/eu/arrowhead/common/messages/GSDResult.java new file mode 100644 index 00000000..ce9a3447 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/GSDResult.java @@ -0,0 +1,36 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import java.util.ArrayList; +import java.util.List; + +public class GSDResult { + + private List response = new ArrayList<>(); + + public GSDResult() { + } + + public GSDResult(List response) { + this.response = response; + } + + public List getResponse() { + return response; + } + + public void setResponse(List response) { + this.response = response; + } + + public boolean isValid() { + return response != null && !response.isEmpty(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/GatewayConnectionInfo.java b/core-common/src/main/java/eu/arrowhead/common/messages/GatewayConnectionInfo.java new file mode 100644 index 00000000..ba478d10 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/GatewayConnectionInfo.java @@ -0,0 +1,69 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class GatewayConnectionInfo { + + private String brokerName; + private int brokerPort; + private String queueName; + private String controlQueueName; + private String gatewayPublicKey; + + public GatewayConnectionInfo() { + } + + public GatewayConnectionInfo(String brokerName, int brokerPort, String queueName, String controlQueueName, String gatewayPublicKey) { + this.brokerName = brokerName; + this.brokerPort = brokerPort; + this.queueName = queueName; + this.controlQueueName = controlQueueName; + this.gatewayPublicKey = gatewayPublicKey; + } + + public String getBrokerName() { + return brokerName; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public int getBrokerPort() { + return brokerPort; + } + + public void setBrokerPort(int brokerPort) { + this.brokerPort = brokerPort; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + public void setControlQueueName(String controlQueueName) { + this.controlQueueName = controlQueueName; + } + + public String getControlQueueName() { + return controlQueueName; + } + + public String getGatewayPublicKey() { + return gatewayPublicKey; + } + + public void setGatewayPublicKey(String gatewayPublicKey) { + this.gatewayPublicKey = gatewayPublicKey; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ICNEnd.java b/core-common/src/main/java/eu/arrowhead/common/messages/ICNEnd.java new file mode 100644 index 00000000..252a497e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ICNEnd.java @@ -0,0 +1,44 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class ICNEnd { + + @Valid + @NotNull + private OrchestrationForm orchestrationForm; + private GatewayConnectionInfo gatewayConnInfo; + + public ICNEnd() { + } + + public ICNEnd(OrchestrationForm orchestrationForm, GatewayConnectionInfo gatewayConnInfo) { + this.orchestrationForm = orchestrationForm; + this.gatewayConnInfo = gatewayConnInfo; + } + + public OrchestrationForm getOrchestrationForm() { + return orchestrationForm; + } + + public void setOrchestrationForm(OrchestrationForm orchestrationForm) { + this.orchestrationForm = orchestrationForm; + } + + public GatewayConnectionInfo getGatewayConnInfo() { + return gatewayConnInfo; + } + + public void setGatewayConnInfo(GatewayConnectionInfo gatewayConnInfo) { + this.gatewayConnInfo = gatewayConnInfo; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ICNProposal.java b/core-common/src/main/java/eu/arrowhead/common/messages/ICNProposal.java new file mode 100644 index 00000000..e0d2b3a4 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ICNProposal.java @@ -0,0 +1,117 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.Broker; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class ICNProposal { + + @Valid + @NotNull + private ArrowheadService requestedService; + @Valid + @NotNull + private ArrowheadCloud requesterCloud; + @Valid + @NotNull + private ArrowheadSystem requesterSystem; + private List preferredSystems = new ArrayList<>(); + private Map negotiationFlags = new HashMap<>(); + private List preferredBrokers; + private int timeout; + private String gatewayPublicKey; + + public ICNProposal() { + } + + public ICNProposal(ArrowheadService requestedService, ArrowheadCloud requesterCloud, ArrowheadSystem requesterSystem, + List preferredSystems, Map negotiationFlags, List preferredBrokers, int timeout, + String gatewayPublicKey) { + this.requestedService = requestedService; + this.requesterCloud = requesterCloud; + this.requesterSystem = requesterSystem; + this.preferredSystems = preferredSystems; + this.negotiationFlags = negotiationFlags; + this.preferredBrokers = preferredBrokers; + this.timeout = timeout; + this.gatewayPublicKey = gatewayPublicKey; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public ArrowheadCloud getRequesterCloud() { + return requesterCloud; + } + + public void setRequesterCloud(ArrowheadCloud requesterCloud) { + this.requesterCloud = requesterCloud; + } + + public ArrowheadSystem getRequesterSystem() { + return requesterSystem; + } + + public void setRequesterSystem(ArrowheadSystem requesterSystem) { + this.requesterSystem = requesterSystem; + } + + public List getPreferredSystems() { + return preferredSystems; + } + + public void setPreferredSystems(List preferredSystems) { + this.preferredSystems = preferredSystems; + } + + public Map getNegotiationFlags() { + return negotiationFlags; + } + + public void setNegotiationFlags(Map negotiationFlags) { + this.negotiationFlags = negotiationFlags; + } + + public List getPreferredBrokers() { + return preferredBrokers; + } + + public void setPreferredBrokers(List preferredBrokers) { + this.preferredBrokers = preferredBrokers; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getGatewayPublicKey() { + return gatewayPublicKey; + } + + public void setGatewayPublicKey(String gatewayPublicKey) { + this.gatewayPublicKey = gatewayPublicKey; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ICNRequestForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/ICNRequestForm.java new file mode 100644 index 00000000..b10c31ed --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ICNRequestForm.java @@ -0,0 +1,85 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class ICNRequestForm { + + @Valid + @NotNull + private ArrowheadService requestedService; + @Valid + @NotNull + private ArrowheadCloud targetCloud; + @Valid + @NotNull + private ArrowheadSystem requesterSystem; + private List preferredSystems = new ArrayList<>(); + private Map negotiationFlags = new HashMap<>(); + + public ICNRequestForm() { + } + + public ICNRequestForm(ArrowheadService requestedService, ArrowheadCloud targetCloud, ArrowheadSystem requesterSystem, + List preferredSystems, Map negotiationFlags) { + this.requestedService = requestedService; + this.targetCloud = targetCloud; + this.requesterSystem = requesterSystem; + this.preferredSystems = preferredSystems; + this.negotiationFlags = negotiationFlags; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public ArrowheadCloud getTargetCloud() { + return targetCloud; + } + + public void setTargetCloud(ArrowheadCloud targetCloud) { + this.targetCloud = targetCloud; + } + + public ArrowheadSystem getRequesterSystem() { + return requesterSystem; + } + + public void setRequesterSystem(ArrowheadSystem requesterSystem) { + this.requesterSystem = requesterSystem; + } + + public List getPreferredSystems() { + return preferredSystems; + } + + public void setPreferredSystems(List preferredSystems) { + this.preferredSystems = preferredSystems; + } + + public Map getNegotiationFlags() { + return negotiationFlags; + } + + public void setNegotiationFlags(Map negotiationFlags) { + this.negotiationFlags = negotiationFlags; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ICNResult.java b/core-common/src/main/java/eu/arrowhead/common/messages/ICNResult.java new file mode 100644 index 00000000..230574b1 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ICNResult.java @@ -0,0 +1,38 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class ICNResult { + + @Valid + @NotNull + private OrchestrationResponse orchResponse; + + public ICNResult() { + } + + public ICNResult(OrchestrationResponse orchResponse) { + this.orchResponse = orchResponse; + } + + public OrchestrationResponse getOrchResponse() { + return orchResponse; + } + + public void setOrchResponse(OrchestrationResponse orchResponse) { + this.orchResponse = orchResponse; + } + + public boolean isValid() { + return orchResponse != null && orchResponse.getResponse() != null && !orchResponse.getResponse().isEmpty(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthEntry.java b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthEntry.java new file mode 100644 index 00000000..88305553 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthEntry.java @@ -0,0 +1,51 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.validator.constraints.NotEmpty; + +public class InterCloudAuthEntry { + + @Valid + @NotNull + private ArrowheadCloud cloud; + @Valid + @NotEmpty + private List serviceList = new ArrayList<>(); + + public InterCloudAuthEntry() { + } + + public InterCloudAuthEntry(ArrowheadCloud cloud, List serviceList) { + this.cloud = cloud; + this.serviceList = serviceList; + } + + public ArrowheadCloud getCloud() { + return cloud; + } + + public void setCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + public List getServiceList() { + return serviceList; + } + + public void setServiceList(List serviceList) { + this.serviceList = serviceList; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthRequest.java b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthRequest.java new file mode 100644 index 00000000..0514d1c4 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthRequest.java @@ -0,0 +1,48 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class InterCloudAuthRequest { + + @Valid + @NotNull + private ArrowheadCloud cloud; + @Valid + @NotNull + private ArrowheadService service; + + public InterCloudAuthRequest() { + } + + public InterCloudAuthRequest(ArrowheadCloud cloud, ArrowheadService service) { + this.cloud = cloud; + this.service = service; + } + + public ArrowheadCloud getCloud() { + return cloud; + } + + public void setCloud(ArrowheadCloud cloud) { + this.cloud = cloud; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthResponse.java new file mode 100644 index 00000000..1b05b276 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/InterCloudAuthResponse.java @@ -0,0 +1,30 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class InterCloudAuthResponse { + + private boolean authorized; + + public InterCloudAuthResponse() { + } + + public InterCloudAuthResponse(boolean isAuthorized) { + this.authorized = isAuthorized; + } + + + public boolean isAuthorized() { + return authorized; + } + + public void setAuthorized(boolean isAuthorized) { + this.authorized = isAuthorized; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthEntry.java b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthEntry.java new file mode 100644 index 00000000..869bc215 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthEntry.java @@ -0,0 +1,63 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.validator.constraints.NotEmpty; + +public class IntraCloudAuthEntry { + + @Valid + @NotNull + private ArrowheadSystem consumer; + @Valid + @NotEmpty + private List providerList = new ArrayList<>(); + @Valid + @NotEmpty + private List serviceList = new ArrayList<>(); + + public IntraCloudAuthEntry() { + } + + public IntraCloudAuthEntry(ArrowheadSystem consumer, List providerList, List serviceList) { + this.consumer = consumer; + this.providerList = providerList; + this.serviceList = serviceList; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public List getProviderList() { + return providerList; + } + + public void setProviderList(List providerList) { + this.providerList = providerList; + } + + public List getServiceList() { + return serviceList; + } + + public void setServiceList(List serviceList) { + this.serviceList = serviceList; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthRequest.java b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthRequest.java new file mode 100644 index 00000000..18fe363b --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthRequest.java @@ -0,0 +1,63 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashSet; +import java.util.Set; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.validator.constraints.NotEmpty; + +public class IntraCloudAuthRequest { + + @Valid + @NotNull + private ArrowheadSystem consumer; + @Valid + @NotEmpty + private Set providers = new HashSet<>(); + @Valid + @NotNull + private ArrowheadService service; + + public IntraCloudAuthRequest() { + } + + public IntraCloudAuthRequest(ArrowheadSystem consumer, Set providers, ArrowheadService service) { + this.consumer = consumer; + this.providers = providers; + this.service = service; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public Set getProviders() { + return providers; + } + + public void setProviders(Set providers) { + this.providers = providers; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthResponse.java new file mode 100644 index 00000000..032561ee --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/IntraCloudAuthResponse.java @@ -0,0 +1,41 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.json.ArrowheadSystemKeyDeserializer; +import java.util.HashMap; +import javax.validation.Valid; + +public class IntraCloudAuthResponse { + + @JsonDeserialize(keyUsing = ArrowheadSystemKeyDeserializer.class) + @Valid + private HashMap authorizationState = new HashMap<>(); + + public IntraCloudAuthResponse() { + } + + public IntraCloudAuthResponse(HashMap authorizationState) { + this.authorizationState = authorizationState; + } + + public HashMap getAuthorizationMap() { + return authorizationState; + } + + public void setAuthorizationMap(HashMap authorizationState) { + this.authorizationState = authorizationState; + } + + public boolean isValid() { + return !authorizationState.isEmpty(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/MonitorRule.java b/core-common/src/main/java/eu/arrowhead/common/messages/MonitorRule.java new file mode 100644 index 00000000..bc62eb6c --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/MonitorRule.java @@ -0,0 +1,131 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashMap; +import java.util.Map; + +/** + * Message used to create a new monitor rule. + * + * @author Renato Ayres + */ +public class MonitorRule { + + private String protocol; + private ArrowheadSystem provider; + private ArrowheadSystem consumer; + private Map parameters = new HashMap<>(); + private boolean softRealTime; + + /** + * Creates a new instance with no parameters initialized. + */ + public MonitorRule() { + } + + /** + * Creates a new instance with the given monitor protocol, service provider, service consumer, monitor parameters and a soft real time clause. + * + * @param protocol the monitor protocol + * @param provider the service provider + * @param consumer the service consumer + * @param parameters the monitor parameters. It works by getting the value of the parameter (key) e.g. key=bandwidth, value=100 + * @param softRealTime the soft real time clause + */ + public MonitorRule(String protocol, ArrowheadSystem provider, ArrowheadSystem consumer, Map parameters, boolean softRealTime) { + this.protocol = protocol; + this.provider = provider; + this.consumer = consumer; + this.parameters = parameters; + this.softRealTime = softRealTime; + } + + /** + * Gets the monitor protocol + * + * @return the monitor protocol + */ + public String getProtocol() { + return protocol; + } + + /** + * Sets the monitor protocol + * + * @param protocol the monitor protocol + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * Gets the service provider + * + * @return the service provider + */ + public ArrowheadSystem getProvider() { + return provider; + } + + /** + * Sets the service provider + * + * @param provider the service provider + */ + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + /** + * Gets the service consumer + * + * @return the service consumer + */ + public ArrowheadSystem getConsumer() { + return consumer; + } + + /** + * Sets the service consumer + * + * @param consumer the service consumer + */ + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + /** + * Gets the monitor parameters + * + * @return the monitor parameters + */ + public Map getParameters() { + return parameters; + } + + /** + * Sets the monitor of parameters + * + * @param parameters the monitor parameters + */ + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + + public boolean isSoftRealTime() { + return softRealTime; + } + + public void setSoftRealTime(boolean softRealTime) { + this.softRealTime = softRealTime; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationForm.java new file mode 100644 index 00000000..9c0ec964 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationForm.java @@ -0,0 +1,107 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class OrchestrationForm { + + @Valid + @NotNull + private ArrowheadService service; + @Valid + @NotNull + private ArrowheadSystem provider; + private String serviceURI; + private String instruction; + private String authorizationToken; + private String signature; + private List warnings = new ArrayList<>(); + + public OrchestrationForm() { + } + + public OrchestrationForm(ArrowheadService service, ArrowheadSystem provider, String serviceURI) { + this.service = service; + this.provider = provider; + this.serviceURI = serviceURI; + } + + public OrchestrationForm(ArrowheadService service, ArrowheadSystem provider, String serviceURI, String instruction, String authorizationToken, + String signature, List warnings) { + this.service = service; + this.provider = provider; + this.serviceURI = serviceURI; + this.instruction = instruction; + this.authorizationToken = authorizationToken; + this.signature = signature; + this.warnings = warnings; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public String getServiceURI() { + return serviceURI; + } + + public void setServiceURI(String serviceURI) { + this.serviceURI = serviceURI; + } + + public String getInstruction() { + return instruction; + } + + public void setInstruction(String instruction) { + this.instruction = instruction; + } + + public String getAuthorizationToken() { + return authorizationToken; + } + + public void setAuthorizationToken(String authorizationToken) { + this.authorizationToken = authorizationToken; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public List getWarnings() { + return warnings; + } + + public void setWarnings(List warnings) { + this.warnings = warnings; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationResponse.java new file mode 100644 index 00000000..9f05b4c3 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationResponse.java @@ -0,0 +1,34 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; + +public class OrchestrationResponse { + + @Valid + private List response = new ArrayList<>(); + + public OrchestrationResponse() { + } + + public OrchestrationResponse(List response) { + this.response = response; + } + + public List getResponse() { + return response; + } + + public void setResponse(List response) { + this.response = response; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStorePriorities.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStorePriorities.java new file mode 100644 index 00000000..4708c324 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStorePriorities.java @@ -0,0 +1,43 @@ +package eu.arrowhead.common.messages; + +import com.fasterxml.jackson.annotation.JsonSetter; +import eu.arrowhead.common.exception.BadPayloadException; +import java.util.HashMap; +import java.util.Map; +import org.hibernate.validator.constraints.NotEmpty; + +public class OrchestrationStorePriorities { + + /* + Key: Orchestration Store entry ID + Value: the new priority the entry should have + */ + @NotEmpty + private Map priorities = new HashMap<>(); + + public OrchestrationStorePriorities() { + } + + public OrchestrationStorePriorities(Map priorities) { + this.priorities = priorities; + } + + public Map getPriorities() { + return priorities; + } + + @JsonSetter + public void setPriorities(Map priorities) { + for (Map.Entry entry : priorities.entrySet()) { + Long key = entry.getKey(); + if (key == null || key < 1) { + throw new BadPayloadException("Store entry ID must be at least 1"); + } + Integer value = entry.getValue(); + if (value == null || value < 1) { + throw new BadPayloadException("Store entry priority must be at least 1"); + } + } + this.priorities = priorities; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQuery.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQuery.java new file mode 100644 index 00000000..98717cdb --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQuery.java @@ -0,0 +1,49 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class OrchestrationStoreQuery { + + @Valid + @NotNull + private ArrowheadService requestedService; + @Valid + @NotNull + private ArrowheadSystem requesterSystem; + + public OrchestrationStoreQuery() { + } + + public OrchestrationStoreQuery(ArrowheadService requestedService, ArrowheadSystem requesterSystem) { + this.requestedService = requestedService; + this.requesterSystem = requesterSystem; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public ArrowheadSystem getRequesterSystem() { + return requesterSystem; + } + + public void setRequesterSystem(ArrowheadSystem requesterSystem) { + this.requesterSystem = requesterSystem; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQueryResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQueryResponse.java new file mode 100644 index 00000000..82debcfb --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestrationStoreQueryResponse.java @@ -0,0 +1,35 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.OrchestrationStore; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; + +public class OrchestrationStoreQueryResponse { + + @Valid + private List entryList = new ArrayList<>(); + + public OrchestrationStoreQueryResponse() { + } + + public OrchestrationStoreQueryResponse(List entryList) { + this.entryList = entryList; + } + + public List getEntryList() { + return entryList; + } + + public void setEntryList(List entryList) { + this.entryList = entryList; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/OrchestratorWarnings.java b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestratorWarnings.java new file mode 100644 index 00000000..3d82558f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/OrchestratorWarnings.java @@ -0,0 +1,12 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public enum OrchestratorWarnings { + FROM_OTHER_CLOUD, TTL_EXPIRING, TTL_EXPIRED, TTL_UNKNOWN +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/PreferredProvider.java b/core-common/src/main/java/eu/arrowhead/common/messages/PreferredProvider.java new file mode 100644 index 00000000..983026af --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/PreferredProvider.java @@ -0,0 +1,68 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.json.support.PreferredProviderSupport; +import javax.validation.Valid; + +public class PreferredProvider { + + @Valid + private ArrowheadSystem providerSystem; + @Valid + private ArrowheadCloud providerCloud; + + public PreferredProvider() { + } + + public PreferredProvider(ArrowheadSystem providerSystem, ArrowheadCloud providerCloud) { + this.providerSystem = providerSystem; + this.providerCloud = providerCloud; + } + + public PreferredProvider(PreferredProviderSupport supportProvider) { + this.providerSystem = new ArrowheadSystem(supportProvider.getProviderSystem()); + this.providerCloud = supportProvider.getProviderCloud(); + } + + public ArrowheadSystem getProviderSystem() { + return providerSystem; + } + + public void setProviderSystem(ArrowheadSystem providerSystem) { + this.providerSystem = providerSystem; + } + + public ArrowheadCloud getProviderCloud() { + return providerCloud; + } + + public void setProviderCloud(ArrowheadCloud providerCloud) { + this.providerCloud = providerCloud; + } + + public boolean isLocal() { + return providerSystem != null && providerCloud == null; + } + + public boolean isGlobal() { + return providerCloud != null; + } + + public boolean isValid() { + return isLocal() || isGlobal(); + } + + @Override + public String toString() { + return "PreferredProvider{" + "providerSystem=" + providerSystem + ", providerCloud=" + providerCloud + '}'; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/PublishEvent.java b/core-common/src/main/java/eu/arrowhead/common/messages/PublishEvent.java new file mode 100644 index 00000000..e622b821 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/PublishEvent.java @@ -0,0 +1,57 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadSystem; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class PublishEvent { + + @Valid + @NotNull + private ArrowheadSystem source; + @Valid + @NotNull + private Event event; + private String deliveryCompleteUri; + + public PublishEvent() { + } + + public PublishEvent(ArrowheadSystem source, Event event, String deliveryCompleteUri) { + this.source = source; + this.event = event; + this.deliveryCompleteUri = deliveryCompleteUri; + } + + public ArrowheadSystem getSource() { + return source; + } + + public void setSource(ArrowheadSystem source) { + this.source = source; + } + + public Event getEvent() { + return event; + } + + public void setEvent(Event event) { + this.event = event; + } + + public String getDeliveryCompleteUri() { + return deliveryCompleteUri; + } + + public void setDeliveryCompleteUri(String deliveryCompleteUri) { + this.deliveryCompleteUri = deliveryCompleteUri; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationCommand.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationCommand.java new file mode 100644 index 00000000..3c74e11e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationCommand.java @@ -0,0 +1,75 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashMap; +import java.util.Map; + +public class QoSReservationCommand { + + private ArrowheadService service; + private ArrowheadSystem producer; + private ArrowheadSystem consumer; + private Map commands = new HashMap<>(); + private Map requestedQoS = new HashMap<>(); + + public QoSReservationCommand() { + } + + public QoSReservationCommand(ArrowheadService service, ArrowheadSystem producer, ArrowheadSystem consumer, Map commands, + Map requestedQoS) { + this.service = service; + this.producer = producer; + this.consumer = consumer; + this.commands = commands; + this.requestedQoS = requestedQoS; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public ArrowheadSystem getProducer() { + return producer; + } + + public void setProducer(ArrowheadSystem producer) { + this.producer = producer; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public Map getCommands() { + return commands; + } + + public void setCommands(Map commands) { + this.commands = commands; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationForm.java new file mode 100644 index 00000000..404fea43 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationForm.java @@ -0,0 +1,64 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashMap; +import java.util.Map; + +public class QoSReservationForm { + + private ArrowheadService service; + private ArrowheadSystem provider; + private ArrowheadSystem consumer; + private Map requestedQoS = new HashMap<>(); + + public QoSReservationForm() { + } + + public QoSReservationForm(ArrowheadService service, ArrowheadSystem provider, ArrowheadSystem consumer, Map requestedQoS) { + this.service = service; + this.provider = provider; + this.consumer = consumer; + this.requestedQoS = requestedQoS; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationResponse.java new file mode 100644 index 00000000..e77cd306 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReservationResponse.java @@ -0,0 +1,44 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class QoSReservationResponse { + + private boolean successfulReservation; + private QoSReservationCommand command; + + public QoSReservationResponse() { + } + + public QoSReservationResponse(boolean successfulReservation, QoSReservationCommand command) { + this.successfulReservation = successfulReservation; + this.command = command; + } + + public QoSReservationResponse(boolean successfulReservation) { + this.successfulReservation = successfulReservation; + } + + public QoSReservationCommand getCommand() { + return command; + } + + public void setCommand(QoSReservationCommand command) { + this.command = command; + } + + + public boolean isSuccessfulReservation() { + return successfulReservation; + } + + public void setSuccessfulReservation(boolean successfulReservation) { + this.successfulReservation = successfulReservation; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSReserve.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReserve.java new file mode 100644 index 00000000..0ce99d72 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSReserve.java @@ -0,0 +1,75 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.HashMap; +import java.util.Map; + +public class QoSReserve { + + private ArrowheadSystem provider; + private ArrowheadSystem consumer; + private ArrowheadService service; + private Map requestedQoS = new HashMap<>(); + private Map commands = new HashMap<>(); + + public QoSReserve() { + } + + public QoSReserve(ArrowheadSystem provider, ArrowheadSystem consumer, ArrowheadService service, Map requestedQoS, + Map commands) { + this.provider = provider; + this.consumer = consumer; + this.service = service; + this.requestedQoS = requestedQoS; + this.commands = commands; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + + public Map getCommands() { + return commands; + } + + public void setCommands(Map commands) { + this.commands = commands; + } + + public ArrowheadSystem getProvider() { + return provider; + } + + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerificationResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerificationResponse.java new file mode 100644 index 00000000..069f17e5 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerificationResponse.java @@ -0,0 +1,52 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.messages.QoSVerifierResponse.RejectMotivationTypes; +import java.util.HashMap; +import java.util.Map; + +public class QoSVerificationResponse { + + private Map response = new HashMap<>(); + private Map rejectMotivation = new HashMap<>(); + + public QoSVerificationResponse() { + } + + public QoSVerificationResponse(Map response, Map rejectMotivation) { + this.response = response; + this.rejectMotivation = rejectMotivation; + } + + public Map getResponse() { + return response; + } + + public void setResponse(Map response) { + this.response = response; + } + + public Map getRejectMotivation() { + return rejectMotivation; + } + + public void setRejectMotivation(Map rejectMotivation) { + this.rejectMotivation = rejectMotivation; + } + + public void addResponse(ArrowheadSystem system, Boolean resp) { + response.put(system, resp); + } + + public void addRejectMotivation(ArrowheadSystem system, QoSVerifierResponse.RejectMotivationTypes rejectType) { + rejectMotivation.put(system, rejectType); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerifierResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerifierResponse.java new file mode 100644 index 00000000..162bb06e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerifierResponse.java @@ -0,0 +1,49 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +/** + * Message Exchanged from QoSVerifierAlgorithm to QoSManager. + * + * @author Paulo + */ +public class QoSVerifierResponse { + + private boolean response; + private RejectMotivationTypes RejectMotivaiton; + + public QoSVerifierResponse() { + } + + public QoSVerifierResponse(boolean response, RejectMotivationTypes rejectMotivaiton) { + super(); + this.response = response; + RejectMotivaiton = rejectMotivaiton; + } + + public RejectMotivationTypes getRejectMotivation() { + return RejectMotivaiton; + } + + public void setRejectMotivaiton(RejectMotivationTypes rejectMotivaiton) { + RejectMotivaiton = rejectMotivaiton; + } + + public boolean getResponse() { + return response; + } + + public void setResponse(boolean response) { + this.response = response; + } + + public enum RejectMotivationTypes { + ALWAYS, TEMPORARY, COMBINATION + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerify.java b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerify.java new file mode 100644 index 00000000..6a05e34b --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/QoSVerify.java @@ -0,0 +1,77 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class QoSVerify { + + private ArrowheadSystem consumer; + private ArrowheadService requestedService; + private List providers = new ArrayList<>(); + private Map requestedQoS = new HashMap<>(); + private Map commands = new HashMap<>(); + + public QoSVerify() { + } + + public QoSVerify(ArrowheadSystem consumer, ArrowheadService requestedService, List providers, Map specifications, + Map commands) { + this.consumer = consumer; + this.requestedService = requestedService; + this.providers = providers; + this.requestedQoS = specifications; + this.commands = commands; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + public List getProviders() { + return providers; + } + + public void setProviders(List providers) { + this.providers = providers; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + + public Map getCommands() { + return commands; + } + + public void setCommands(Map commands) { + this.commands = commands; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/RawTokenInfo.java b/core-common/src/main/java/eu/arrowhead/common/messages/RawTokenInfo.java new file mode 100644 index 00000000..7fb7470e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/RawTokenInfo.java @@ -0,0 +1,48 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +public class RawTokenInfo { + + private String s; + private String c; + private Long e; + + public RawTokenInfo() { + } + + public String getS() { + return s; + } + + public void setS(String s) { + this.s = s; + } + + public String getC() { + return c; + } + + public void setC(String c) { + this.c = c; + } + + public Long getE() { + return e; + } + + public void setE(Long e) { + this.e = e; + } + + @Override + public String toString() { + return "ClassPojo [s = " + s + ", c = " + c + ", e = " + e + "]"; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/RemoveMonitorRule.java b/core-common/src/main/java/eu/arrowhead/common/messages/RemoveMonitorRule.java new file mode 100644 index 00000000..81937f37 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/RemoveMonitorRule.java @@ -0,0 +1,82 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadSystem; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Message used to delete a monitor rule. + * + * @author Renato Ayres + */ +@XmlRootElement +class RemoveMonitorRule { + + private ArrowheadSystem provider; + private ArrowheadSystem consumer; + + /** + * Creates a new instance with no parameters initialized. + */ + public RemoveMonitorRule() { + } + + /** + * Creates a new instance with the given service provider, and service consumer. + * + * @param provider the service provider + * @param consumer the service consumer + */ + public RemoveMonitorRule(ArrowheadSystem provider, ArrowheadSystem consumer) { + this.provider = provider; + this.consumer = consumer; + } + + /** + * Gets the service provider + * + * @return the service provider + */ + public ArrowheadSystem getProvider() { + return provider; + } + + /** + * Sets the service provider + * + * @param provider the service provider + */ + public void setProvider(ArrowheadSystem provider) { + this.provider = provider; + } + + /** + * Gets the service consumer + * + * @return the service consumer + */ + public ArrowheadSystem getConsumer() { + return consumer; + } + + /** + * Sets the service consumer + * + * @param consumer the service consumer + */ + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryByRegex.java b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryByRegex.java new file mode 100644 index 00000000..218b0849 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryByRegex.java @@ -0,0 +1,48 @@ +package eu.arrowhead.common.messages; + +import javax.validation.constraints.NotNull; + +public class ServiceQueryByRegex { + + public enum FieldType {systemName, serviceDefinition, interfaces} + + @NotNull(message = "Field name can not be null, valid values are: systemName, serviceDefinition, interfaces") + private FieldType fieldName; + @NotNull(message = "The regular expression can not be null") + private String regularExpression; + + private Boolean partialMatch = true; + + public ServiceQueryByRegex() { + } + + public ServiceQueryByRegex(FieldType fieldName, String regularExpression, Boolean partialMatch) { + this.fieldName = fieldName; + this.regularExpression = regularExpression; + this.partialMatch = partialMatch; + } + + public FieldType getFieldName() { + return fieldName; + } + + public void setFieldName(FieldType fieldName) { + this.fieldName = fieldName; + } + + public String getRegularExpression() { + return regularExpression; + } + + public void setRegularExpression(String regularExpression) { + this.regularExpression = regularExpression; + } + + public Boolean getPartialMatch() { + return partialMatch; + } + + public void setPartialMatch(Boolean partialMatch) { + this.partialMatch = partialMatch; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryForm.java new file mode 100644 index 00000000..fd9e6598 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryForm.java @@ -0,0 +1,72 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +public class ServiceQueryForm { + + @Valid + @NotNull + private ArrowheadService service; + private boolean pingProviders; + private boolean metadataSearch; + private Integer version; + + public ServiceQueryForm() { + } + + public ServiceQueryForm(ArrowheadService service, boolean pingProviders, boolean metadataSearch) { + this.service = service; + this.pingProviders = pingProviders; + this.metadataSearch = metadataSearch; + } + + public ServiceQueryForm(ServiceRequestForm srf) { + this.service = srf.getRequestedService(); + this.pingProviders = srf.getOrchestrationFlags().getOrDefault("pingProvider", false); + this.metadataSearch = srf.getOrchestrationFlags().getOrDefault("metadataSearch", false); + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + + public boolean isPingProviders() { + return pingProviders; + } + + public void setPingProviders(boolean pingProviders) { + this.pingProviders = pingProviders; + } + + + public boolean isMetadataSearch() { + return metadataSearch; + } + + public void setMetadataSearch(boolean metadataSearch) { + this.metadataSearch = metadataSearch; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryResult.java b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryResult.java new file mode 100644 index 00000000..04cc8bb3 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceQueryResult.java @@ -0,0 +1,39 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ServiceRegistryEntry; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; + +public class ServiceQueryResult { + + @Valid + private List serviceQueryData = new ArrayList<>(); + + public ServiceQueryResult() { + } + + public ServiceQueryResult(List serviceQueryData) { + this.serviceQueryData = serviceQueryData; + } + + public List getServiceQueryData() { + return serviceQueryData; + } + + public void setServiceQueryData(List serviceQueryData) { + this.serviceQueryData = serviceQueryData; + } + + public boolean isValid() { + return serviceQueryData != null && !serviceQueryData.isEmpty(); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/ServiceRequestForm.java b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceRequestForm.java new file mode 100644 index 00000000..e0e3bedf --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/ServiceRequestForm.java @@ -0,0 +1,212 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import com.fasterxml.jackson.annotation.JsonSetter; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.exception.BadPayloadException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * This is what the Orchestrator Core System receives from Arrowhead Systems trying to request services. + */ +public class ServiceRequestForm { + + private static final List flagKeys = new ArrayList<>(Arrays.asList("triggerInterCloud", "externalServiceRequest", "enableInterCloud", + "metadataSearch", "pingProviders", "overrideStore", "matchmaking", + "onlyPreferred", "enableQoS")); + @Valid + @NotNull + private ArrowheadSystem requesterSystem; + @Valid + private ArrowheadCloud requesterCloud; + @Valid + private ArrowheadService requestedService; + @Size(max = 9, message = "There are only 9 orchestration flags, map size must not be bigger than 9") + private Map orchestrationFlags = new HashMap<>(); + @Valid + private List preferredProviders = new ArrayList<>(); + private Map requestedQoS = new HashMap<>(); + private Map commands = new HashMap<>(); + + public ServiceRequestForm() { + for (String key : flagKeys) { + if (!orchestrationFlags.containsKey(key)) { + orchestrationFlags.put(key, false); + } + } + } + + private ServiceRequestForm(Builder builder) { + requesterSystem = builder.requesterSystem; + requesterCloud = builder.requesterCloud; + requestedService = builder.requestedService; + orchestrationFlags = builder.orchestrationFlags; + preferredProviders = builder.preferredProviders; + requestedQoS = builder.requestedQoS; + commands = builder.commands; + } + + public ArrowheadSystem getRequesterSystem() { + return requesterSystem; + } + + public void setRequesterSystem(ArrowheadSystem requesterSystem) { + this.requesterSystem = requesterSystem; + } + + public ArrowheadCloud getRequesterCloud() { + return requesterCloud; + } + + public void setRequesterCloud(ArrowheadCloud requesterCloud) { + this.requesterCloud = requesterCloud; + } + + public ArrowheadService getRequestedService() { + return requestedService; + } + + public void setRequestedService(ArrowheadService requestedService) { + this.requestedService = requestedService; + } + + + public Map getOrchestrationFlags() { + return orchestrationFlags; + } + + @JsonSetter + public void setOrchestrationFlags(Map orchestrationFlags) { + for (Map.Entry entry : orchestrationFlags.entrySet()) { + String key = entry.getKey(); + if (key == null || key.trim().isEmpty()) { + throw new BadPayloadException("SRF orchestration flag key can not be blank!"); + } + Boolean value = entry.getValue(); + if (value == null) { + throw new BadPayloadException("SRF orchestration flag value can not be null!"); + } + } + this.orchestrationFlags = orchestrationFlags; + } + + public List getPreferredProviders() { + return preferredProviders; + } + + public void setPreferredProviders(List preferredProviders) { + this.preferredProviders = preferredProviders; + } + + public Map getRequestedQoS() { + return requestedQoS; + } + + public void setRequestedQoS(Map requestedQoS) { + this.requestedQoS = requestedQoS; + } + + public Map getCommands() { + return commands; + } + + public void setCommands(Map commands) { + this.commands = commands; + } + + public static class Builder { + + // Required parameters + private ArrowheadSystem requesterSystem; + // Optional parameters + private ArrowheadCloud requesterCloud; + private ArrowheadService requestedService; + + private Map orchestrationFlags = new HashMap<>(); + private List preferredProviders = new ArrayList<>(); + private Map requestedQoS = new HashMap<>(); + private Map commands = new HashMap<>(); + + public Builder(ArrowheadSystem requesterSystem) { + this.requesterSystem = requesterSystem; + } + + public Builder requesterCloud(ArrowheadCloud cloud) { + requesterCloud = cloud; + return this; + } + + public Builder requestedService(ArrowheadService service) { + requestedService = service; + return this; + } + + public Builder orchestrationFlags(Map flags) { + orchestrationFlags = flags; + return this; + } + + public Builder preferredProviders(List providers) { + preferredProviders = providers; + return this; + } + + public Builder requestedQoS(Map qos) { + requestedQoS = qos; + return this; + } + + public Builder commands(Map commands) { + this.commands = commands; + return this; + } + + public ServiceRequestForm build() { + return new ServiceRequestForm(this); + } + } + + public void validateCrossParameterConstraints() { + for (String key : flagKeys) { + if (!orchestrationFlags.containsKey(key)) { + orchestrationFlags.put(key, false); + } + } + if (requestedService == null && orchestrationFlags.getOrDefault("overrideStore", false)) { + throw new BadPayloadException("RequestedService can not be null when overrideStore is TRUE"); + } + + if (orchestrationFlags.getOrDefault("onlyPreferred", false)) { + List tmp = new ArrayList<>(); + for (PreferredProvider provider : preferredProviders) { + if (!provider.isValid()) { + tmp.add(provider); + } + } + preferredProviders.removeAll(tmp); + if (preferredProviders.isEmpty()) { + throw new BadPayloadException("There is no valid PreferredProvider, but \"onlyPreferred\" is set to true"); + } + } + + if (orchestrationFlags.getOrDefault("enableQoS", false) && (requestedQoS.isEmpty() || commands.isEmpty())) { + throw new BadPayloadException("RequestedQoS or commands hashmap is empty while \"enableQoS\" is set to true"); + } + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/TokenData.java b/core-common/src/main/java/eu/arrowhead/common/messages/TokenData.java new file mode 100644 index 00000000..cebec787 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/TokenData.java @@ -0,0 +1,65 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import javax.validation.Valid; + +public class TokenData { + + @Valid + private ArrowheadSystem system; + @Valid + private ArrowheadService service; + private String token; + private String signature; + + public TokenData() { + } + + public TokenData(ArrowheadSystem system, ArrowheadService service, String token, String signature) { + this.system = system; + this.service = service; + this.token = token; + this.signature = signature; + } + + public ArrowheadSystem getSystem() { + return system; + } + + public void setSystem(ArrowheadSystem system) { + this.system = system; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenHelper.java b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenHelper.java new file mode 100644 index 00000000..617f0979 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenHelper.java @@ -0,0 +1,83 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +// This is just a utility class for the orchestration, it is not sent between core systems +public class TokenGenHelper { + + private ArrowheadService service; + private List providers = new ArrayList<>(); + + public TokenGenHelper() { + } + + public TokenGenHelper(ArrowheadService service, List providers) { + this.service = service; + this.providers = providers; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public List getProviders() { + return providers; + } + + public void setProviders(List providers) { + this.providers = providers; + } + + public static List convertOfList(List ofList) { + Set uniqueServices = new HashSet<>(); + for (OrchestrationForm form : ofList) { + Map metadata = form.getService().getServiceMetadata(); + if ("token".equals(metadata.get("security"))) { + uniqueServices.add(form.getService()); + } + } + + List tokenGenHelper = new ArrayList<>(); + for (ArrowheadService service : uniqueServices) { + TokenGenHelper tokenHelp = new TokenGenHelper(service, new ArrayList<>()); + for (OrchestrationForm form : ofList) { + ArrowheadService formService = form.getService(); + if (formService.equals(service) && "token".equals(formService.getServiceMetadata().get("security"))) { + tokenHelp.getProviders().add(form.getProvider()); + } + } + tokenGenHelper.add(tokenHelp); + } + + return tokenGenHelper; + } + + public static void updateFormsWithTokens(List ofList, List tokenData) { + for (TokenData data : tokenData) { + for (OrchestrationForm form : ofList) { + if (data.getService().equals(form.getService()) && data.getSystem().equals(form.getProvider())) { + form.setAuthorizationToken(data.getToken()); + form.setSignature(data.getSignature()); + } + } + } + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationRequest.java b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationRequest.java new file mode 100644 index 00000000..ec250b59 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationRequest.java @@ -0,0 +1,86 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.hibernate.validator.constraints.NotEmpty; + +public class TokenGenerationRequest { + + @Valid + @NotNull + private ArrowheadSystem consumer; + @Valid + private ArrowheadCloud consumerCloud; + @Valid + @NotEmpty + private List providers = new ArrayList<>(); + @Valid + @NotNull + private ArrowheadService service; + private int duration; + + public TokenGenerationRequest() { + } + + public TokenGenerationRequest(ArrowheadSystem consumer, ArrowheadCloud consumerCloud, List providers, ArrowheadService service, + int duration) { + this.consumer = consumer; + this.consumerCloud = consumerCloud; + this.providers = providers; + this.service = service; + this.duration = duration; + } + + public ArrowheadSystem getConsumer() { + return consumer; + } + + public void setConsumer(ArrowheadSystem consumer) { + this.consumer = consumer; + } + + public ArrowheadCloud getConsumerCloud() { + return consumerCloud; + } + + public void setConsumerCloud(ArrowheadCloud consumerCloud) { + this.consumerCloud = consumerCloud; + } + + public List getProviders() { + return providers; + } + + public void setProviders(List providers) { + this.providers = providers; + } + + public ArrowheadService getService() { + return service; + } + + public void setService(ArrowheadService service) { + this.service = service; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationResponse.java b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationResponse.java new file mode 100644 index 00000000..4b2a2c71 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/messages/TokenGenerationResponse.java @@ -0,0 +1,34 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.messages; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.Valid; + +public class TokenGenerationResponse { + + @Valid + private List tokenData = new ArrayList<>(); + + public TokenGenerationResponse() { + } + + public TokenGenerationResponse(List tokenData) { + this.tokenData = tokenData; + } + + public List getTokenData() { + return tokenData; + } + + public void setTokenData(List tokenData) { + this.tokenData = tokenData; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystem.java b/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystem.java new file mode 100644 index 00000000..48b20ce1 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystem.java @@ -0,0 +1,99 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +import eu.arrowhead.common.ArrowheadMain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public enum CoreSystem { + AUTHORIZATION(8444, 8445, Arrays.asList("db_user", "db_password", "db_address", "keystore", "keystorepass"), + Arrays.asList("keypass", "truststore", "truststorepass"), + Arrays.asList(CoreSystemService.AUTH_CONTROL_SERVICE, CoreSystemService.TOKEN_GEN_SERVICE), 62541), + CERTIFICATE_AUTHORITY(8458, 8459, null, null, null, 62542), CHOREOGRAPHER(8456, 8457, null, null, null, 62543), + EVENT_HANDLER(8454, 8455, ArrowheadMain.dbFields, ArrowheadMain.certFields, + Arrays.asList(CoreSystemService.EVENT_PUBLISH, CoreSystemService.EVENT_SUBSCRIPTION),62544), + GATEKEEPER_INTERNAL(8446, 8447, ArrowheadMain.dbFields, + Arrays.asList("gatekeeper_keystore", "gatekeeper_keystore_pass", "gatekeeper_keypass", "cloud_keystore", + "cloud_keystore_pass", "cloud_keypass", "master_arrowhead_cert"), + null, 62545), + GATEKEEPER_EXTERNAL(8448, 8449, ArrowheadMain.dbFields, + Arrays.asList("gatekeeper_keystore", "gatekeeper_keystore_pass", "gatekeeper_keypass", "cloud_keystore", + "cloud_keystore_pass", "cloud_keypass", "master_arrowhead_cert"), + Arrays.asList(CoreSystemService.GSD_SERVICE, CoreSystemService.ICN_SERVICE), 62546), + GATEWAY(8452, 8453, null, + Arrays.asList("keystore", "keystorepass", "keypass", "truststore", "truststorepass", "trustpass", + "master_arrowhead_cert"), + Arrays.asList(CoreSystemService.GW_PROVIDER_SERVICE, CoreSystemService.GW_CONSUMER_SERVICE, + CoreSystemService.GW_SESSION_MGMT), 62547), + ORCHESTRATOR(8440, 8441, ArrowheadMain.dbFields, ArrowheadMain.certFields, + Collections.singletonList(CoreSystemService.ORCH_SERVICE), 62548), + QOS(8450, 8451, Arrays.asList("db_user", "db_password", "db_address", "monitor_url"), ArrowheadMain.certFields, + null, 62548), + SERVICE_REGISTRY_DNS(8442, 8443, null, ArrowheadMain.certFields, null, 62549), + SERVICE_REGISTRY_SQL(8442, 8443, ArrowheadMain.dbFields, ArrowheadMain.certFields, null, 62550), + SYSTEM_REGISTRY(8436, 8437, ArrowheadMain.dbFields, ArrowheadMain.certFields, + Collections.singletonList(CoreSystemService.SYS_REG_SERVICE), 62551), + DEVICE_REGISTRY(8438, 8439, ArrowheadMain.dbFields, ArrowheadMain.certFields, + Collections.singletonList(CoreSystemService.DEVICE_REG_SERVICE), 62552); + + private final int insecurePort; + private final int securePort; + private final int opcuaPort; + private final List alwaysMandatoryFields; + private final List secureMandatoryFields; + private final List services; + + CoreSystem(int insecPort, int secPort, List awf, List smf, List cs, + int opcuaPort) { + insecurePort = insecPort; + securePort = secPort; + if (awf == null) { + alwaysMandatoryFields = new ArrayList<>(); + } else { + alwaysMandatoryFields = awf; + } + if (smf == null) { + secureMandatoryFields = new ArrayList<>(); + } else { + secureMandatoryFields = smf; + } + if (cs == null) { + services = new ArrayList<>(); + } else { + services = cs; + } + this.opcuaPort = opcuaPort; + } + + public int getInsecurePort() { + return insecurePort; + } + + public int getSecurePort() { + return securePort; + } + + public List getAlwaysMandatoryFields() { + return alwaysMandatoryFields; + } + + public List getSecureMandatoryFields() { + return secureMandatoryFields; + } + + public List getServices() { + return services; + } + + public int getOpcUaPort() { + return opcuaPort; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystemService.java b/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystemService.java new file mode 100644 index 00000000..586944ee --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/CoreSystemService.java @@ -0,0 +1,40 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +public enum CoreSystemService { + AUTH_CONTROL_SERVICE("AuthorizationControl", "authorization"), + TOKEN_GEN_SERVICE("TokenGeneration", "authorization/token"), + EVENT_PUBLISH("EventPublishing", "eventhandler/publish"), + EVENT_SUBSCRIPTION("EventSubscription", "eventhandler/subscription"), + GSD_SERVICE("GlobalServiceDiscovery", "gatekeeper/init_gsd"), + ICN_SERVICE("InterCloudNegotiations", "gatekeeper/init_icn"), + GW_CONSUMER_SERVICE("ConnectToConsumer", "gateway/connectToConsumer"), + GW_PROVIDER_SERVICE("ConnectToProvider", "gateway/connectToProvider"), + GW_SESSION_MGMT("SessionManagement", "gateway/management"), + ORCH_SERVICE("OrchestrationService", "orchestrator/orchestration"), + SYS_REG_SERVICE("PlaceHolder", "systemregistry/placeholder"), + DEVICE_REG_SERVICE("Placeholder2", "deviceregistry/placeholder"); + + private final String serviceDef; + private final String serviceURI; + + CoreSystemService(String serviceDef, String serviceURI) { + this.serviceDef = serviceDef; + this.serviceURI = serviceURI; + } + + public String getServiceDef() { + return serviceDef; + } + + public String getServiceURI() { + return serviceURI; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/GetCoreSystemServicesTask.java b/core-common/src/main/java/eu/arrowhead/common/misc/GetCoreSystemServicesTask.java new file mode 100644 index 00000000..b1a4fd17 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/GetCoreSystemServicesTask.java @@ -0,0 +1,55 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +import eu.arrowhead.common.Utility; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Timer; +import java.util.TimerTask; + +public class GetCoreSystemServicesTask extends TimerTask { + + private Object context; + private List serviceDefs; + private Map uriMap = new HashMap<>(); + + public GetCoreSystemServicesTask(Object context, List serviceDefs) { + if (context instanceof NeedsCoreSystemService) { + this.context = context; + } else { + throw new AssertionError("This task can only be called with a class that implements the NeedsCoreSystemService interface!"); + } + this.serviceDefs = serviceDefs; + } + + @Override + public void run() { + for (String serviceDef : serviceDefs) { + Optional optionalUri = Utility.getServiceInfo(serviceDef); + optionalUri.ifPresent(uri -> uriMap.put(serviceDef, uri)); + } + + if (uriMap.size() < serviceDefs.size()) { + TimerTask task = new GetCoreSystemServicesTask(this.context, this.serviceDefs); + Timer timer = new Timer(); + timer.schedule(task, 30L * 1000L); //run the same task again, 30 sec later if not all Core System Services were found + } + + try { + Method method = context.getClass().getMethod("getCoreSystemServiceURIs", Map.class); + method.invoke(context, uriMap); + } catch (Exception e) { + throw new AssertionError("Exception occurred with Java reflection!", e); + } + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/NeedsCoreSystemService.java b/core-common/src/main/java/eu/arrowhead/common/misc/NeedsCoreSystemService.java new file mode 100644 index 00000000..5ae89634 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/NeedsCoreSystemService.java @@ -0,0 +1,16 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +import java.util.Map; + +public interface NeedsCoreSystemService { + + void getCoreSystemServiceURIs(Map uriMap); + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/SecurityUtils.java b/core-common/src/main/java/eu/arrowhead/common/misc/SecurityUtils.java new file mode 100644 index 00000000..f2f19b4a --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/SecurityUtils.java @@ -0,0 +1,318 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +import eu.arrowhead.common.exception.ArrowheadException; +import eu.arrowhead.common.exception.AuthException; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Enumeration; +import java.util.NoSuchElementException; +import java.util.ServiceConfigurationError; +import java.util.regex.Pattern; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import org.apache.log4j.Logger; + +@SuppressWarnings("unused") +public final class SecurityUtils { + + private static final Logger log = Logger.getLogger(SecurityUtils.class.getName()); + + public static KeyStore loadKeyStore(String filePath, String pass) { + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream is = new FileInputStream(filePath); + keystore.load(is, pass.toCharArray()); + is.close(); + return keystore; + } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) { + log.fatal("Loading the keystore failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Loading the keystore failed...", e); + } + } + + public static X509Certificate getFirstCertFromKeyStore(KeyStore keystore) { + try { + Enumeration enumeration = keystore.aliases(); + String alias = enumeration.nextElement(); + return (X509Certificate) keystore.getCertificate(alias); + } catch (KeyStoreException | NoSuchElementException e) { + log.error("Getting the first cert from keystore failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Getting the first cert from keystore failed...", e); + } + } + + public static String getFirstAliasFromKeyStore(KeyStore keyStore) { + try { + Enumeration aliases = keyStore.aliases(); + return aliases.nextElement(); + } catch (KeyStoreException e) { + log.error("Getting the first alias from keystore failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Getting the first alias from keystore failed...", e); + } + } + + public static String getCertCNFromSubject(String subjectName) { + String cn = null; + try { + // Subject is in LDAP format, we can use the LdapName object for parsing + LdapName ldapname = new LdapName(subjectName); + for (Rdn rdn : ldapname.getRdns()) { + // Find the data after the CN field + if (rdn.getType().equalsIgnoreCase("CN")) { + cn = (String) rdn.getValue(); + } + } + } catch (InvalidNameException e) { + System.out.println("InvalidNameException in getCertCNFromSubject: " + e.getMessage()); + return ""; + } + + if (cn == null) { + return ""; + } + + return cn; + } + + public static PrivateKey getPrivateKey(KeyStore keystore, String pass) { + PrivateKey privatekey = null; + String element; + try { + Enumeration enumeration = keystore.aliases(); + while (enumeration.hasMoreElements()) { + element = enumeration.nextElement(); + privatekey = (PrivateKey) keystore.getKey(element, pass.toCharArray()); + if (privatekey != null) { + break; + } + } + } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) { + log.error("Getting the private key from keystore failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Getting the private key from keystore failed...", e); + } + + if (privatekey == null) { + throw new ServiceConfigurationError("Getting the private key failed, keystore aliases do not identify a key."); + } + return privatekey; + } + + public static KeyStore createKeyStoreFromCert(String filePath, String alias) { + try { + InputStream is = new FileInputStream(filePath); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(is); + + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + keystore.load(null); // We don't need the KeyStore instance to come from a file. + keystore.setCertificateEntry(alias, cert); + return keystore; + } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) { + log.fatal("Keystore creation from cert failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Keystore creation from cert failed...", e); + } + } + + public static SSLContext createMasterSSLContext(String keyStorePath, String keyStorePass, String keyPass, String masterArrowheadCertPath) { + try { + KeyStore keyStore = loadKeyStore(keyStorePath, keyStorePass); + String kmfAlgorithm = System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(kmfAlgorithm); + keyManagerFactory.init(keyStore, keyPass != null ? keyPass.toCharArray() : keyStorePass.toCharArray()); + + KeyStore trustStore = createKeyStoreFromCert(masterArrowheadCertPath, "arrowhead.eu"); + String tmfAlgorithm = System.getProperty("ssl.TrustManagerFactory.algorithm", TrustManagerFactory.getDefaultAlgorithm()); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm); + trustManagerFactory.init(trustStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + return sslContext; + } catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException | KeyManagementException e) { + log.fatal("Master SSLContext creation failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("Master SSLContext creation failed...", e); + } + } + + public static SSLContext createSSLContextWithDummyTrustManager(String keyStorePath, String keyStorePass) { + try { + KeyStore keyStore = loadKeyStore(keyStorePath, keyStorePass); + String kmfAlgorithm = System.getProperty("ssl.KeyManagerFactory.algorithm", KeyManagerFactory.getDefaultAlgorithm()); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(kmfAlgorithm); + keyManagerFactory.init(keyStore, keyStorePass.toCharArray()); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagerFactory.getKeyManagers(), createTrustManagers(), null); + return sslContext; + } catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException | KeyManagementException e) { + log.fatal("SSLContext (with dummy trust manager) creation failed: " + e.toString() + " " + e.getMessage()); + throw new ServiceConfigurationError("AcceptAll SSLContext creation failed...", e); + } + } + + public static boolean isKeyStoreCNArrowheadValid(String commonName) { + String[] cnFields = commonName.split("\\.", 0); + return cnFields.length == 5 && cnFields[3].equals("arrowhead") && cnFields[4].equals("eu"); + } + + public static boolean isKeyStoreCNArrowheadValid(String clientCN, String cloudCN) { + String[] clientFields = clientCN.split("\\.", 2); + return cloudCN.equalsIgnoreCase(clientFields[1]); + } + + public static boolean isTrustStoreCNArrowheadValid(String commonName) { + String[] cnFields = commonName.split("\\.", 0); + return cnFields.length == 4 && cnFields[2].equals("arrowhead") && cnFields[3].equals("eu"); + } + + public static boolean isKeyStoreCNArrowheadValidLegacy(String commonName) { + String[] cnFields = commonName.split("\\.", 0); + return cnFields.length == 6 && cnFields[3].equals("arrowhead") && cnFields[4].equals("eu"); + } + + public static X509Certificate getCertFromKeyStore(KeyStore keystore, String name) { + Enumeration enumeration; + try { + enumeration = keystore.aliases(); + } catch (KeyStoreException e) { + log.error("getCertFromKeyStore throws KeyStoreException"); + return null; + } + + while (enumeration.hasMoreElements()) { + String alias = enumeration.nextElement(); + + X509Certificate clientCert; + try { + clientCert = (X509Certificate) keystore.getCertificate(alias); + } catch (KeyStoreException e) { + log.error("getCertFromKeyStore throws KeyStoreException when loading cert: " + alias); + continue; + } + String clientCertCN = getCertCNFromSubject(clientCert.getSubjectDN().getName()); + + if (!clientCertCN.equals(name)) { + continue; + } + return clientCert; + } + + return null; + } + + public static String getKeyEncoded(Key key) { + if (key == null) { + return ""; + } + + byte[] encpub = key.getEncoded(); + StringBuilder sb = new StringBuilder(encpub.length * 2); + for (byte b : encpub) { + sb.append(String.format("%02x", b & 0xff)); + } + return sb.toString(); + } + + public static String getByteEncoded(byte[] array) { + StringBuilder sb = new StringBuilder(array.length * 2); + for (byte b : array) { + sb.append(String.format("%02X", b & 0xff)); + } + return sb.toString(); + } + + /** + * Extract a public key either from a PEM encoded file or directly from the Base64 coded string. + * + * @param filePathOrEncodedKey either a file path for the PEM file or the Base64 encoded key + * @param isFilePath true if the first parameter is a file path + * + * @return the PublicKey + */ + public static PublicKey getPublicKey(String filePathOrEncodedKey, boolean isFilePath) { + byte[] keyBytes; + if (isFilePath) { + keyBytes = loadPEM(filePathOrEncodedKey); + } else { + try { + keyBytes = Base64.getDecoder().decode(filePathOrEncodedKey); + } catch (IllegalArgumentException | NullPointerException e) { + throw new AuthException("Public key decoding failed! Caused by: " + e.getMessage(), e); + } + } + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePublic(new X509EncodedKeySpec(keyBytes)); + } catch (NoSuchAlgorithmException e) { + log.fatal("KeyFactory.getInstance(String) throws NoSuchAlgorithmException, code needs to be changed!"); + throw new AssertionError("KeyFactory.getInstance(String) throws NoSuchAlgorithmException, code needs to be changed!", e); + } catch (InvalidKeySpecException e) { + log.error("getPublicKey: X509 keyspec could not be created from the decoded bytes."); + throw new AuthException("PublicKey decoding failed due wrong input key", e); + } + } + + public static byte[] loadPEM(String filePath) { + try (FileInputStream in = new FileInputStream(filePath)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + for (int read = 0; read != -1; read = in.read(buf)) { + baos.write(buf, 0, read); + } + String pem = new String(baos.toByteArray(), StandardCharsets.ISO_8859_1); + baos.close(); + Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*"); + String encoded = parse.matcher(pem).replaceFirst("$1"); + return Base64.getMimeDecoder().decode(encoded); + } catch (IOException e) { + throw new ArrowheadException("IOException occurred during PEM file loading from " + filePath, e); + } + } + + private static TrustManager[] createTrustManagers() { + return new TrustManager[]{new X509TrustManager() { + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + }}; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/TypeSafeProperties.java b/core-common/src/main/java/eu/arrowhead/common/misc/TypeSafeProperties.java new file mode 100644 index 00000000..e0f06759 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/TypeSafeProperties.java @@ -0,0 +1,32 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.misc; + +import java.util.Properties; + +public class TypeSafeProperties extends Properties { + + public int getIntProperty(String key, int defaultValue) { + String val = getProperty(key); + try { + return (val == null) ? defaultValue : Integer.valueOf(val); + } catch (NumberFormatException e) { + System.out + .println(val + " is not a valid number! Please fix the \"" + key + "\" property! Using default value (" + defaultValue + ") instead!"); + return defaultValue; + } + } + + public boolean getBooleanProperty(String key, boolean defaultValue) { + String val = getProperty(key); + return (val == null) ? defaultValue : Boolean.valueOf(val); + } + + //NOTE add more data types later if needed + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryResource.java b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryResource.java new file mode 100644 index 00000000..b915c70f --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryResource.java @@ -0,0 +1,54 @@ +package eu.arrowhead.common.misc.registry_interfaces; + +import eu.arrowhead.common.exception.ArrowheadException; +import javax.validation.Valid; + +/** + * A RegistryResource offers methods to publish, unpublish and lookup entities + * of type T + * + * @param The generic type of the entity which is handled by this registry + * @param The generic response type of requests. e.g. + * {@link javax.ws.rs.core.Response} + * + * @author mzsilak + */ +public interface RegistryResource extends Resource { + + final static String LOOKUP_PATH = "/lookup/{id}"; + final static String PUBLISH_PATH = "/publish"; + final static String UNPUBLISH_PATH = "/unpublish"; + + /** + * A lookup for a specific entity T which is identified by the + * parameter id. + * + * @param id The identifier of the entity + * + * @return A single entity embedded in the generic response type + * R + */ + R lookup(final long id) throws ArrowheadException; + + /** + * A publish method for a specific entity T which registers the + * entity to the registry. + * + * @param entity The entity which shall be registered + * + * @return The registered entity embedded in the generic response type + * R + */ + R publish(@Valid final T entity) throws ArrowheadException; + + /** + * A unpublish method for a specific entity T which removes the + * entity from the registry. + * + * @param entity The entity which shall be removed + * + * @return The removed entity embedded in the generic response type + * R + */ + R unpublish(@Valid final T entity) throws ArrowheadException; +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryService.java b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryService.java new file mode 100644 index 00000000..b1e6da1c --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/RegistryService.java @@ -0,0 +1,13 @@ +package eu.arrowhead.common.misc.registry_interfaces; + +import eu.arrowhead.common.exception.ArrowheadException; +import javax.validation.Valid; + +public interface RegistryService { + + T lookup(final long id) throws ArrowheadException; + + T publish(@Valid final T entity) throws ArrowheadException; + + T unpublish(@Valid final T entity) throws ArrowheadException; +} diff --git a/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/Resource.java b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/Resource.java new file mode 100644 index 00000000..b584ad06 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/misc/registry_interfaces/Resource.java @@ -0,0 +1,18 @@ +package eu.arrowhead.common.misc.registry_interfaces; + +/** + * This interface denotes a resource in the Arrowhead framework which is reachable by non-RMI methods. + * + * @param The generic response type of requests. e.g. {@link javax.ws.rs.core.Response} + * + * @author Mario ZSILAK + */ +public interface Resource { + + /** + * A ping method to verify the general reachability of this resource. + * + * @return A positive response with the given generic type. + */ + R ping(); +} diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaNamespace.java b/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaNamespace.java new file mode 100644 index 00000000..f242f950 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaNamespace.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019 the Eclipse Milo Authors + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package eu.arrowhead.common.opcua; + +import java.util.List; + +import org.eclipse.milo.opcua.sdk.server.OpcUaServer; +import org.eclipse.milo.opcua.sdk.server.UaNodeManager; +import org.eclipse.milo.opcua.sdk.server.api.DataItem; +import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespace; +import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem; +import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext; +import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArrowheadOpcUaNamespace extends ManagedNamespace { + + static final String NAMESPACE_URI = "urn:arrowhead:opcua"; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final SubscriptionModel subscriptionModel; + + ArrowheadOpcUaNamespace(OpcUaServer server, String namespaceUri) { + super(server, namespaceUri); + + subscriptionModel = new SubscriptionModel(server, this); + } + + @Override + protected void onStartup() { + super.onStartup(); + } + + @Override + public void onDataItemsCreated(List dataItems) { + subscriptionModel.onDataItemsCreated(dataItems); + } + + @Override + public void onDataItemsModified(List dataItems) { + subscriptionModel.onDataItemsModified(dataItems); + } + + @Override + public void onDataItemsDeleted(List dataItems) { + subscriptionModel.onDataItemsDeleted(dataItems); + } + + @Override + public void onMonitoringModeChanged(List monitoredItems) { + subscriptionModel.onMonitoringModeChanged(monitoredItems); + } + + public UaNodeContext getNamespaceNodeContext() { + // TODO Auto-generated method stub + return getNodeContext(); + } + + public UaNodeManager getNamespaceNodeManager() { + // TODO Auto-generated method stub + return getNodeManager(); + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaServer.java b/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaServer.java new file mode 100644 index 00000000..6bf6eef9 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/ArrowheadOpcUaServer.java @@ -0,0 +1,274 @@ +package eu.arrowhead.common.opcua; + +import static com.google.common.collect.Lists.newArrayList; +import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS; +import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME; +import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_X509; + +import java.io.File; +import java.security.KeyPair; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.eclipse.milo.opcua.sdk.core.Reference; +import org.eclipse.milo.opcua.sdk.server.OpcUaServer; +import org.eclipse.milo.opcua.sdk.server.UaNodeManager; +import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig; +import org.eclipse.milo.opcua.sdk.server.api.methods.AbstractMethodInvocationHandler; +import org.eclipse.milo.opcua.sdk.server.identity.CompositeValidator; +import org.eclipse.milo.opcua.sdk.server.identity.UsernameIdentityValidator; +import org.eclipse.milo.opcua.sdk.server.identity.X509IdentityValidator; +import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode; +import org.eclipse.milo.opcua.sdk.server.nodes.UaMethodNode; +import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext; +import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil; +import org.eclipse.milo.opcua.stack.core.Identifiers; +import org.eclipse.milo.opcua.stack.core.StatusCodes; +import org.eclipse.milo.opcua.stack.core.UaRuntimeException; +import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateManager; +import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateValidator; +import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager; +import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; +import org.eclipse.milo.opcua.stack.core.transport.TransportProfile; +import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime; +import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; +import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId; +import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName; +import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort; +import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode; +import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo; +import org.eclipse.milo.opcua.stack.core.util.CertificateUtil; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedHttpsCertificateBuilder; +import org.eclipse.milo.opcua.stack.server.EndpointConfiguration; +import org.slf4j.LoggerFactory; + +public class ArrowheadOpcUaServer { + + private static ArrowheadOpcUaServer arrowheadOpcUaserver; + + public static ArrowheadOpcUaServer getInstance(int port, String endpoint) { + if (arrowheadOpcUaserver == null) { + synchronized (ArrowheadOpcUaServer.class) { + try { + arrowheadOpcUaserver = new ArrowheadOpcUaServer(port, endpoint); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + return arrowheadOpcUaserver; + } + + private static final int HTTPS_BIND_PORT = 8443; + + static { + + // Required for SecurityPolicy.Aes256_Sha256_RsaPss + Security.addProvider(new BouncyCastleProvider()); + } + + private final OpcUaServer server; + private UaNodeContext nodeContext; + private UaNodeManager nodeManager; + + private ArrowheadOpcUaServer(int port, String endpoint) throws Exception { + File securityTempDir = new File(System.getProperty("java.io.tmpdir"), "security"); + if (!securityTempDir.exists() && !securityTempDir.mkdirs()) { + throw new Exception("unable to create security temp dir: " + securityTempDir); + } + LoggerFactory.getLogger(getClass()).info("security temp dir: {}", securityTempDir.getAbsolutePath()); + + KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir); + + DefaultCertificateManager certificateManager = new DefaultCertificateManager(loader.getServerKeyPair(), + loader.getServerCertificateChain()); + + File pkiDir = securityTempDir.toPath().resolve("pki").toFile(); + DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir); + LoggerFactory.getLogger(getClass()).info("pki dir: {}", pkiDir.getAbsolutePath()); + + DefaultCertificateValidator certificateValidator = new DefaultCertificateValidator(trustListManager); + + KeyPair httpsKeyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); + + SelfSignedHttpsCertificateBuilder httpsCertificateBuilder = new SelfSignedHttpsCertificateBuilder(httpsKeyPair); + httpsCertificateBuilder.setCommonName(HostnameUtil.getHostname()); + HostnameUtil.getHostnames("0.0.0.0").forEach(httpsCertificateBuilder::addDnsName); +// X509Certificate httpsCertificate = httpsCertificateBuilder.build(); + + UsernameIdentityValidator identityValidator = new UsernameIdentityValidator(true, authChallenge -> { + String username = authChallenge.getUsername(); + String password = authChallenge.getPassword(); + + boolean userOk = "user".equals(username) && "password1".equals(password); + boolean adminOk = "admin".equals(username) && "password2".equals(password); + + return userOk || adminOk; + }); + + X509IdentityValidator x509IdentityValidator = new X509IdentityValidator(c -> true); + + // If you need to use multiple certificates you'll have to be smarter than this. + X509Certificate certificate = certificateManager.getCertificates().stream().findFirst() + .orElseThrow(() -> new UaRuntimeException(StatusCodes.Bad_ConfigurationError, "no certificate found")); + + // The configured application URI must match the one in the certificate(s) + String applicationUri = CertificateUtil.getSanUri(certificate) + .orElseThrow(() -> new UaRuntimeException(StatusCodes.Bad_ConfigurationError, + "certificate is missing the application URI")); + + Set endpointConfigurations = createEndpointConfigurations(certificate, endpoint, port); + + OpcUaServerConfig serverConfig = OpcUaServerConfig.builder().setApplicationUri(applicationUri) + .setApplicationName(LocalizedText.english("Arrowhead OPC UA "+ endpoint.toUpperCase() + " Server")) + .setEndpoints(endpointConfigurations) + .setBuildInfo(new BuildInfo("urn:arrowhead:opcua-sr-server", "arrowhead", "arrowhead opcua server", + OpcUaServer.SDK_VERSION, "", DateTime.now())) + .setCertificateManager(certificateManager).setTrustListManager(trustListManager) + .setCertificateValidator(certificateValidator).setHttpsKeyPair(httpsKeyPair) +// .setHttpsCertificate(httpsCertificate) + .setIdentityValidator(new CompositeValidator(identityValidator, x509IdentityValidator)) + .setProductUri("urn:arrowhead:opcua-sr-server").build(); + + server = new OpcUaServer(serverConfig); + + ArrowheadOpcUaNamespace namespace = new ArrowheadOpcUaNamespace(server, + "urn:arrowhead:namespace"); + namespace.startup(); + nodeContext = namespace.getNamespaceNodeContext(); + nodeManager = namespace.getNamespaceNodeManager(); + } + + private Set createEndpointConfigurations(X509Certificate certificate, String endpoint, int port) { + Set endpointConfigurations = new LinkedHashSet<>(); + + List bindAddresses = newArrayList(); + bindAddresses.add("0.0.0.0"); + + Set hostnames = new LinkedHashSet<>(); + hostnames.add(HostnameUtil.getHostname()); + hostnames.addAll(HostnameUtil.getHostnames("0.0.0.0")); + + for (String bindAddress : bindAddresses) { + for (String hostname : hostnames) { + EndpointConfiguration.Builder builder = EndpointConfiguration.newBuilder().setBindAddress(bindAddress) + .setHostname(hostname).setPath("/" + endpoint).setCertificate(certificate).addTokenPolicies( + USER_TOKEN_POLICY_ANONYMOUS, USER_TOKEN_POLICY_USERNAME, USER_TOKEN_POLICY_X509); + + EndpointConfiguration.Builder noSecurityBuilder = builder.copy().setSecurityPolicy(SecurityPolicy.None) + .setSecurityMode(MessageSecurityMode.None); + + endpointConfigurations.add(buildTcpEndpoint(port, noSecurityBuilder)); +// endpointConfigurations.add(buildHttpsEndpoint(noSecurityBuilder)); + + // TCP Basic256Sha256 / SignAndEncrypt +// endpointConfigurations +// .add(buildTcpEndpoint(port, builder.copy().setSecurityPolicy(SecurityPolicy.Basic256Sha256) +// .setSecurityMode(MessageSecurityMode.SignAndEncrypt))); + + // HTTPS Basic256Sha256 / Sign (SignAndEncrypt not allowed for HTTPS) +// endpointConfigurations.add(buildHttpsEndpoint(builder.copy() +// .setSecurityPolicy(SecurityPolicy.Basic256Sha256).setSecurityMode(MessageSecurityMode.Sign))); + + /* + * It's good practice to provide a discovery-specific endpoint with no security. + * It's required practice if all regular endpoints have security configured. + * + * Usage of the "/discovery" suffix is defined by OPC UA Part 6: + * + * Each OPC UA Server Application implements the Discovery Service Set. If the + * OPC UA Server requires a different address for this Endpoint it shall create + * the address by appending the path "/discovery" to its base address. + */ + + EndpointConfiguration.Builder discoveryBuilder = builder.copy().setPath("/" + endpoint + "/discovery") + .setSecurityPolicy(SecurityPolicy.None).setSecurityMode(MessageSecurityMode.None); + + endpointConfigurations.add(buildTcpEndpoint(port, discoveryBuilder)); +// endpointConfigurations.add(buildHttpsEndpoint(discoveryBuilder)); + } + } + + return endpointConfigurations; + } + + private static EndpointConfiguration buildTcpEndpoint(int port, EndpointConfiguration.Builder base) { + return base.copy().setTransportProfile(TransportProfile.TCP_UASC_UABINARY).setBindPort(port).build(); + } + + private static EndpointConfiguration buildHttpsEndpoint(EndpointConfiguration.Builder base) { + return base.copy().setTransportProfile(TransportProfile.HTTPS_UABINARY).setBindPort(HTTPS_BIND_PORT).build(); + } + + public OpcUaServer getServer() { + return server; + } + + public UaNodeContext getNodeContext() { + return nodeContext; + } + + public UaNodeManager getNodeManager() { + return nodeManager; + } + + public CompletableFuture startup() { + return server.startup(); + } + + public CompletableFuture shutdown() { + return server.shutdown(); + } + + public void addMethodNodeInNamespace(UaMethodNode methodNode, UaFolderNode folderNode, + AbstractMethodInvocationHandler methodClass) { + + methodNode.setProperty(UaMethodNode.InputArguments, methodClass.getInputArguments()); + methodNode.setProperty(UaMethodNode.OutputArguments, methodClass.getOutputArguments()); + methodNode.setInvocationHandler(methodClass); + + nodeManager.addNode(methodNode); + + methodNode.addReference(new Reference(methodNode.getNodeId(), Identifiers.HasComponent, + folderNode.getNodeId().expanded(), false)); + + nodeManager.addNode(methodNode); + + folderNode.addReference(new Reference(folderNode.getNodeId(), Identifiers.HasComponent, + methodNode.getNodeId().expanded(), true)); + + methodNode.addReference(new Reference(methodNode.getNodeId(), Identifiers.HasComponent, + folderNode.getNodeId().expanded(), false)); + + } + + public UaMethodNode addMethodNode(UShort namespaceIndex, UaFolderNode folderNode, String name) { + UaMethodNode newMethodNode = UaMethodNode.builder(nodeContext).setNodeId(new NodeId(namespaceIndex, name)) + .setBrowseName(new QualifiedName(namespaceIndex, name)).setDisplayName(new LocalizedText(null, name)) + .setDescription(LocalizedText.english("")).build(); + + return newMethodNode; + }; + + public UaFolderNode addFolder(UShort namespaceIndex, String name) { + NodeId folderNodeId = new NodeId(namespaceIndex, name); + + UaFolderNode folderNode = new UaFolderNode(nodeContext, folderNodeId, new QualifiedName(namespaceIndex, name), + LocalizedText.english(name)); + + nodeManager.addNode(folderNode); + + folderNode.addReference( + new Reference(Identifiers.ObjectsFolder, Identifiers.Organizes, folderNodeId.expanded(), true)); + + return folderNode; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/KeyStoreLoader.java b/core-common/src/main/java/eu/arrowhead/common/opcua/KeyStoreLoader.java new file mode 100644 index 00000000..a5f6ee65 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/KeyStoreLoader.java @@ -0,0 +1,105 @@ +package eu.arrowhead.common.opcua; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.UUID; +import java.util.regex.Pattern; + +import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; +import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class KeyStoreLoader { + + private static final Pattern IP_ADDR_PATTERN = Pattern.compile( + "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); + + private static final String SERVER_ALIAS = "server-ai"; + private static final char[] PASSWORD = "password".toCharArray(); + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private X509Certificate[] serverCertificateChain; + private X509Certificate serverCertificate; + private KeyPair serverKeyPair; + + KeyStoreLoader load(File baseDir) throws Exception { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + + File serverKeyStore = baseDir.toPath().resolve("example-server.pfx").toFile(); + + logger.info("Loading KeyStore at {}", serverKeyStore); + + if (!serverKeyStore.exists()) { + keyStore.load(null, PASSWORD); + + KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); + + String applicationUri = "urn:eclipse:milo:examples:server:" + UUID.randomUUID(); + + SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair) + .setCommonName("Eclipse Milo Example Server") + .setOrganization("digitalpetri") + .setOrganizationalUnit("dev") + .setLocalityName("Folsom") + .setStateName("CA") + .setCountryCode("US") + .setApplicationUri(applicationUri) + .addDnsName("localhost") + .addIpAddress("127.0.0.1"); + + // Get as many hostnames and IP addresses as we can listed in the certificate. + for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) { + if (IP_ADDR_PATTERN.matcher(hostname).matches()) { + builder.addIpAddress(hostname); + } else { + builder.addDnsName(hostname); + } + } + + X509Certificate certificate = builder.build(); + + keyStore.setKeyEntry(SERVER_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate}); + keyStore.store(new FileOutputStream(serverKeyStore), PASSWORD); + } else { + keyStore.load(new FileInputStream(serverKeyStore), PASSWORD); + } + + Key serverPrivateKey = keyStore.getKey(SERVER_ALIAS, PASSWORD); + if (serverPrivateKey instanceof PrivateKey) { + serverCertificate = (X509Certificate) keyStore.getCertificate(SERVER_ALIAS); + + serverCertificateChain = Arrays.stream(keyStore.getCertificateChain(SERVER_ALIAS)) + .map(X509Certificate.class::cast) + .toArray(X509Certificate[]::new); + + PublicKey serverPublicKey = serverCertificate.getPublicKey(); + serverKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey); + } + + return this; + } + + X509Certificate getServerCertificate() { + return serverCertificate; + } + + public X509Certificate[] getServerCertificateChain() { + return serverCertificateChain; + } + + KeyPair getServerKeyPair() { + return serverKeyPair; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/OpcUaHelper.java b/core-common/src/main/java/eu/arrowhead/common/opcua/OpcUaHelper.java new file mode 100644 index 00000000..a5c6e211 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/OpcUaHelper.java @@ -0,0 +1,159 @@ +package eu.arrowhead.common.opcua; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.database.OrchestrationStore; +import eu.arrowhead.common.database.ServiceRegistryEntry; +import eu.arrowhead.common.messages.IntraCloudAuthEntry; +import eu.arrowhead.common.messages.ServiceQueryForm; + +public class OpcUaHelper { + public ServiceRegistryEntry sreFromJsonString(String json) + throws JsonParseException, JsonMappingException, IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode actualObj = mapper.readTree(json); + List interfaces = mapper.readValue(actualObj.get("providedService").get("interfaces").toString(), + new TypeReference>() { + }); + Set interfaceSet = new HashSet(); + for (String s : interfaces) { + interfaceSet.add(s); + } + TypeReference> typeRef = new TypeReference>() { + }; + Map metadata = mapper + .readValue(actualObj.get("providedService").get("serviceMetadata").toString(), typeRef); + ArrowheadService ahService = new ArrowheadService( + actualObj.get("providedService").get("serviceDefinition").toString().replaceAll("\"", ""), interfaceSet, + metadata); + String systemName = actualObj.get("provider").get("systemName").toString().replaceAll("\"", ""); + String address = actualObj.get("provider").get("address").toString().replaceAll("\"", ""); + Integer port = actualObj.get("provider").get("port").asInt(); + String authenticationInfo = actualObj.get("provider").get("authenticationInfo").toString().replaceAll("\"", ""); + ArrowheadSystem ahSystem = new ArrowheadSystem(systemName, address, port, authenticationInfo); + + ServiceRegistryEntry sre = new ServiceRegistryEntry(ahService, ahSystem, + actualObj.get("serviceURI").toString().replaceAll("\"", "")); + return sre; + } + + public ServiceQueryForm sqfFromJsonString(String json) + throws JsonParseException, JsonMappingException, IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode actualObj = mapper.readTree(json); + List interfaces = mapper.readValue(actualObj.get("service").get("interfaces").toString(), + new TypeReference>() { + }); + Set interfaceSet = new HashSet(); + for (String s : interfaces) { + interfaceSet.add(s); + } + TypeReference> typeRef = new TypeReference>() { + }; + Map metadata = mapper.readValue(actualObj.get("service").get("serviceMetadata").toString(), + typeRef); + ArrowheadService ahService = new ArrowheadService( + actualObj.get("service").get("serviceDefinition").toString().replaceAll("\"", ""), interfaceSet, + metadata); + Boolean metadataSearch = actualObj.get("metadataSearch").asBoolean(); + Boolean pingProviders = actualObj.get("pingProviders").asBoolean(); + + ServiceQueryForm sqf = new ServiceQueryForm(ahService, metadataSearch, pingProviders); + return sqf; + } + + public List orchestrationStoreListFromJsonString(String json) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode actualObj = mapper.readTree(json); + + List orchestrationStoresList = new ArrayList(); + for (JsonNode node : actualObj) { + String serviceDefinition = node.get("service").get("serviceDefinition").toString().replaceAll("\"", ""); + List interfaces = mapper.readValue(node.get("service").get("interfaces").toString(), + new TypeReference>() { + }); + Set interfaceSet = new HashSet(); + for (String s : interfaces) { + interfaceSet.add(s); + } + TypeReference> typeRef = new TypeReference>() { + }; + Map metadata = mapper.readValue(node.get("service").get("serviceMetadata").toString(), + typeRef); + ArrowheadService service = new ArrowheadService(serviceDefinition, interfaceSet, metadata); + + String consumerSystemName = node.get("consumer").get("systemName").toString().replaceAll("\"", ""); + String consumerAddress = node.get("consumer").get("address").toString().replaceAll("\"", ""); + Integer consumerPort = node.get("consumer").get("port").asInt(); + String consumerAuthenticationInfo = node.get("consumer").get("authenticationInfo") == null ? "" + : node.get("consumer").get("authenticationInfo").toString().replaceAll("\"", ""); + ArrowheadSystem consumer = new ArrowheadSystem(consumerSystemName, consumerAddress, consumerPort, + consumerAuthenticationInfo); + + String providerSystemName = node.get("providerSystem").get("systemName").toString().replaceAll("\"", ""); + String providerAddress = node.get("providerSystem").get("address").toString().replaceAll("\"", ""); + Integer providerPort = node.get("providerSystem").get("port").asInt(); + String providerAuthenticationInfo = node.get("providerSystem").get("authenticationInfo") == null ? "" + : node.get("providerSystem").get("authenticationInfo").toString().replaceAll("\"", ""); + ArrowheadSystem providerSystem = new ArrowheadSystem(providerSystemName, providerAddress, providerPort, + providerAuthenticationInfo); + + int priority = node.get("priority").asInt(); + + boolean defaultEntry = node.get("defaultEntry").asBoolean(); + + String name = node.get("name").textValue(); + + OrchestrationStore os = new OrchestrationStore(service, consumer, providerSystem, null, priority, + defaultEntry, name, null, null, null, null); + orchestrationStoresList.add(os); + } + return orchestrationStoresList; + } + + public IntraCloudAuthEntry icaeFromJsonString(String json) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode actualObj = mapper.readTree(json); + + String systemName = actualObj.get("consumer").get("systemName").toString().replaceAll("\"", ""); + String address = actualObj.get("consumer").get("address").toString().replaceAll("\"", ""); + Integer port = actualObj.get("consumer").get("port").asInt(); + String authenticationInfo = actualObj.get("consumer").get("authenticationInfo") == null ? "" + : actualObj.get("consumer").get("authenticationInfo").toString().replaceAll("\"", ""); + ArrowheadSystem consumer = new ArrowheadSystem(systemName, address, port, authenticationInfo); + + List providersList = new ArrayList(); + for (JsonNode node : actualObj.get("providerList")) { + systemName = node.get("systemName").toString().replaceAll("\"", ""); + address = node.get("address").toString().replaceAll("\"", ""); + port = node.get("port").asInt(); + authenticationInfo = node.get("authenticationInfo") == null ? "" + : node.get("authenticationInfo").toString().replaceAll("\"", ""); + providersList.add(new ArrowheadSystem(systemName, address, port, authenticationInfo)); + } + + List servicesList = new ArrayList(); + for (JsonNode node : actualObj.get("serviceList")) { + String serviceDefinition = node.get("serviceDefinition").toString().replaceAll("\"", ""); + servicesList.add(new ArrowheadService(serviceDefinition, null, null)); + } + IntraCloudAuthEntry icae = new IntraCloudAuthEntry(consumer, providersList, servicesList); + return icae; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/PemFile.java b/core-common/src/main/java/eu/arrowhead/common/opcua/PemFile.java new file mode 100644 index 00000000..890e1b64 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/PemFile.java @@ -0,0 +1,27 @@ +package eu.arrowhead.common.opcua; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; + +public class PemFile { + + private PemObject pemObject; + + public PemFile(String filename) throws FileNotFoundException, IOException { + PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream(filename))); + try { + this.pemObject = pemReader.readPemObject(); + } finally { + pemReader.close(); + } + } + + public PemObject getPemObject() { + return pemObject; + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/opcua/datatypes/ServiceRegistryEntry.java b/core-common/src/main/java/eu/arrowhead/common/opcua/datatypes/ServiceRegistryEntry.java new file mode 100644 index 00000000..c71274fd --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/opcua/datatypes/ServiceRegistryEntry.java @@ -0,0 +1,35 @@ +package eu.arrowhead.common.opcua.datatypes; + +import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; + +import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger; + +public class ServiceRegistryEntry { + + private final String foo; + private final UInteger bar; + private final boolean baz; + + public ServiceRegistryEntry() { + this(null, uint(0), false); + } + + public ServiceRegistryEntry(String foo, UInteger bar, boolean baz) { + this.foo = foo; + this.bar = bar; + this.baz = baz; + } + + public String getFoo() { + return foo; + } + + public UInteger getBar() { + return bar; + } + + public boolean isBaz() { + return baz; + } + +} \ No newline at end of file diff --git a/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadCloudApi.java b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadCloudApi.java new file mode 100644 index 00000000..c55d3cba --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadCloudApi.java @@ -0,0 +1,137 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.web; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadCloud; +import eu.arrowhead.common.exception.DataNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; + +@Path("mgmt/clouds") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ArrowheadCloudApi { + + private final HashMap restrictionMap = new HashMap<>(); + private static final Logger log = Logger.getLogger(ArrowheadCloudApi.class.getName()); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + + @GET + public List getClouds(@QueryParam("operator") String operator, @QueryParam("cloudName") String cloudName) { + if (operator != null) { + restrictionMap.put("operator", operator); + } + if (cloudName != null) { + restrictionMap.put("cloudName", cloudName); + } + + List cloudList = dm.getAll(ArrowheadCloud.class, restrictionMap); + if (cloudList.isEmpty()) { + log.info("CommonApi:getAllClouds throws DataNotFoundException"); + throw new DataNotFoundException("ArrowheadClouds not found in the database."); + } + + return cloudList; + } + + @GET + @Path("{cloudId}") + public ArrowheadCloud getCloud(@PathParam("cloudId") long cloudId) { + return dm.get(ArrowheadCloud.class, cloudId).orElseThrow(() -> new DataNotFoundException("ArrowheadCloud entry not found with id: " + cloudId)); + } + + /** + * Adds a list of ArrowheadClouds to the database. Elements which would cause DuplicateEntryException are being skipped. The returned list only + * contains the elements which were saved in the process. + * + * @return List + */ + @POST + public Response addClouds(@Valid List cloudList) { + + List savedClouds = new ArrayList<>(); + for (ArrowheadCloud cloud : cloudList) { + restrictionMap.clear(); + restrictionMap.put("operator", cloud.getOperator()); + restrictionMap.put("cloudName", cloud.getCloudName()); + ArrowheadCloud retrievedCloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (retrievedCloud == null) { + dm.save(cloud); + savedClouds.add(cloud); + } + } + + if (savedClouds.isEmpty()) { + return Response.status(Status.NO_CONTENT).build(); + } else { + return Response.status(Status.CREATED).entity(savedClouds).build(); + } + } + + /** + * Updates an existing ArrowheadCloud in the database. Returns 204 (no content) if the specified ArrowheadCloud was not in the database. + */ + @PUT + public Response updateCloud(@Valid ArrowheadCloud cloud) { + restrictionMap.put("operator", cloud.getOperator()); + restrictionMap.put("cloudName", cloud.getCloudName()); + + ArrowheadCloud retrievedCloud = dm.get(ArrowheadCloud.class, restrictionMap); + if (retrievedCloud != null) { + retrievedCloud.setAddress(cloud.getAddress()); + retrievedCloud.setPort(cloud.getPort()); + retrievedCloud.setGatekeeperServiceURI(cloud.getGatekeeperServiceURI()); + retrievedCloud.setAuthenticationInfo(cloud.getAuthenticationInfo()); + retrievedCloud.setSecure(cloud.isSecure()); + retrievedCloud = dm.merge(retrievedCloud); + return Response.status(Status.ACCEPTED).entity(retrievedCloud).build(); + } else { + return Response.noContent().build(); + } + } + + @PUT + @Path("{cloudId}") + public Response updateCloud(@PathParam("cloudId") long cloudId, @Valid ArrowheadCloud cloud) { + ArrowheadCloud retrievedCloud = dm.get(ArrowheadCloud.class, cloudId) + .orElseThrow(() -> new DataNotFoundException("ArrowheadCloud entry not found with id: " + cloudId)); + retrievedCloud.partialUpdate(cloud); + retrievedCloud = dm.merge(retrievedCloud); + return Response.status(Status.ACCEPTED).entity(retrievedCloud).build(); + } + + @DELETE + @Path("{cloudId}") + public Response deleteCloud(@PathParam("cloudId") long cloudId) { + return dm.get(ArrowheadCloud.class, cloudId).map(cloud -> { + dm.delete(cloud); + log.info("deleteCloud successfully returns."); + return Response.ok().build(); + }).orElseThrow(() -> { + log.info("deleteCloud had no effect."); + throw new DataNotFoundException("ArrowheadCloud entry not found with id: " + cloudId); + }); + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadDeviceApi.java b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadDeviceApi.java new file mode 100644 index 00000000..6746441e --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadDeviceApi.java @@ -0,0 +1,42 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.web; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadDevice; +import eu.arrowhead.common.exception.DataNotFoundException; +import java.util.HashMap; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.log4j.Logger; + +@Path("mgmt/devices") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ArrowheadDeviceApi { + + private final HashMap restrictionMap = new HashMap<>(); + private static final Logger log = Logger.getLogger(ArrowheadDeviceApi.class.getName()); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + + @GET + public List getDevices() { + List deviceList = dm.getAll(ArrowheadDevice.class, restrictionMap); + if (deviceList.isEmpty()) { + log.info("getDevices throws DataNotFoundException"); + throw new DataNotFoundException("ArrowheadDevices not found in the database."); + } + + return deviceList; + } + +} diff --git a/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadServiceApi.java b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadServiceApi.java new file mode 100644 index 00000000..6e0cd2b3 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadServiceApi.java @@ -0,0 +1,120 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.web; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadService; +import eu.arrowhead.common.exception.DataNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; + +@Path("mgmt/services") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ArrowheadServiceApi { + + private final HashMap restrictionMap = new HashMap<>(); + private static final Logger log = Logger.getLogger(ArrowheadService.class.getName()); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + + @GET + public List getServices(@QueryParam("definition") String definition) { + if (definition != null) { + restrictionMap.put("serviceDefinition", definition); + } + + List serviceList = dm.getAll(ArrowheadService.class, restrictionMap); + if (serviceList.isEmpty()) { + log.info("getServices throws DataNotFoundException"); + throw new DataNotFoundException("ArrowheadServices not found in the database."); + } + + return serviceList; + } + + @GET + @Path("{serviceId}") + public ArrowheadService getService(@PathParam("serviceId") long serviceId) { + return dm.get(ArrowheadService.class, serviceId) + .orElseThrow(() -> new DataNotFoundException("ArrowheadService entry not found with id: " + serviceId)); + } + + @POST + public Response addServices(@Valid List serviceList) { + + List savedServices = new ArrayList<>(); + for (ArrowheadService service : serviceList) { + restrictionMap.clear(); + restrictionMap.put("serviceDefinition", service.getServiceDefinition()); + ArrowheadService retrievedService = dm.get(ArrowheadService.class, restrictionMap); + if (retrievedService == null) { + dm.save(service); + savedServices.add(service); + } + } + + if (savedServices.isEmpty()) { + return Response.status(Status.NO_CONTENT).build(); + } else { + return Response.status(Status.CREATED).entity(savedServices).build(); + } + } + + @PUT + public Response updateService(@Valid ArrowheadService service) { + restrictionMap.put("serviceDefinition", service.getServiceDefinition()); + + ArrowheadService retrievedService = dm.get(ArrowheadService.class, restrictionMap); + if (retrievedService != null) { + retrievedService.setInterfaces(service.getInterfaces()); + retrievedService.setServiceMetadata(service.getServiceMetadata()); + retrievedService = dm.merge(retrievedService); + return Response.status(Status.ACCEPTED).entity(retrievedService).build(); + } else { + return Response.noContent().build(); + } + } + + @PUT + @Path("{serviceId}") + public Response updateService(@PathParam("serviceId") long serviceId, @Valid ArrowheadService service) { + ArrowheadService retrievedService = dm.get(ArrowheadService.class, serviceId) + .orElseThrow(() -> new DataNotFoundException("ArrowheadService entry not found with id: " + serviceId)); + retrievedService.partialUpdate(service); + retrievedService = dm.merge(retrievedService); + return Response.status(Status.ACCEPTED).entity(retrievedService).build(); + } + + @DELETE + @Path("{serviceId}") + public Response deleteService(@PathParam("serviceId") long serviceId) { + return dm.get(ArrowheadService.class, serviceId).map(service -> { + dm.delete(service); + log.info("deleteService successfully returns."); + return Response.ok().build(); + }).orElseThrow(() -> { + log.info("deleteService had no effect."); + throw new DataNotFoundException("ArrowheadService entry not found with id: " + serviceId); + }); + } +} diff --git a/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadSystemApi.java b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadSystemApi.java new file mode 100644 index 00000000..d55dc133 --- /dev/null +++ b/core-common/src/main/java/eu/arrowhead/common/web/ArrowheadSystemApi.java @@ -0,0 +1,122 @@ +/* + * This work is part of the Productive 4.0 innovation project, which receives grants from the + * European Commissions H2020 research and innovation programme, ECSEL Joint Undertaking + * (project no. 737459), the free state of Saxony, the German Federal Ministry of Education and + * national funding authorities from involved countries. + */ + +package eu.arrowhead.common.web; + +import eu.arrowhead.common.DatabaseManager; +import eu.arrowhead.common.database.ArrowheadSystem; +import eu.arrowhead.common.exception.DataNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import javax.validation.Valid; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import org.apache.log4j.Logger; + +@Path("mgmt/systems") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ArrowheadSystemApi { + + private final HashMap restrictionMap = new HashMap<>(); + private static final Logger log = Logger.getLogger(ArrowheadSystemApi.class.getName()); + private static final DatabaseManager dm = DatabaseManager.getInstance(); + + @GET + public List getSystems(@QueryParam("systemName") String systemName) { + if (systemName != null) { + restrictionMap.put("systemName", systemName); + } + + List systemList = dm.getAll(ArrowheadSystem.class, restrictionMap); + if (systemList.isEmpty()) { + log.info("getSystems throws DataNotFoundException"); + throw new DataNotFoundException("ArrowheadSystems not found in the database."); + } + + return systemList; + } + + @GET + @Path("{systemId}") + public ArrowheadSystem getSystem(@PathParam("systemId") long systemId) { + return dm.get(ArrowheadSystem.class, systemId) + .orElseThrow(() -> new DataNotFoundException("ArrowheadSystem entry not found with id: " + systemId)); + } + + @POST + public Response addSystems(@Valid List systemList) { + + List savedSystems = new ArrayList<>(); + for (ArrowheadSystem system : systemList) { + restrictionMap.clear(); + restrictionMap.put("systemName", system.getSystemName()); + ArrowheadSystem retrievedSystem = dm.get(ArrowheadSystem.class, restrictionMap); + if (retrievedSystem == null) { + dm.save(system); + savedSystems.add(system); + } + } + + if (savedSystems.isEmpty()) { + return Response.status(Status.NO_CONTENT).build(); + } else { + return Response.status(Status.CREATED).entity(savedSystems).build(); + } + } + + @PUT + public Response updateSystem(@Valid ArrowheadSystem system) { + restrictionMap.put("systemName", system.getSystemName()); + + ArrowheadSystem retrievedSystem = dm.get(ArrowheadSystem.class, restrictionMap); + if (retrievedSystem != null) { + retrievedSystem.setAddress(system.getAddress()); + retrievedSystem.setPort(system.getPort()); + retrievedSystem.setAuthenticationInfo(system.getAuthenticationInfo()); + retrievedSystem = dm.merge(retrievedSystem); + return Response.status(Status.ACCEPTED).entity(retrievedSystem).build(); + } else { + return Response.noContent().build(); + } + } + + @PUT + @Path("{systemId}") + public Response updateSystem(@PathParam("systemId") long systemId, @Valid ArrowheadSystem system) { + ArrowheadSystem retrievedSystem = dm.get(ArrowheadSystem.class, systemId) + .orElseThrow(() -> new DataNotFoundException("ArrowheadSystem entry not found with id: " + systemId)); + retrievedSystem.partialUpdate(system); + retrievedSystem = dm.merge(retrievedSystem); + return Response.status(Status.ACCEPTED).entity(retrievedSystem).build(); + } + + @DELETE + @Path("{systemId}") + public Response deleteSystem(@PathParam("systemId") long systemId) { + return dm.get(ArrowheadSystem.class, systemId).map(system -> { + dm.delete(system); + log.info("deleteSystem successfully returns."); + return Response.ok().build(); + }).orElseThrow(() -> { + log.info("deleteSystem had no effect."); + throw new DataNotFoundException("ArrowheadSystem entry not found with id: " + systemId); + }); + } + +} diff --git a/core-common/src/main/resources/swagger/favicon-16x16.png b/core-common/src/main/resources/swagger/favicon-16x16.png new file mode 100644 index 00000000..0f7e13b0 Binary files /dev/null and b/core-common/src/main/resources/swagger/favicon-16x16.png differ diff --git a/core-common/src/main/resources/swagger/favicon-32x32.png b/core-common/src/main/resources/swagger/favicon-32x32.png new file mode 100644 index 00000000..b0a3352f Binary files /dev/null and b/core-common/src/main/resources/swagger/favicon-32x32.png differ diff --git a/core-common/src/main/resources/swagger/index.html b/core-common/src/main/resources/swagger/index.html new file mode 100644 index 00000000..68bedfb3 --- /dev/null +++ b/core-common/src/main/resources/swagger/index.html @@ -0,0 +1,56 @@ + + + + + + Swagger UI + + + + + + + +

+ + + + + + diff --git a/core-common/src/main/resources/swagger/oauth2-redirect.html b/core-common/src/main/resources/swagger/oauth2-redirect.html new file mode 100644 index 00000000..38b5d3ad --- /dev/null +++ b/core-common/src/main/resources/swagger/oauth2-redirect.html @@ -0,0 +1,70 @@ + + + + + + diff --git a/core-common/src/main/resources/swagger/swagger-ui-bundle.js b/core-common/src/main/resources/swagger/swagger-ui-bundle.js new file mode 100644 index 00000000..967f8a5c --- /dev/null +++ b/core-common/src/main/resources/swagger/swagger-ui-bundle.js @@ -0,0 +1,93 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=446)}([function(e,t,n){"use strict";e.exports=n(74)},function(e,t,n){e.exports=n(853)()},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){"use strict";t.__esModule=!0;var r,o=n(262),i=(r=o)&&r.__esModule?r:{default:r};t.default=function(){function e(e,t){for(var n=0;n>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?C(e)+t:t}function A(){return!0}function O(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function P(e,t){return M(e,t,0)}function T(e,t){return M(e,t,t)}function M(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var I=0,j=1,N=2,R="function"==typeof Symbol&&Symbol.iterator,D="@@iterator",L=R||D;function U(e){this.next=e}function q(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function F(){return{value:void 0,done:!0}}function z(e){return!!H(e)}function B(e){return e&&"function"==typeof e.next}function V(e){var t=H(e);return t&&t.call(e)}function H(e){var t=e&&(R&&e[R]||e[D]);if("function"==typeof t)return t}function W(e){return e&&"number"==typeof e.length}function J(e){return null===e||void 0===e?ie():a(e)?e.toSeq():function(e){var t=se(e)||"object"==typeof e&&new te(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function Y(e){return null===e||void 0===e?ie().toKeyedSeq():a(e)?u(e)?e.toSeq():e.fromEntrySeq():ae(e)}function K(e){return null===e||void 0===e?ie():a(e)?u(e)?e.entrySeq():e.toIndexedSeq():ue(e)}function G(e){return(null===e||void 0===e?ie():a(e)?u(e)?e.entrySeq():e:ue(e)).toSetSeq()}U.prototype.toString=function(){return"[Iterator]"},U.KEYS=I,U.VALUES=j,U.ENTRIES=N,U.prototype.inspect=U.prototype.toSource=function(){return this.toString()},U.prototype[L]=function(){return this},t(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(e,t){return le(this,e,t,!0)},J.prototype.__iterator=function(e,t){return ce(this,e,t,!0)},t(Y,J),Y.prototype.toKeyedSeq=function(){return this},t(K,J),K.of=function(){return K(arguments)},K.prototype.toIndexedSeq=function(){return this},K.prototype.toString=function(){return this.__toString("Seq [","]")},K.prototype.__iterate=function(e,t){return le(this,e,t,!1)},K.prototype.__iterator=function(e,t){return ce(this,e,t,!1)},t(G,J),G.of=function(){return G(arguments)},G.prototype.toSetSeq=function(){return this},J.isSeq=oe,J.Keyed=Y,J.Set=G,J.Indexed=K;var $,Z,X,Q="@@__IMMUTABLE_SEQ__@@";function ee(e){this._array=e,this.size=e.length}function te(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function ne(e){this._iterable=e,this.size=e.length||e.size}function re(e){this._iterator=e,this._iteratorCache=[]}function oe(e){return!(!e||!e[Q])}function ie(){return $||($=new ee([]))}function ae(e){var t=Array.isArray(e)?new ee(e).fromEntrySeq():B(e)?new re(e).fromEntrySeq():z(e)?new ne(e).fromEntrySeq():"object"==typeof e?new te(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function ue(e){var t=se(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function se(e){return W(e)?new ee(e):B(e)?new re(e):z(e)?new ne(e):void 0}function le(e,t,n,r){var o=e._cache;if(o){for(var i=o.length-1,a=0;a<=i;a++){var u=o[n?i-a:a];if(!1===t(u[1],r?u[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function ce(e,t,n,r){var o=e._cache;if(o){var i=o.length-1,a=0;return new U(function(){var e=o[n?i-a:a];return a++>i?{value:void 0,done:!0}:q(t,r?e[0]:a-1,e[1])})}return e.__iteratorUncached(t,n)}function fe(e,t){return t?function e(t,n,r,o){if(Array.isArray(n))return t.call(o,r,K(n).map(function(r,o){return e(t,r,o,n)}));if(de(n))return t.call(o,r,Y(n).map(function(r,o){return e(t,r,o,n)}));return n}(t,e,"",{"":e}):pe(e)}function pe(e){return Array.isArray(e)?K(e).map(pe).toList():de(e)?Y(e).map(pe).toMap():e}function de(e){return e&&(e.constructor===Object||void 0===e.constructor)}function he(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ve(e,t){if(e===t)return!0;if(!a(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||u(e)!==u(t)||s(e)!==s(t)||c(e)!==c(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!l(e);if(c(e)){var r=e.entries();return t.every(function(e,t){var o=r.next().value;return o&&he(o[1],e)&&(n||he(o[0],t))})&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var i=e;e=t,t=i}var f=!0,p=t.__iterate(function(t,r){if(n?!e.has(t):o?!he(t,e.get(r,g)):!he(e.get(r,g),t))return f=!1,!1});return f&&e.size===p}function me(e,t){if(!(this instanceof me))return new me(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(Z)return Z;Z=this}}function ye(e,t){if(!e)throw new Error(t)}function ge(e,t,n){if(!(this instanceof ge))return new ge(e,t,n);if(ye(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?{value:void 0,done:!0}:q(e,o,n[t?r-o++:o++])})},t(te,Y),te.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},te.prototype.has=function(e){return this._object.hasOwnProperty(e)},te.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,i=0;i<=o;i++){var a=r[t?o-i:i];if(!1===e(n[a],a,this))return i+1}return i},te.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,i=0;return new U(function(){var a=r[t?o-i:i];return i++>o?{value:void 0,done:!0}:q(e,a,n[a])})},te.prototype[h]=!0,t(ne,K),ne.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=V(this._iterable),r=0;if(B(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},ne.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=V(this._iterable);if(!B(n))return new U(F);var r=0;return new U(function(){var t=n.next();return t.done?t:q(e,r++,t.value)})},t(re,K),re.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return q(e,o,r[o++])})},t(me,K),me.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},me.prototype.get=function(e,t){return this.has(e)?this._value:t},me.prototype.includes=function(e){return he(this._value,e)},me.prototype.slice=function(e,t){var n=this.size;return O(e,t,n)?this:new me(this._value,T(t,n)-P(e,n))},me.prototype.reverse=function(){return this},me.prototype.indexOf=function(e){return he(this._value,e)?0:-1},me.prototype.lastIndexOf=function(e){return he(this._value,e)?this.size:-1},me.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?{value:void 0,done:!0}:q(e,i++,a)})},ge.prototype.equals=function(e){return e instanceof ge?this._start===e._start&&this._end===e._end&&this._step===e._step:ve(this,e)},t(be,n),t(_e,be),t(we,be),t(Ee,be),be.Keyed=_e,be.Indexed=we,be.Set=Ee;var xe="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function Se(e){return e>>>1&1073741824|3221225471&e}function Ce(e){if(!1===e||null===e||void 0===e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null===e||void 0===e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return Se(n)}if("string"===t)return e.length>je?function(e){var t=De[e];void 0===t&&(t=ke(e),Re===Ne&&(Re=0,De={}),Re++,De[e]=t);return t}(e):ke(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function(e){var t;if(Te&&void 0!==(t=Pe.get(e)))return t;if(void 0!==(t=e[Ie]))return t;if(!Oe){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[Ie]))return t;if(void 0!==(t=function(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}t=++Me,1073741824&Me&&(Me=0);if(Te)Pe.set(e,t);else{if(void 0!==Ae&&!1===Ae(e))throw new Error("Non-extensible objects are not allowed as keys.");if(Oe)Object.defineProperty(e,Ie,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[Ie]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[Ie]=t}}return t}(e);if("function"==typeof e.toString)return ke(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ke(e){for(var t=0,n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}})},Ue.prototype.toString=function(){return this.__toString("Map {","}")},Ue.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Ue.prototype.set=function(e,t){return Qe(this,e,t)},Ue.prototype.setIn=function(e,t){return this.updateIn(e,g,function(){return t})},Ue.prototype.remove=function(e){return Qe(this,e,g)},Ue.prototype.deleteIn=function(e){return this.updateIn(e,function(){return g})},Ue.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Ue.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=function e(t,n,r,o){var i=t===g;var a=n.next();if(a.done){var u=i?r:t,s=o(u);return s===u?t:s}ye(i||t&&t.set,"invalid keyPath");var l=a.value;var c=i?g:t.get(l,g);var f=e(c,n,r,o);return f===c?t:f===g?t.remove(l):(i?Xe():t).set(l,f)}(this,nn(e),t,n);return r===g?void 0:r},Ue.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):Xe()},Ue.prototype.merge=function(){return rt(this,void 0,arguments)},Ue.prototype.mergeWith=function(t){return rt(this,t,e.call(arguments,1))},Ue.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]})},Ue.prototype.mergeDeep=function(){return rt(this,ot,arguments)},Ue.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return rt(this,it(t),n)},Ue.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]})},Ue.prototype.sort=function(e){return Pt(Wt(this,e))},Ue.prototype.sortBy=function(e,t){return Pt(Wt(this,t,e))},Ue.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Ue.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new x)},Ue.prototype.asImmutable=function(){return this.__ensureOwner()},Ue.prototype.wasAltered=function(){return this.__altered},Ue.prototype.__iterator=function(e,t){return new Ke(this,e,t)},Ue.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate(function(t){return r++,e(t[1],t[0],n)},t),r},Ue.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Ze(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Ue.isMap=qe;var Fe,ze="@@__IMMUTABLE_MAP__@@",Be=Ue.prototype;function Ve(e,t){this.ownerID=e,this.entries=t}function He(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function We(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Je(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Ye(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function Ke(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&$e(e._root)}function Ge(e,t){return q(e,t[0],t[1])}function $e(e,t){return{node:e,index:0,__prev:t}}function Ze(e,t,n,r){var o=Object.create(Be);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Xe(){return Fe||(Fe=Ze(0))}function Qe(e,t,n){var r,o;if(e._root){var i=w(b),a=w(_);if(r=et(e._root,e.__ownerID,0,void 0,t,n,i,a),!a.value)return e;o=e.size+(i.value?n===g?-1:1:0)}else{if(n===g)return e;o=1,r=new Ve(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?Ze(o,r):Xe()}function et(e,t,n,r,o,i,a,u){return e?e.update(t,n,r,o,i,a,u):i===g?e:(E(u),E(a),new Ye(t,r,[o,i]))}function tt(e){return e.constructor===Ye||e.constructor===Je}function nt(e,t,n,r,o){if(e.keyHash===r)return new Je(t,r,[e.entry,o]);var i,a=(0===n?e.keyHash:e.keyHash>>>n)&y,u=(0===n?r:r>>>n)&y;return new He(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function st(e,t,n,r){var o=r?e:S(e);return o[t]=n,o}Be[ze]=!0,Be.delete=Be.remove,Be.removeIn=Be.deleteIn,Ve.prototype.get=function(e,t,n,r){for(var o=this.entries,i=0,a=o.length;i=lt)return function(e,t,n,r){e||(e=new x);for(var o=new Ye(e,Ce(n),[n,r]),i=0;i>>e)&y),i=this.bitmap;return 0==(i&o)?r:this.nodes[ut(i&o-1)].get(e+v,t,n,r)},He.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var u=(0===t?n:n>>>t)&y,s=1<=ct)return function(e,t,n,r,o){for(var i=0,a=new Array(m),u=0;0!==n;u++,n>>>=1)a[u]=1&n?t[i++]:void 0;return a[r]=o,new We(e,i+1,a)}(e,p,l,u,h);if(c&&!h&&2===p.length&&tt(p[1^f]))return p[1^f];if(c&&h&&1===p.length&&tt(h))return h;var b=e&&e===this.ownerID,_=c?h?l:l^s:l|s,w=c?h?st(p,f,h,b):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;a>>e)&y,i=this.nodes[o];return i?i.get(e+v,t,n,r):r},We.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var u=(0===t?n:n>>>t)&y,s=o===g,l=this.nodes,c=l[u];if(s&&!c)return this;var f=et(c,e,t+v,n,r,o,i,a);if(f===c)return this;var p=this.count;if(c){if(!f&&--p0&&r=0&&e=e.size||t<0)return e.withMutations(function(e){t<0?kt(e,t).set(0,n):kt(e,0,t+1).set(t,n)});t+=e._origin;var r=e._tail,o=e._root,i=w(_);t>=Ot(e._capacity)?r=xt(r,e.__ownerID,0,t,n,i):o=xt(o,e.__ownerID,e._level,t,n,i);if(!i.value)return e;if(e.__ownerID)return e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e;return wt(e._origin,e._capacity,e._level,o,r)}(this,e,t)},pt.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},pt.prototype.insert=function(e,t){return this.splice(e,0,t)},pt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=v,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):Et()},pt.prototype.push=function(){var e=arguments,t=this.size;return this.withMutations(function(n){kt(n,0,t+e.length);for(var r=0;r>>t&y;if(r>=this.array.length)return new mt([],e);var o,i=0===r;if(t>0){var a=this.array[r];if((o=a&&a.removeBefore(e,t-v,n))===a&&i)return this}if(i&&!o)return this;var u=St(this,e);if(!i)for(var s=0;s>>t&y;if(o>=this.array.length)return this;if(t>0){var i=this.array[o];if((r=i&&i.removeAfter(e,t-v,n))===i&&o===this.array.length-1)return this}var a=St(this,e);return a.array.splice(o+1),r&&(a.array[o]=r),a};var yt,gt,bt={};function _t(e,t){var n=e._origin,r=e._capacity,o=Ot(r),i=e._tail;return a(e._root,e._level,0);function a(e,u,s){return 0===u?function(e,a){var u=a===o?i&&i.array:e&&e.array,s=a>n?0:n-a,l=r-a;l>m&&(l=m);return function(){if(s===l)return bt;var e=t?--l:s++;return u&&u[e]}}(e,s):function(e,o,i){var u,s=e&&e.array,l=i>n?0:n-i>>o,c=1+(r-i>>o);c>m&&(c=m);return function(){for(;;){if(u){var e=u();if(e!==bt)return e;u=null}if(l===c)return bt;var n=t?--c:l++;u=a(s&&s[n],o-v,i+(n<>>n&y,s=e&&u0){var l=e&&e.array[u],c=xt(l,t,n-v,r,o,i);return c===l?e:((a=St(e,t)).array[u]=c,a)}return s&&e.array[u]===o?e:(E(i),a=St(e,t),void 0===o&&u===a.array.length-1?a.array.pop():a.array[u]=o,a)}function St(e,t){return t&&e&&t===e.ownerID?e:new mt(e?e.array.slice():[],t)}function Ct(e,t){if(t>=Ot(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&y],r-=v;return n}}function kt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new x,o=e._origin,i=e._capacity,a=o+t,u=void 0===n?i:n<0?i+n:o+n;if(a===o&&u===i)return e;if(a>=u)return e.clear();for(var s=e._level,l=e._root,c=0;a+c<0;)l=new mt(l&&l.array.length?[void 0,l]:[],r),c+=1<<(s+=v);c&&(a+=c,o+=c,u+=c,i+=c);for(var f=Ot(i),p=Ot(u);p>=1<f?new mt([],r):d;if(d&&p>f&&av;g-=v){var b=f>>>g&y;m=m.array[b]=St(m.array[b],r)}m.array[f>>>v&y]=d}if(u=p)a-=p,u-=p,s=v,l=null,h=h&&h.removeBefore(r,0,a);else if(a>o||p>>s&y;if(_!==p>>>s&y)break;_&&(c+=(1<o&&(l=l.removeBefore(r,s,a-c)),l&&pi&&(i=l.size),a(s)||(l=l.map(function(e){return fe(e)})),r.push(l)}return i>e.size&&(e=e.setSize(i)),at(e,t,r)}function Ot(e){return e>>v<=m&&a.size>=2*i.size?(r=(o=a.filter(function(e,t){return void 0!==e&&u!==t})).toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=i.remove(t),o=u===a.size-1?a.pop():a.set(u,void 0))}else if(s){if(n===a.get(u)[1])return e;r=i,o=a.set(u,[t,n])}else r=i.set(t,a.size),o=a.set(a.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Mt(r,o)}function Nt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function Rt(e){this._iter=e,this.size=e.size}function Dt(e){this._iter=e,this.size=e.size}function Lt(e){this._iter=e,this.size=e.size}function Ut(e){var t=Qt(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=en,t.__iterateUncached=function(t,n){var r=this;return e.__iterate(function(e,n){return!1!==t(n,e,r)},n)},t.__iteratorUncached=function(t,n){if(t===N){var r=e.__iterator(t,n);return new U(function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e})}return e.__iterator(t===j?I:j,n)},t}function qt(e,t,n){var r=Qt(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var i=e.get(r,g);return i===g?o:t.call(n,i,r,e)},r.__iterateUncached=function(r,o){var i=this;return e.__iterate(function(e,o,a){return!1!==r(t.call(n,e,o,a),o,i)},o)},r.__iteratorUncached=function(r,o){var i=e.__iterator(N,o);return new U(function(){var o=i.next();if(o.done)return o;var a=o.value,u=a[0];return q(r,u,t.call(n,a[1],u,e),o)})},r}function Ft(e,t){var n=Qt(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Ut(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=en,n.__iterate=function(t,n){var r=this;return e.__iterate(function(e,n){return t(e,n,r)},!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function zt(e,t,n,r){var o=Qt(e);return r&&(o.has=function(r){var o=e.get(r,g);return o!==g&&!!t.call(n,o,r,e)},o.get=function(r,o){var i=e.get(r,g);return i!==g&&t.call(n,i,r,e)?i:o}),o.__iterateUncached=function(o,i){var a=this,u=0;return e.__iterate(function(e,i,s){if(t.call(n,e,i,s))return u++,o(e,r?i:u-1,a)},i),u},o.__iteratorUncached=function(o,i){var a=e.__iterator(N,i),u=0;return new U(function(){for(;;){var i=a.next();if(i.done)return i;var s=i.value,l=s[0],c=s[1];if(t.call(n,c,l,e))return q(o,r?l:u++,c,i)}})},o}function Bt(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),O(t,n,o))return e;var i=P(t,o),a=T(n,o);if(i!=i||a!=a)return Bt(e.toSeq().cacheResult(),t,n,r);var u,s=a-i;s==s&&(u=s<0?0:s);var l=Qt(e);return l.size=0===u?u:e.size&&u||void 0,!r&&oe(e)&&u>=0&&(l.get=function(t,n){return(t=k(this,t))>=0&&tu)return{value:void 0,done:!0};var e=o.next();return r||t===j?e:q(t,s-1,t===I?void 0:e.value[1],e)})},l}function Vt(e,t,n,r){var o=Qt(e);return o.__iterateUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterate(o,i);var u=!0,s=0;return e.__iterate(function(e,i,l){if(!u||!(u=t.call(n,e,i,l)))return s++,o(e,r?i:s-1,a)}),s},o.__iteratorUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterator(o,i);var u=e.__iterator(N,i),s=!0,l=0;return new U(function(){var e,i,c;do{if((e=u.next()).done)return r||o===j?e:q(o,l++,o===I?void 0:e.value[1],e);var f=e.value;i=f[0],c=f[1],s&&(s=t.call(n,c,i,a))}while(s);return o===N?e:q(o,i,c,e)})},o}function Ht(e,t,n){var r=Qt(e);return r.__iterateUncached=function(r,o){var i=0,u=!1;return function e(s,l){var c=this;s.__iterate(function(o,s){return(!t||l0}function Kt(e,t,r){var o=Qt(e);return o.size=new ee(r).map(function(e){return e.size}).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(j,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var i=r.map(function(e){return e=n(e),V(o?e.reverse():e)}),a=0,u=!1;return new U(function(){var n;return u||(n=i.map(function(e){return e.next()}),u=n.some(function(e){return e.done})),u?{value:void 0,done:!0}:q(e,a++,t.apply(null,n.map(function(e){return e.value})))})},o}function Gt(e,t){return oe(e)?t:e.constructor(t)}function $t(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function Zt(e){return Le(e.size),C(e)}function Xt(e){return u(e)?r:s(e)?o:i}function Qt(e){return Object.create((u(e)?Y:s(e)?K:G).prototype)}function en(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):J.prototype.cacheResult.call(this)}function tn(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):An(e,t)},En.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;Le(e.size);var t=this.size,n=this._head;return e.reverse().forEach(function(e){t++,n={value:e,next:n}}),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):An(t,n)},En.prototype.pop=function(){return this.slice(1)},En.prototype.unshift=function(){return this.push.apply(this,arguments)},En.prototype.unshiftAll=function(e){return this.pushAll(e)},En.prototype.shift=function(){return this.pop.apply(this,arguments)},En.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):On()},En.prototype.slice=function(e,t){if(O(e,t,this.size))return this;var n=P(e,this.size);if(T(t,this.size)!==this.size)return we.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):An(r,o)},En.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?An(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},En.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},En.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new U(function(){if(r){var t=r.value;return r=r.next,q(e,n++,t)}return{value:void 0,done:!0}})},En.isStack=xn;var Sn,Cn="@@__IMMUTABLE_STACK__@@",kn=En.prototype;function An(e,t,n,r){var o=Object.create(kn);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function On(){return Sn||(Sn=An(0))}function Pn(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}kn[Cn]=!0,kn.withMutations=Be.withMutations,kn.asMutable=Be.asMutable,kn.asImmutable=Be.asImmutable,kn.wasAltered=Be.wasAltered,n.Iterator=U,Pn(n,{toArray:function(){Le(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate(function(t,n){e[n]=t}),e},toIndexedSeq:function(){return new Rt(this)},toJS:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJS?e.toJS():e}).__toJS()},toJSON:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e}).__toJS()},toKeyedSeq:function(){return new Nt(this,!0)},toMap:function(){return Ue(this.toKeyedSeq())},toObject:function(){Le(this.size);var e={};return this.__iterate(function(t,n){e[n]=t}),e},toOrderedMap:function(){return Pt(this.toKeyedSeq())},toOrderedSet:function(){return mn(u(this)?this.valueSeq():this)},toSet:function(){return sn(u(this)?this.valueSeq():this)},toSetSeq:function(){return new Dt(this)},toSeq:function(){return s(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return En(u(this)?this.valueSeq():this)},toList:function(){return pt(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return Gt(this,function(e,t){var n=u(e),o=[e].concat(t).map(function(e){return a(e)?n&&(e=r(e)):e=n?ae(e):ue(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===o.length)return e;if(1===o.length){var i=o[0];if(i===e||n&&u(i)||s(e)&&s(i))return i}var l=new ee(o);return n?l=l.toKeyedSeq():s(e)||(l=l.toSetSeq()),(l=l.flatten(!0)).size=o.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),l}(this,e.call(arguments,0)))},includes:function(e){return this.some(function(t){return he(t,e)})},entries:function(){return this.__iterator(N)},every:function(e,t){Le(this.size);var n=!0;return this.__iterate(function(r,o,i){if(!e.call(t,r,o,i))return n=!1,!1}),n},filter:function(e,t){return Gt(this,zt(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return Le(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){Le(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate(function(r){n?n=!1:t+=e,t+=null!==r&&void 0!==r?r.toString():""}),t},keys:function(){return this.__iterator(I)},map:function(e,t){return Gt(this,qt(this,e,t))},reduce:function(e,t,n){var r,o;return Le(this.size),arguments.length<2?o=!0:r=t,this.__iterate(function(t,i,a){o?(o=!1,r=t):r=e.call(n,r,t,i,a)}),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Gt(this,Ft(this,!0))},slice:function(e,t){return Gt(this,Bt(this,e,t,!0))},some:function(e,t){return!this.every(Nn(e),t)},sort:function(e){return Gt(this,Wt(this,e))},values:function(){return this.__iterator(j)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(e,t){return C(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return function(e,t,n){var r=Ue().asMutable();return e.__iterate(function(o,i){r.update(t.call(n,o,i,e),0,function(e){return e+1})}),r.asImmutable()}(this,e,t)},equals:function(e){return ve(this,e)},entrySeq:function(){var e=this;if(e._cache)return new ee(e._cache);var t=e.toSeq().map(jn).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(Nn(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate(function(n,o,i){if(e.call(t,n,o,i))return r=[o,n],!1}),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(A)},flatMap:function(e,t){return Gt(this,function(e,t,n){var r=Xt(e);return e.toSeq().map(function(o,i){return r(t.call(n,o,i,e))}).flatten(!0)}(this,e,t))},flatten:function(e){return Gt(this,Ht(this,e,!0))},fromEntrySeq:function(){return new Lt(this)},get:function(e,t){return this.find(function(t,n){return he(n,e)},void 0,t)},getIn:function(e,t){for(var n,r=this,o=nn(e);!(n=o.next()).done;){var i=n.value;if((r=r&&r.get?r.get(i,g):g)===g)return t}return r},groupBy:function(e,t){return function(e,t,n){var r=u(e),o=(c(e)?Pt():Ue()).asMutable();e.__iterate(function(i,a){o.update(t.call(n,i,a,e),function(e){return(e=e||[]).push(r?[a,i]:i),e})});var i=Xt(e);return o.map(function(t){return Gt(e,i(t))})}(this,e,t)},has:function(e){return this.get(e,g)!==g},hasIn:function(e){return this.getIn(e,g)!==g},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every(function(t){return e.includes(t)})},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey(function(t){return he(t,e)})},keySeq:function(){return this.toSeq().map(In).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return Jt(this,e)},maxBy:function(e,t){return Jt(this,t,e)},min:function(e){return Jt(this,e?Rn(e):Un)},minBy:function(e,t){return Jt(this,t?Rn(t):Un,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return Gt(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return Gt(this,Vt(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(Nn(e),t)},sortBy:function(e,t){return Gt(this,Wt(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return Gt(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return Gt(this,function(e,t,n){var r=Qt(e);return r.__iterateUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterate(r,o);var a=0;return e.__iterate(function(e,o,u){return t.call(n,e,o,u)&&++a&&r(e,o,i)}),a},r.__iteratorUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterator(r,o);var a=e.__iterator(N,o),u=!0;return new U(function(){if(!u)return{value:void 0,done:!0};var e=a.next();if(e.done)return e;var o=e.value,s=o[0],l=o[1];return t.call(n,l,s,i)?r===N?e:q(r,s,l,e):(u=!1,{value:void 0,done:!0})})},r}(this,e,t))},takeUntil:function(e,t){return this.takeWhile(Nn(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=function(e){if(e.size===1/0)return 0;var t=c(e),n=u(e),r=t?1:0;return function(e,t){return t=xe(t,3432918353),t=xe(t<<15|t>>>-15,461845907),t=xe(t<<13|t>>>-13,5),t=xe((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=Se((t=xe(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(n?t?function(e,t){r=31*r+qn(Ce(e),Ce(t))|0}:function(e,t){r=r+qn(Ce(e),Ce(t))|0}:t?function(e){r=31*r+Ce(e)|0}:function(e){r=r+Ce(e)|0}),r)}(this))}});var Tn=n.prototype;Tn[f]=!0,Tn[L]=Tn.values,Tn.__toJS=Tn.toArray,Tn.__toStringMapper=Dn,Tn.inspect=Tn.toSource=function(){return this.toString()},Tn.chain=Tn.flatMap,Tn.contains=Tn.includes,Pn(r,{flip:function(){return Gt(this,Ut(this))},mapEntries:function(e,t){var n=this,r=0;return Gt(this,this.toSeq().map(function(o,i){return e.call(t,[i,o],r++,n)}).fromEntrySeq())},mapKeys:function(e,t){var n=this;return Gt(this,this.toSeq().flip().map(function(r,o){return e.call(t,r,o,n)}).flip())}});var Mn=r.prototype;function In(e,t){return t}function jn(e,t){return[t,e]}function Nn(e){return function(){return!e.apply(this,arguments)}}function Rn(e){return function(){return-e.apply(this,arguments)}}function Dn(e){return"string"==typeof e?JSON.stringify(e):String(e)}function Ln(){return S(arguments)}function Un(e,t){return et?-1:0}function qn(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Mn[p]=!0,Mn[L]=Tn.entries,Mn.__toJS=Tn.toObject,Mn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+Dn(e)},Pn(o,{toKeyedSeq:function(){return new Nt(this,!1)},filter:function(e,t){return Gt(this,zt(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return Gt(this,Ft(this,!1))},slice:function(e,t){return Gt(this,Bt(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=P(e,e<0?this.count():this.size);var r=this.slice(0,e);return Gt(this,1===n?r:r.concat(S(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return Gt(this,Ht(this,e,!1))},get:function(e,t){return(e=k(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find(function(t,n){return n===e},void 0,t)},has:function(e){return(e=k(this,e))>=0&&(void 0!==this.size?this.size===1/0||e>",i={listOf:function(e){return l(e,"List",r.List.isList)},mapOf:function(e,t){return c(e,t,"Map",r.Map.isMap)},orderedMapOf:function(e,t){return c(e,t,"OrderedMap",r.OrderedMap.isOrderedMap)},setOf:function(e){return l(e,"Set",r.Set.isSet)},orderedSetOf:function(e){return l(e,"OrderedSet",r.OrderedSet.isOrderedSet)},stackOf:function(e){return l(e,"Stack",r.Stack.isStack)},iterableOf:function(e){return l(e,"Iterable",r.Iterable.isIterable)},recordOf:function(e){return u(function(t,n,o,i,u){for(var s=arguments.length,l=Array(s>5?s-5:0),c=5;c6?s-6:0),c=6;c5?l-5:0),f=5;f5?i-5:0),u=5;u key("+c[f]+")"].concat(a));if(d instanceof Error)return d}})).apply(void 0,i);var s})}function f(e){var t=void 0===arguments[1]?"Iterable":arguments[1],n=void 0===arguments[2]?r.Iterable.isIterable:arguments[2];return u(function(r,o,i,u,s){for(var l=arguments.length,c=Array(l>5?l-5:0),f=5;f5e3)return e.textContent;return function(e){for(var n,r,o,i,a,u=e.textContent,s=0,l=u[0],c=1,f=e.innerHTML="",p=0;r=n,n=p<7&&"\\"==n?1:c;){if(c=l,l=u[++s],i=f.length>1,!c||p>8&&"\n"==c||[/\S/.test(c),1,1,!/[$\w]/.test(c),("/"==n||"\n"==n)&&i,'"'==n&&i,"'"==n&&i,u[s-4]+r+n=="--\x3e",r+n=="*/"][p])for(f&&(e.appendChild(a=t.createElement("span")).setAttribute("style",["color: #555; font-weight: bold;","","","color: #555;",""][p?p<3?2:p>6?4:p>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(f):0]),a.appendChild(t.createTextNode(f))),o=p&&p<7?p:o,f="",p=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(c),/[\])]/.test(c),/[$\w]/.test(c),"/"==c&&o<2&&"<"!=n,'"'==c,"'"==c,c+l+u[s+1]+u[s+2]=="\x3c!--",c+l=="/*",c+l=="//","#"==c][--p];);f+=c}}(e)},t.mapToList=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"key";var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:l.default.Map();if(!l.default.Map.isMap(t)||!t.size)return l.default.List();Array.isArray(n)||(n=[n]);if(n.length<1)return t.merge(r);var a=l.default.List();var u=n[0];var s=!0;var c=!1;var f=void 0;try{for(var p,d=(0,i.default)(t.entries());!(s=(p=d.next()).done);s=!0){var h=p.value,v=(0,o.default)(h,2),m=v[0],y=v[1],g=e(y,n.slice(1),r.set(u,m));a=l.default.List.isList(g)?a.concat(g):a.push(g)}}catch(e){c=!0,f=e}finally{try{!s&&d.return&&d.return()}finally{if(c)throw f}}return a},t.extractFileNameFromContentDispositionHeader=function(e){var t=/filename="([^;]*);?"/i.exec(e);null===t&&(t=/filename=([^;]*);?/i.exec(e));if(null!==t&&t.length>1)return t[1];return null},t.pascalCase=C,t.pascalCaseFilename=function(e){return C(e.replace(/\.[^./]*$/,""))},t.sanitizeUrl=function(e){if("string"!=typeof e||""===e)return"";return(0,c.sanitizeUrl)(e)},t.getAcceptControllingResponse=function(e){if(!l.default.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find(function(e,t){return t.startsWith("2")&&(0,u.default)(e.get("content")||{}).length>0}),n=e.get("default")||l.default.OrderedMap(),r=(n.get("content")||l.default.OrderedMap()).keySeq().toJS().length?n:null;return t||r},t.deeplyStripKey=function e(t,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){return!0};if("object"!==(void 0===t?"undefined":(0,s.default)(t))||Array.isArray(t)||null===t||!n)return t;var o=(0,a.default)({},t);(0,u.default)(o).forEach(function(t){t===n&&r(o[t],t)?delete o[t]:o[t]=e(o[t],n,r)});return o},t.stringify=function(e){if("string"==typeof e)return e;e.toJS&&(e=e.toJS());if("object"===(void 0===e?"undefined":(0,s.default)(e))&&null!==e)try{return(0,r.default)(e,null,2)}catch(t){return String(e)}return e.toString()},t.numberToString=function(e){if("number"==typeof e)return e.toString();return e};var l=_(n(7)),c=n(572),f=_(n(573)),p=_(n(280)),d=_(n(285)),h=_(n(288)),v=_(n(650)),m=_(n(105)),y=n(192),g=_(n(32)),b=_(n(723));function _(e){return e&&e.__esModule?e:{default:e}}var w="default",E=t.isImmutable=function(e){return l.default.Iterable.isIterable(e)};function x(e){return Array.isArray(e)?e:[e]}function S(e){return!!e&&"object"===(void 0===e?"undefined":(0,s.default)(e))}t.memoize=d.default;function C(e){return(0,p.default)((0,f.default)(e))}t.propChecker=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];return(0,u.default)(e).length!==(0,u.default)(t).length||((0,v.default)(e,function(e,n){if(r.includes(n))return!1;var o=t[n];return l.default.Iterable.isIterable(e)?!l.default.is(e,o):("object"!==(void 0===e?"undefined":(0,s.default)(e))||"object"!==(void 0===o?"undefined":(0,s.default)(o)))&&e!==o})||n.some(function(n){return!(0,m.default)(e[n],t[n])}))};var k=t.validateMaximum=function(e,t){if(e>t)return"Value must be less than Maximum"},A=t.validateMinimum=function(e,t){if(et)return"Value must be less than MaxLength"},D=t.validateMinLength=function(e,t){if(e.length2&&void 0!==arguments[2]&&arguments[2],r=[],o=t&&"body"===e.get("in")?e.get("value_xml"):e.get("value"),i=e.get("required"),a=n?e.get("schema"):e;if(!a)return r;var u=a.get("maximum"),c=a.get("minimum"),f=a.get("type"),p=a.get("format"),d=a.get("maxLength"),h=a.get("minLength"),v=a.get("pattern");if(f&&(i||o)){var m="string"===f&&o,y="array"===f&&Array.isArray(o)&&o.length,b="array"===f&&l.default.List.isList(o)&&o.count(),_="file"===f&&o instanceof g.default.File,w="boolean"===f&&(o||!1===o),E="number"===f&&(o||0===o),x="integer"===f&&(o||0===o),S=!1;if(n&&"object"===f)if("object"===(void 0===o?"undefined":(0,s.default)(o)))S=!0;else if("string"==typeof o)try{JSON.parse(o),S=!0}catch(e){return r.push("Parameter string value must be valid JSON"),r}var C=[m,y,b,_,w,E,x,S].some(function(e){return!!e});if(i&&!C)return r.push("Required field is not provided"),r;if(v){var U=L(o,v);U&&r.push(U)}if(d||0===d){var q=R(o,d);q&&r.push(q)}if(h){var F=D(o,h);F&&r.push(F)}if(u||0===u){var z=k(o,u);z&&r.push(z)}if(c||0===c){var B=A(o,c);B&&r.push(B)}if("string"===f){var V=void 0;if(!(V="date-time"===p?j(o):"uuid"===p?N(o):I(o)))return r;r.push(V)}else if("boolean"===f){var H=M(o);if(!H)return r;r.push(H)}else if("number"===f){var W=O(o);if(!W)return r;r.push(W)}else if("integer"===f){var J=P(o);if(!J)return r;r.push(J)}else if("array"===f){var Y;if(!b||!o.count())return r;Y=a.getIn(["items","type"]),o.forEach(function(e,t){var n=void 0;"number"===Y?n=O(e):"integer"===Y?n=P(e):"string"===Y&&(n=I(e)),n&&r.push({index:t,error:n})})}else if("file"===f){var K=T(o);if(!K)return r;r.push(K)}}return r},t.getSampleSchema=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated --\x3e':null;var o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}return(0,y.memoizedCreateXMLExample)(e,n)}var i=(0,y.memoizedSampleFromSchema)(e,n);return"object"===(void 0===i?"undefined":(0,s.default)(i))?(0,r.default)(i,null,2):i},t.parseSearch=function(){var e={},t=g.default.location.search;if(!t)return{};if(""!=t){var n=t.substr(1).split("&");for(var r in n)n.hasOwnProperty(r)&&(r=n[r].split("="),e[decodeURIComponent(r[0])]=r[1]&&decodeURIComponent(r[1])||"")}return e},t.serializeSearch=function(e){return(0,u.default)(e).map(function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])}).join("&")},t.btoa=function(t){return(t instanceof e?t:new e(t.toString(),"utf-8")).toString("base64")},t.sorters={operationsSorter:{alpha:function(e,t){return e.get("path").localeCompare(t.get("path"))},method:function(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function(e,t){return e.localeCompare(t)}}},t.buildFormData=function(e){var t=[];for(var n in e){var r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},t.shallowEqualKeys=function(e,t,n){return!!(0,h.default)(n,function(n){return(0,m.default)(e[n],t[n])})};var U=t.createDeepLinkPath=function(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"_"):""};t.escapeDeepLinkPath=function(e){return(0,b.default)(U(e))},t.getExtensions=function(e){return e.filter(function(e,t){return/^x-/.test(t)})},t.getCommonExtensions=function(e){return e.filter(function(e,t){return/^pattern|maxLength|minLength|maximum|minimum/.test(t)})}}).call(t,n(54).Buffer)},function(e,t,n){"use strict"; +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,a,u=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),s=1;s?@[\]^_`{|}~-])/g;function a(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function u(e){if(e>65535){var t=55296+((e-=65536)>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}var s=/&([a-z#][a-z0-9]{1,31});/gi,l=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,c=n(417);function f(e,t){var n=0;return o(c,t)?c[t]:35===t.charCodeAt(0)&&l.test(t)&&a(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?u(n):e}var p=/[&<>"]/,d=/[&<>"]/g,h={"&":"&","<":"<",">":">",'"':"""};function v(e){return h[e]}t.assign=function(e){return[].slice.call(arguments,1).forEach(function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach(function(n){e[n]=t[n]})}}),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=o,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(i,"$1")},t.isValidEntityCode=a,t.fromCodePoint=u,t.replaceEntities=function(e){return e.indexOf("&")<0?e:e.replace(s,f)},t.escapeHtml=function(e){return p.test(e)?e.replace(d,v):e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var r=n(33),o=n(60),i=n(58),a=n(72),u=n(120),s=function(e,t,n){var l,c,f,p,d=e&s.F,h=e&s.G,v=e&s.S,m=e&s.P,y=e&s.B,g=h?r:v?r[t]||(r[t]={}):(r[t]||{}).prototype,b=h?o:o[t]||(o[t]={}),_=b.prototype||(b.prototype={});for(l in h&&(n=t),n)f=((c=!d&&g&&void 0!==g[l])?g:n)[l],p=y&&c?u(f,r):m&&"function"==typeof f?u(Function.call,f):f,g&&a(g,l,f,e&s.U),b[l]!=f&&i(b,l,p),m&&_[l]!=f&&(_[l]=f)};r.core=o,s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t,n){var r=n(29),o=n(101),i=n(53),a=/"/g,u=function(e,t,n,r){var o=String(i(e)),u="<"+t;return""!==n&&(u+=" "+n+'="'+String(r).replace(a,""")+'"'),u+">"+o+""};e.exports=function(e,t){var n={};n[e]=t(u),r(r.P+r.F*o(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r,o=n(91),i=(r=o)&&r.__esModule?r:{default:r};e.exports=function(){var e={location:{},history:{},open:function(){},close:function(){},File:function(){}};if("undefined"==typeof window)return e;try{e=window;var t=!0,n=!1,r=void 0;try{for(var o,a=(0,i.default)(["File","Blob","FormData"]);!(t=(o=a.next()).done);t=!0){var u=o.value;u in window&&(e[u]=window[u])}}catch(e){n=!0,r=e}finally{try{!t&&a.return&&a.return()}finally{if(n)throw r}}}catch(e){console.error(e)}return e}()},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=i(n(25));t.isOAS3=a,t.isSwagger2=function(e){var t=e.get("swagger");if("string"!=typeof t)return!1;return t.startsWith("2.0")},t.OAS3ComponentWrapFactory=function(e){return function(t,n){return function(i){if(n&&n.specSelectors&&n.specSelectors.specJson){var u=n.specSelectors.specJson();return a(u)?o.default.createElement(e,(0,r.default)({},i,n,{Ori:t})):o.default.createElement(t,i)}return console.warn("OAS3 wrapper: couldn't get spec"),null}}};var o=i(n(0));function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=e.get("openapi");return"string"==typeof t&&(t.startsWith("3.0.")&&t.length>4)}},function(e,t,n){var r=n(28);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){var r=n(278),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t,n){"use strict";var r=null;e.exports={debugTool:r}},function(e,t,n){var r=n(36),o=n(238),i=n(157),a=Object.defineProperty;t.f=n(44)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){e.exports={default:n(517),__esModule:!0}},function(e,t,n){e.exports={default:n(518),__esModule:!0}},function(e,t,n){"use strict";var r=n(10),o=n(13),i=n(355),a=n(68),u=n(356),s=n(88),l=n(147),c=n(8),f=[],p=0,d=i.getPooled(),h=!1,v=null;function m(){E.ReactReconcileTransaction&&v||r("123")}var y=[{initialize:function(){this.dirtyComponentsLength=f.length},close:function(){this.dirtyComponentsLength!==f.length?(f.splice(0,this.dirtyComponentsLength),w()):f.length=0}},{initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}}];function g(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=i.getPooled(),this.reconcileTransaction=E.ReactReconcileTransaction.getPooled(!0)}function b(e,t){return e._mountOrder-t._mountOrder}function _(e){var t=e.dirtyComponentsLength;t!==f.length&&r("124",t,f.length),f.sort(b),p++;for(var n=0;n + * @license MIT + */ +var r=n(529),o=n(530),i=n(261);function a(){return s.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function u(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function h(e,t){if(s.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return z(e).length;default:if(r)return F(e).length;t=(""+t).toLowerCase(),r=!0}}function v(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function m(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=s.from(t,r)),s.isBuffer(t))return 0===t.length?-1:y(e,t,n,r,o);if("number"==typeof t)return t&=255,s.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):y(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function y(e,t,n,r,o){var i,a=1,u=e.length,s=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,u/=2,s/=2,n/=2}function l(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var c=-1;for(i=n;iu&&(n=u-s),i=n;i>=0;i--){for(var f=!0,p=0;po&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function S(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function C(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:l>223?3:l>191?2:1;if(o+f<=n)switch(f){case 1:l<128&&(c=l);break;case 2:128==(192&(i=e[o+1]))&&(s=(31&l)<<6|63&i)>127&&(c=s);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(s=(15&l)<<12|(63&i)<<6|63&a)>2047&&(s<55296||s>57343)&&(c=s);break;case 4:i=e[o+1],a=e[o+2],u=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&u)&&(s=(15&l)<<18|(63&i)<<12|(63&a)<<6|63&u)>65535&&s<1114112&&(c=s)}null===c?(c=65533,f=1):c>65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c),o+=f}return function(e){var t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);var n="",r=0;for(;rthis.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return C(this,t,n);case"ascii":return A(this,t,n);case"latin1":case"binary":return O(this,t,n);case"base64":return S(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}.apply(this,arguments)},s.prototype.equals=function(e){if(!s.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===s.compare(this,e)},s.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},s.prototype.compare=function(e,t,n,r,o){if(!s.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,o>>>=0,this===e)return 0;for(var i=o-r,a=n-t,u=Math.min(i,a),l=this.slice(r,o),c=e.slice(t,n),f=0;fo)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return g(this,e,t,n);case"utf8":case"utf-8":return b(this,e,t,n);case"ascii":return _(this,e,t,n);case"latin1":case"binary":return w(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return x(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},s.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var k=4096;function A(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function I(e,t,n,r,o,i){if(!s.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function j(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function N(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function R(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function D(e,t,n,r,i){return i||R(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function L(e,t,n,r,i){return i||R(e,0,n,8),o.write(e,t,n,r,52,8),n+8}s.prototype.slice=function(e,t){var n,r=this.length;if(e=~~e,t=void 0===t?r:~~t,e<0?(e+=r)<0&&(e=0):e>r&&(e=r),t<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},s.prototype.readUInt8=function(e,t){return t||M(e,1,this.length),this[e]},s.prototype.readUInt16LE=function(e,t){return t||M(e,2,this.length),this[e]|this[e+1]<<8},s.prototype.readUInt16BE=function(e,t){return t||M(e,2,this.length),this[e]<<8|this[e+1]},s.prototype.readUInt32LE=function(e,t){return t||M(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},s.prototype.readUInt32BE=function(e,t){return t||M(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},s.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||M(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},s.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||M(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},s.prototype.readInt8=function(e,t){return t||M(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},s.prototype.readInt16LE=function(e,t){t||M(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt16BE=function(e,t){t||M(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt32LE=function(e,t){return t||M(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},s.prototype.readInt32BE=function(e,t){return t||M(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},s.prototype.readFloatLE=function(e,t){return t||M(e,4,this.length),o.read(this,e,!0,23,4)},s.prototype.readFloatBE=function(e,t){return t||M(e,4,this.length),o.read(this,e,!1,23,4)},s.prototype.readDoubleLE=function(e,t){return t||M(e,8,this.length),o.read(this,e,!0,52,8)},s.prototype.readDoubleBE=function(e,t){return t||M(e,8,this.length),o.read(this,e,!1,52,8)},s.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||I(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},s.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,255,0),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},s.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},s.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},s.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):N(this,e,t,!0),t+4},s.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},s.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=0,a=1,u=0;for(this[t]=255&e;++i>0)-u&255;return t+n},s.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=n-1,a=1,u=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===u&&0!==this[t+i+1]&&(u=1),this[t+i]=(e/a>>0)-u&255;return t+n},s.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,127,-128),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},s.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},s.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},s.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):N(this,e,t,!0),t+4},s.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},s.prototype.writeFloatLE=function(e,t,n){return D(this,e,t,!0,n)},s.prototype.writeFloatBE=function(e,t,n){return D(this,e,t,!1,n)},s.prototype.writeDoubleLE=function(e,t,n){return L(this,e,t,!0,n)},s.prototype.writeDoubleBE=function(e,t,n){return L(this,e,t,!1,n)},s.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!s.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function z(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(U,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function B(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(t,n(31))},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function u(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var s,l=[],c=!1,f=-1;function p(){c&&s&&(c=!1,s.length?l=s.concat(l):f=-1,l.length&&d())}function d(){if(!c){var e=u(p);c=!0;for(var t=l.length;t;){for(s=l,l=[];++f1)for(var n=1;n1?t-1:0),r=1;r2?n-2:0),o=2;o1){for(var h=Array(d),v=0;v1){for(var y=Array(m),g=0;g=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}},function(e,t,n){"use strict";function r(e){return void 0===e||null===e}e.exports.isNothing=r,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:r(e)?[]:[e]},e.exports.repeat=function(e,t){var n,r="";for(n=0;n=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){e.exports=!n(101)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports={}},function(e,t,n){var r=n(119),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t,n){"use strict";e.exports=function(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r0?o(r(e),9007199254740991):0}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){var r=n(59),o=n(460),i=n(461),a=Object.defineProperty;t.f=n(100)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(121);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,o){return e.call(t,n,r,o)}}return function(){return e.apply(t,arguments)}}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(466),o=n(53);e.exports=function(e){return r(o(e))}},function(e,t,n){"use strict";var r=n(58),o=n(72),i=n(101),a=n(53),u=n(17);e.exports=function(e,t,n){var s=u(e),l=n(a,s,""[e]),c=l[0],f=l[1];i(function(){var t={};return t[s]=function(){return 7},7!=""[e](t)})&&(o(String.prototype,e,c),r(RegExp.prototype,s,2==t?function(e,t){return f.call(e,this,t)}:function(e){return f.call(e,this)}))}},function(e,t,n){var r=n(116)("meta"),o=n(28),i=n(52),a=n(40).f,u=0,s=Object.isExtensible||function(){return!0},l=!n(51)(function(){return s(Object.preventExtensions({}))}),c=function(e){a(e,r,{value:{i:"O"+ ++u,w:{}}})},f=e.exports={KEY:r,NEED:!1,fastKey:function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!i(e,r)){if(!s(e))return"F";if(!t)return"E";c(e)}return e[r].i},getWeak:function(e,t){if(!i(e,r)){if(!s(e))return!0;if(!t)return!1;c(e)}return e[r].w},onFreeze:function(e){return l&&f.NEED&&s(e)&&!i(e,r)&&c(e),e}}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){"use strict";var r={};e.exports=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CLEAR_BY=t.CLEAR=t.NEW_AUTH_ERR=t.NEW_SPEC_ERR_BATCH=t.NEW_SPEC_ERR=t.NEW_THROWN_ERR_BATCH=t.NEW_THROWN_ERR=void 0,t.newThrownErr=function(e){return{type:a,payload:(0,i.default)(e)}},t.newThrownErrBatch=function(e){return{type:u,payload:e}},t.newSpecErr=function(e){return{type:s,payload:e}},t.newSpecErrBatch=function(e){return{type:l,payload:e}},t.newAuthErr=function(e){return{type:c,payload:e}},t.clear=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:f,payload:e}},t.clearBy=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!0};return{type:p,payload:e}};var r,o=n(179),i=(r=o)&&r.__esModule?r:{default:r};var a=t.NEW_THROWN_ERR="err_new_thrown_err",u=t.NEW_THROWN_ERR_BATCH="err_new_thrown_err_batch",s=t.NEW_SPEC_ERR="err_new_spec_err",l=t.NEW_SPEC_ERR_BATCH="err_new_spec_err_batch",c=t.NEW_AUTH_ERR="err_new_auth_err",f=t.CLEAR="err_clear",p=t.CLEAR_BY="err_clear_by"},function(e,t,n){var r=n(61),o=n(47),i="[object Symbol]";e.exports=function(e){return"symbol"==typeof e||o(e)&&r(e)==i}},function(e,t,n){var r=n(62)(Object,"create");e.exports=r},function(e,t,n){var r=n(600),o=n(601),i=n(602),a=n(603),u=n(604);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&eb;b++)if((m=t?g(a(h=e[b])[0],h[1]):g(e[b]))===l||m===c)return m}else for(v=y.call(e);!(h=v.next()).done;)if((m=o(v,g,h.value,t))===l||m===c)return m}).BREAK=l,t.RETURN=c},function(e,t,n){"use strict";var r=n(86);e.exports=r.DEFAULT=new r({include:[n(108)],explicit:[n(757),n(758),n(759)]})},function(e,t,n){var r=n(345),o=n(105),i=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var a=e[t];i.call(e,t)&&o(a,n)&&(void 0!==n||t in e)||r(e,t,n)}},function(e,t,n){"use strict";var r=n(10),o=(n(8),{}),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,u,s){var l,c;this.isInTransaction()&&r("27");try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,u,s),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(e){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n]/,s=n(218)(function(e,t){if(e.namespaceURI!==i.svg||"innerHTML"in e)e.innerHTML=t;else{(r=r||document.createElement("div")).innerHTML=""+t+"";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(o.canUseDOM){var l=document.createElement("div");l.innerHTML=" ",""===l.innerHTML&&(s=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&u.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),l=null}e.exports=s},function(e,t,n){"use strict";var r=/["'&<>]/;e.exports=function(e){return"boolean"==typeof e||"number"==typeof e?""+e:function(e){var t,n=""+e,o=r.exec(n);if(!o)return n;var i="",a=0,u=0;for(a=o.index;adocument.F=Object<\/script>"),e.close(),s=e.F;r--;)delete s.prototype[i[r]];return s()};e.exports=Object.create||function(e,t){var n;return null!==e?(u.prototype=r(e),n=new u,u.prototype=null,n[a]=e):n=s(),void 0===t?n:o(n,t)}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(162)("keys"),o=n(116);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(21),o=r["__core-js_shared__"]||(r["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){var r=n(165),o=n(19)("iterator"),i=n(69);e.exports=n(15).getIteratorMethod=function(e){if(void 0!=e)return e[o]||e["@@iterator"]||i[r(e)]}},function(e,t,n){var r=n(93),o=n(19)("toStringTag"),i="Arguments"==r(function(){return arguments}());e.exports=function(e){var t,n,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:i?r(t):"Object"==(a=r(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t,n){var r=n(99),o=n(17)("toStringTag"),i="Arguments"==r(function(){return arguments}());e.exports=function(e){var t,n,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:i?r(t):"Object"==(a=r(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){var r=n(73),o=n(33).document,i=r(o)&&r(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t,n){var r=n(242)("keys"),o=n(167);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(117).f,o=n(118),i=n(17)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){"use strict";var r=n(121);e.exports.f=function(e){return new function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=r(t),this.reject=r(n)}(e)}},function(e,t,n){var r=n(256),o=n(53);e.exports=function(e,t,n){if(r(t))throw TypeError("String#"+n+" doesn't accept regex!");return String(o(e))}},function(e,t,n){var r=n(17)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[r]=!1,!"/./"[e](t)}catch(e){}}return!0}},function(e,t,n){t.f=n(19)},function(e,t,n){var r=n(21),o=n(15),i=n(114),a=n(174),u=n(40).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=i?{}:r.Symbol||{});"_"==e.charAt(0)||e in t||u(t,e,{value:a.f(e)})}},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){},function(e,t,n){"use strict";(function(t){ +/*! + * @description Recursive object extending + * @author Viacheslav Lotsmanov + * @license MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Viacheslav Lotsmanov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function n(e){return e instanceof t||e instanceof Date||e instanceof RegExp}function r(e){if(e instanceof t){var n=t.alloc?t.alloc(e.length):new t(e.length);return e.copy(n),n}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function o(e,t){return"__proto__"===t?void 0:e[t]}var i=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,a=arguments[0];return Array.prototype.slice.call(arguments,1).forEach(function(u){"object"!=typeof u||null===u||Array.isArray(u)||Object.keys(u).forEach(function(s){return t=o(a,s),(e=o(u,s))===a?void 0:"object"!=typeof e||null===e?void(a[s]=e):Array.isArray(e)?void(a[s]=function e(t){var o=[];return t.forEach(function(t,a){"object"==typeof t&&null!==t?Array.isArray(t)?o[a]=e(t):n(t)?o[a]=r(t):o[a]=i({},t):o[a]=t}),o}(e)):n(e)?void(a[s]=r(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(a[s]=i({},e)):void(a[s]=i(t,e))})}),a}}).call(t,n(54).Buffer)},function(e,t,n){"use strict";e.exports=function(e){return"object"==typeof e?function e(t,n){var r;r=Array.isArray(t)?[]:{};n.push(t);Object.keys(t).forEach(function(o){var i=t[o];"function"!=typeof i&&(i&&"object"==typeof i?-1!==n.indexOf(t[o])?r[o]="[Circular]":r[o]=e(t[o],n.slice(0)):r[o]=i)});"string"==typeof t.name&&(r.name=t.name);"string"==typeof t.message&&(r.message=t.message);"string"==typeof t.stack&&(r.stack=t.stack);return r}(e,[]):"function"==typeof e?"[Function: "+(e.name||"anonymous")+"]":e}},function(e,t,n){var r=n(589),o=n(605),i=n(607),a=n(608),u=n(609);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e<=n}},function(e,t){e.exports=function(e){return function(t){return e(t)}}},function(e,t,n){(function(e){var r=n(278),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,a=i&&i.exports===o&&r.process,u=function(){try{var e=i&&i.require&&i.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=u}).call(t,n(134)(e))},function(e,t,n){var r=n(24),o=n(128),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||a.test(e)||!i.test(e)||null!=t&&e in Object(t)}},function(e,t){e.exports=function(e){return e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.memoizedSampleFromSchema=t.memoizedCreateXMLExample=t.sampleXmlFromSchema=t.inferSchema=t.sampleFromSchema=void 0,t.createXMLExample=f;var r=n(12),o=a(n(656)),i=a(n(669));function a(e){return e&&e.__esModule?e:{default:e}}var u={string:function(){return"string"},string_email:function(){return"user@example.com"},"string_date-time":function(){return(new Date).toISOString()},number:function(){return 0},number_float:function(){return 0},integer:function(){return 0},boolean:function(e){return"boolean"!=typeof e.default||e.default}},s=function(e){var t=e=(0,r.objectify)(e),n=t.type,o=t.format,i=u[n+"_"+o]||u[n];return(0,r.isFunc)(i)?i(e):"Unknown Type: "+e.type},l=t.sampleFromSchema=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=(0,r.objectify)(t),i=o.type,a=o.example,u=o.properties,l=o.additionalProperties,c=o.items,f=n.includeReadOnly,p=n.includeWriteOnly;if(void 0!==a)return(0,r.deeplyStripKey)(a,"$$ref",function(e){return"string"==typeof e&&e.indexOf("#")>-1});if(!i)if(u)i="object";else{if(!c)return;i="array"}if("object"===i){var d=(0,r.objectify)(u),h={};for(var v in d)d[v]&&d[v].readOnly&&!f||d[v]&&d[v].writeOnly&&!p||(h[v]=e(d[v],n));if(!0===l)h.additionalProp1={};else if(l)for(var m=(0,r.objectify)(l),y=e(m,n),g=1;g<4;g++)h["additionalProp"+g]=y;return h}return"array"===i?Array.isArray(c.anyOf)?c.anyOf.map(function(t){return e(t,n)}):Array.isArray(c.oneOf)?c.oneOf.map(function(t){return e(t,n)}):[e(c,n)]:t.enum?t.default?t.default:(0,r.normalizeArray)(t.enum)[0]:"file"!==i?s(t):void 0},c=(t.inferSchema=function(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},t.sampleXmlFromSchema=function e(t){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=(0,r.objectify)(t),a=i.type,u=i.properties,l=i.additionalProperties,c=i.items,f=i.example,p=o.includeReadOnly,d=o.includeWriteOnly,h=i.default,v={},m={},y=t.xml,g=y.name,b=y.prefix,_=y.namespace,w=i.enum,E=void 0;if(!a)if(u||l)a="object";else{if(!c)return;a="array"}(g=g||"notagname",n=(b?b+":":"")+g,_)&&(m[b?"xmlns:"+b:"xmlns"]=_);if("array"===a&&c){if(c.xml=c.xml||y||{},c.xml.name=c.xml.name||y.name,y.wrapped)return v[n]=[],Array.isArray(f)?f.forEach(function(t){c.example=t,v[n].push(e(c,o))}):Array.isArray(h)?h.forEach(function(t){c.default=t,v[n].push(e(c,o))}):v[n]=[e(c,o)],m&&v[n].push({_attr:m}),v;var x=[];return Array.isArray(f)?(f.forEach(function(t){c.example=t,x.push(e(c,o))}),x):Array.isArray(h)?(h.forEach(function(t){c.default=t,x.push(e(c,o))}),x):e(c,o)}if("object"===a){var S=(0,r.objectify)(u);for(var C in v[n]=[],f=f||{},S)if(S.hasOwnProperty(C)&&(!S[C].readOnly||p)&&(!S[C].writeOnly||d))if(S[C].xml=S[C].xml||{},S[C].xml.attribute){var k=Array.isArray(S[C].enum)&&S[C].enum[0],A=S[C].example,O=S[C].default;m[S[C].xml.name||C]=void 0!==A&&A||void 0!==f[C]&&f[C]||void 0!==O&&O||k||s(S[C])}else{S[C].xml.name=S[C].xml.name||C,void 0===S[C].example&&void 0!==f[C]&&(S[C].example=f[C]);var P=e(S[C]);Array.isArray(P)?v[n]=v[n].concat(P):v[n].push(P)}return!0===l?v[n].push({additionalProp:"Anything can be here"}):l&&v[n].push({additionalProp:s(l)}),m&&v[n].push({_attr:m}),v}return E=void 0!==f?f:void 0!==h?h:Array.isArray(w)?w[0]:s(t),v[n]=m?[{_attr:m},E]:E,v});function f(e,t){var n=c(e,t);if(n)return(0,o.default)(n,{declaration:!0,indent:"\t"})}t.memoizedCreateXMLExample=(0,i.default)(f),t.memoizedSampleFromSchema=(0,i.default)(l)},function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function o(e){return"object"==typeof e&&null!==e}function i(e){return void 0===e}e.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,a,u,s,l;if(this._events||(this._events={}),"error"===e&&(!this._events.error||o(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var c=new Error('Uncaught, unspecified "error" event. ('+t+")");throw c.context=t,c}if(i(n=this._events[e]))return!1;if(r(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:u=Array.prototype.slice.call(arguments,1),n.apply(this,u)}else if(o(n))for(u=Array.prototype.slice.call(arguments,1),a=(l=n.slice()).length,s=0;s0&&this._events[e].length>a&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){if(!r(t))throw TypeError("listener must be a function");var n=!1;function o(){this.removeListener(e,o),n||(n=!0,t.apply(this,arguments))}return o.listener=t,this.on(e,o),this},n.prototype.removeListener=function(e,t){var n,i,a,u;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(a=(n=this._events[e]).length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(u=a;u-- >0;)if(n[u]===t||n[u].listener&&n[u].listener===t){i=u;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(r(n=this._events[e]))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}},function(e,t,n){(t=e.exports=n(306)).Stream=t,t.Readable=t,t.Writable=n(195),t.Duplex=n(64),t.Transform=n(311),t.PassThrough=n(664)},function(e,t,n){"use strict";(function(t,r,o){var i=n(140);function a(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var o=r.callback;t.pendingcb--,o(n),r=r.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=g;var u,s=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:i.nextTick;g.WritableState=y;var l=n(106);l.inherits=n(81);var c={deprecate:n(663)},f=n(307),p=n(141).Buffer,d=o.Uint8Array||function(){};var h,v=n(308);function m(){}function y(e,t){u=u||n(64),e=e||{};var r=t instanceof u;this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var o=e.highWaterMark,l=e.writableHighWaterMark,c=this.objectMode?16:16384;this.highWaterMark=o||0===o?o:r&&(l||0===l)?l:c,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var f=!1===e.decodeStrings;this.decodeStrings=!f,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,o=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,o){--t.pendingcb,n?(i.nextTick(o,r),i.nextTick(S,e,t),e._writableState.errorEmitted=!0,e.emit("error",r)):(o(r),e._writableState.errorEmitted=!0,e.emit("error",r),S(e,t))}(e,n,r,t,o);else{var a=E(n);a||n.corked||n.bufferProcessing||!n.bufferedRequest||w(e,n),r?s(_,e,n,a,o):_(e,n,a,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function g(e){if(u=u||n(64),!(h.call(g,this)||this instanceof u))return new g(e);this._writableState=new y(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),f.call(this)}function b(e,t,n,r,o,i,a){t.writelen=r,t.writecb=a,t.writing=!0,t.sync=!0,n?e._writev(o,t.onwrite):e._write(o,i,t.onwrite),t.sync=!1}function _(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),S(e,t)}function w(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),i=t.corkedRequestsFree;i.entry=n;for(var u=0,s=!0;n;)o[u]=n,n.isBuf||(s=!1),n=n.next,u+=1;o.allBuffers=s,b(e,t,!0,t.length,o,"",i.finish),t.pendingcb++,t.lastBufferedRequest=null,i.next?(t.corkedRequestsFree=i.next,i.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;n;){var l=n.chunk,c=n.encoding,f=n.callback;if(b(e,t,!1,t.objectMode?1:l.length,l,c,f),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function E(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function x(e,t){e._final(function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),S(e,t)})}function S(e,t){var n=E(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,i.nextTick(x,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}l.inherits(g,f),y.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(y.prototype,"buffer",{get:c.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(h=Function.prototype[Symbol.hasInstance],Object.defineProperty(g,Symbol.hasInstance,{value:function(e){return!!h.call(this,e)||this===g&&(e&&e._writableState instanceof y)}})):h=function(e){return e instanceof this},g.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},g.prototype.write=function(e,t,n){var r,o=this._writableState,a=!1,u=!o.objectMode&&(r=e,p.isBuffer(r)||r instanceof d);return u&&!p.isBuffer(e)&&(e=function(e){return p.from(e)}(e)),"function"==typeof t&&(n=t,t=null),u?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=m),o.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),i.nextTick(t,n)}(this,n):(u||function(e,t,n,r){var o=!0,a=!1;return null===n?a=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),i.nextTick(r,a),o=!1),o}(this,o,e,n))&&(o.pendingcb++,a=function(e,t,n,r,o,i){if(!n){var a=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=p.from(t,n));return t}(t,r,o);r!==a&&(n=!0,o="buffer",r=a)}var u=t.objectMode?1:r.length;t.length+=u;var s=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(g.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),g.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},g.prototype._writev=null,g.prototype.end=function(e,t,n){var r=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!==e&&void 0!==e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||r.finished||function(e,t,n){t.ending=!0,S(e,t),n&&(t.finished?i.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,r,n)},Object.defineProperty(g.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),g.prototype.destroy=v.destroy,g.prototype._undestroy=v.undestroy,g.prototype._destroy=function(e,t){this.end(),t(e)}}).call(t,n(55),n(309).setImmediate,n(31))},function(e,t,n){"use strict";e.exports=function(e){return"function"==typeof e}},function(e,t,n){"use strict";e.exports=n(690)()?Array.from:n(691)},function(e,t,n){"use strict";var r=n(704),o=n(66),i=n(82),a=Array.prototype.indexOf,u=Object.prototype.hasOwnProperty,s=Math.abs,l=Math.floor;e.exports=function(e){var t,n,c,f;if(!r(e))return a.apply(this,arguments);for(n=o(i(this).length),c=arguments[1],t=c=isNaN(c)?0:c>=0?l(c):o(this.length)-l(s(c));t1&&void 0!==arguments[1])||arguments[1];return e=(0,r.normalizeArray)(e),{type:u,payload:{thing:e,shown:t}}},t.changeMode=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,r.normalizeArray)(e),{type:a,payload:{thing:e,mode:t}}};var r=n(12),o=t.UPDATE_LAYOUT="layout_update_layout",i=t.UPDATE_FILTER="layout_update_filter",a=t.UPDATE_MODE="layout_update_mode",u=t.SHOW="layout_show"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateBeforeExecute=t.canExecuteScheme=t.operationScheme=t.hasHost=t.operationWithMeta=t.parameterWithMeta=t.parameterInclusionSettingFor=t.parameterWithMetaByIdentity=t.allowTryItOutFor=t.mutatedRequestFor=t.requestFor=t.responseFor=t.mutatedRequests=t.requests=t.responses=t.taggedOperations=t.operationsWithTags=t.tagDetails=t.tags=t.operationsWithRootInherited=t.schemes=t.host=t.basePath=t.definitions=t.findDefinition=t.securityDefinitions=t.security=t.produces=t.consumes=t.operations=t.paths=t.semver=t.version=t.externalDocs=t.info=t.isOAS3=t.spec=t.specJsonWithResolvedSubtrees=t.specResolvedSubtree=t.specResolved=t.specJson=t.specSource=t.specStr=t.url=t.lastError=void 0;var r,o=n(83),i=(r=o)&&r.__esModule?r:{default:r};t.getParameter=function(e,t,n,r){return t=t||[],e.getIn(["meta","paths"].concat((0,i.default)(t),["parameters"]),(0,s.fromJS)([])).find(function(e){return s.Map.isMap(e)&&e.get("name")===n&&e.get("in")===r})||(0,s.Map)()},t.parameterValues=function(e,t,n){return t=t||[],P.apply(void 0,[e].concat((0,i.default)(t))).get("parameters",(0,s.List)()).reduce(function(e,t){var r=n&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set(t.get("in")+"."+t.get("name"),r)},(0,s.fromJS)({}))},t.parametersIncludeIn=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(s.List.isList(e))return e.some(function(e){return s.Map.isMap(e)&&e.get("in")===t})},t.parametersIncludeType=T,t.contentTypeValues=function(e,t){t=t||[];var n=d(e).getIn(["paths"].concat((0,i.default)(t)),(0,s.fromJS)({})),r=e.getIn(["meta","paths"].concat((0,i.default)(t)),(0,s.fromJS)({})),o=M(e,t),a=n.get("parameters")||new s.List,u=r.get("consumes_value")?r.get("consumes_value"):T(a,"file")?"multipart/form-data":T(a,"formData")?"application/x-www-form-urlencoded":void 0;return(0,s.fromJS)({requestContentType:u,responseContentType:o})},t.operationConsumes=function(e,t){return t=t||[],d(e).getIn(["paths"].concat((0,i.default)(t),["consumes"]),(0,s.fromJS)({}))},t.currentProducesFor=M;var a=n(57),u=n(12),s=n(7);var l=["get","put","post","delete","options","head","patch","trace"],c=function(e){return e||(0,s.Map)()},f=(t.lastError=(0,a.createSelector)(c,function(e){return e.get("lastError")}),t.url=(0,a.createSelector)(c,function(e){return e.get("url")}),t.specStr=(0,a.createSelector)(c,function(e){return e.get("spec")||""}),t.specSource=(0,a.createSelector)(c,function(e){return e.get("specSource")||"not-editor"}),t.specJson=(0,a.createSelector)(c,function(e){return e.get("json",(0,s.Map)())})),p=(t.specResolved=(0,a.createSelector)(c,function(e){return e.get("resolved",(0,s.Map)())}),t.specResolvedSubtree=function(e,t){return e.getIn(["resolvedSubtrees"].concat((0,i.default)(t)),void 0)},function e(t,n){return s.Map.isMap(t)&&s.Map.isMap(n)?n.get("$$ref")?n:(0,s.OrderedMap)().mergeWith(e,t,n):n}),d=t.specJsonWithResolvedSubtrees=(0,a.createSelector)(c,function(e){return(0,s.OrderedMap)().mergeWith(p,e.get("json"),e.get("resolvedSubtrees"))}),h=t.spec=function(e){return f(e)},v=(t.isOAS3=(0,a.createSelector)(h,function(){return!1}),t.info=(0,a.createSelector)(h,function(e){return j(e&&e.get("info"))})),m=(t.externalDocs=(0,a.createSelector)(h,function(e){return j(e&&e.get("externalDocs"))}),t.version=(0,a.createSelector)(v,function(e){return e&&e.get("version")})),y=(t.semver=(0,a.createSelector)(m,function(e){return/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e).slice(1)}),t.paths=(0,a.createSelector)(d,function(e){return e.get("paths")})),g=t.operations=(0,a.createSelector)(y,function(e){if(!e||e.size<1)return(0,s.List)();var t=(0,s.List)();return e&&e.forEach?(e.forEach(function(e,n){if(!e||!e.forEach)return{};e.forEach(function(e,r){l.indexOf(r)<0||(t=t.push((0,s.fromJS)({path:n,method:r,operation:e,id:r+"-"+n})))})}),t):(0,s.List)()}),b=t.consumes=(0,a.createSelector)(h,function(e){return(0,s.Set)(e.get("consumes"))}),_=t.produces=(0,a.createSelector)(h,function(e){return(0,s.Set)(e.get("produces"))}),w=(t.security=(0,a.createSelector)(h,function(e){return e.get("security",(0,s.List)())}),t.securityDefinitions=(0,a.createSelector)(h,function(e){return e.get("securityDefinitions")}),t.findDefinition=function(e,t){var n=e.getIn(["resolvedSubtrees","definitions",t],null),r=e.getIn(["json","definitions",t],null);return n||r||null},t.definitions=(0,a.createSelector)(h,function(e){return e.get("definitions")||(0,s.Map)()}),t.basePath=(0,a.createSelector)(h,function(e){return e.get("basePath")}),t.host=(0,a.createSelector)(h,function(e){return e.get("host")}),t.schemes=(0,a.createSelector)(h,function(e){return e.get("schemes",(0,s.Map)())}),t.operationsWithRootInherited=(0,a.createSelector)(g,b,_,function(e,t,n){return e.map(function(e){return e.update("operation",function(e){if(e){if(!s.Map.isMap(e))return;return e.withMutations(function(e){return e.get("consumes")||e.update("consumes",function(e){return(0,s.Set)(e).merge(t)}),e.get("produces")||e.update("produces",function(e){return(0,s.Set)(e).merge(n)}),e})}return(0,s.Map)()})})})),E=t.tags=(0,a.createSelector)(h,function(e){return e.get("tags",(0,s.List)())}),x=t.tagDetails=function(e,t){return(E(e)||(0,s.List)()).filter(s.Map.isMap).find(function(e){return e.get("name")===t},(0,s.Map)())},S=t.operationsWithTags=(0,a.createSelector)(w,E,function(e,t){return e.reduce(function(e,t){var n=(0,s.Set)(t.getIn(["operation","tags"]));return n.count()<1?e.update("default",(0,s.List)(),function(e){return e.push(t)}):n.reduce(function(e,n){return e.update(n,(0,s.List)(),function(e){return e.push(t)})},e)},t.reduce(function(e,t){return e.set(t.get("name"),(0,s.List)())},(0,s.OrderedMap)()))}),C=(t.taggedOperations=function(e){return function(t){var n=(0,t.getConfigs)(),r=n.tagsSorter,o=n.operationsSorter;return S(e).sortBy(function(e,t){return t},function(e,t){var n="function"==typeof r?r:u.sorters.tagsSorter[r];return n?n(e,t):null}).map(function(t,n){var r="function"==typeof o?o:u.sorters.operationsSorter[o],i=r?t.sort(r):t;return(0,s.Map)({tagDetails:x(e,n),operations:i})})}},t.responses=(0,a.createSelector)(c,function(e){return e.get("responses",(0,s.Map)())})),k=t.requests=(0,a.createSelector)(c,function(e){return e.get("requests",(0,s.Map)())}),A=t.mutatedRequests=(0,a.createSelector)(c,function(e){return e.get("mutatedRequests",(0,s.Map)())}),O=(t.responseFor=function(e,t,n){return C(e).getIn([t,n],null)},t.requestFor=function(e,t,n){return k(e).getIn([t,n],null)},t.mutatedRequestFor=function(e,t,n){return A(e).getIn([t,n],null)},t.allowTryItOutFor=function(){return!0},t.parameterWithMetaByIdentity=function(e,t,n){var r=d(e).getIn(["paths"].concat((0,i.default)(t),["parameters"]),(0,s.OrderedMap)()),o=e.getIn(["meta","paths"].concat((0,i.default)(t),["parameters"]),(0,s.OrderedMap)());return r.map(function(e){var t=o.get(n.get("name")+"."+n.get("in")),r=o.get(n.get("name")+"."+n.get("in")+".hash-"+n.hashCode());return(0,s.OrderedMap)().merge(e,t,r)}).find(function(e){return e.get("in")===n.get("in")&&e.get("name")===n.get("name")},(0,s.OrderedMap)())}),P=(t.parameterInclusionSettingFor=function(e,t,n,r){var o=n+"."+r;return e.getIn(["meta","paths"].concat((0,i.default)(t),["parameter_inclusions",o]),!1)},t.parameterWithMeta=function(e,t,n,r){var o=d(e).getIn(["paths"].concat((0,i.default)(t),["parameters"]),(0,s.OrderedMap)()).find(function(e){return e.get("in")===r&&e.get("name")===n},(0,s.OrderedMap)());return O(e,t,o)},t.operationWithMeta=function(e,t,n){var r=d(e).getIn(["paths",t,n],(0,s.OrderedMap)()),o=e.getIn(["meta","paths",t,n],(0,s.OrderedMap)()),i=r.get("parameters",(0,s.List)()).map(function(r){return O(e,[t,n],r)});return(0,s.OrderedMap)().merge(r,o).set("parameters",i)});t.hasHost=(0,a.createSelector)(h,function(e){var t=e.get("host");return"string"==typeof t&&t.length>0&&"/"!==t[0]});function T(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(s.List.isList(e))return e.some(function(e){return s.Map.isMap(e)&&e.get("type")===t})}function M(e,t){t=t||[];var n=d(e).getIn(["paths"].concat((0,i.default)(t)),null);if(null!==n){var r=e.getIn(["meta","paths"].concat((0,i.default)(t),["produces_value"]),null),o=n.getIn(["produces",0],null);return r||o||"application/json"}}var I=t.operationScheme=function(e,t,n){var r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),o=Array.isArray(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||o||""};t.canExecuteScheme=function(e,t,n){return["http","https"].indexOf(I(e,t,n))>-1},t.validateBeforeExecute=function(e,t){t=t||[];var n=!0;return e.getIn(["meta","paths"].concat((0,i.default)(t),["parameters"]),(0,s.fromJS)([])).forEach(function(e){var t=e.get("errors");t&&t.count()&&(n=!1)}),n};function j(e){return s.Map.isMap(e)?e:new s.Map}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=t.executeRequest=t.logRequest=t.setMutatedRequest=t.setRequest=t.setResponse=t.updateEmptyParamInclusion=t.validateParams=t.invalidateResolvedSubtreeCache=t.updateResolvedSubtree=t.requestResolvedSubtree=t.resolveSpec=t.parseToJson=t.SET_SCHEME=t.UPDATE_RESOLVED_SUBTREE=t.UPDATE_RESOLVED=t.UPDATE_OPERATION_META_VALUE=t.CLEAR_VALIDATE_PARAMS=t.CLEAR_REQUEST=t.CLEAR_RESPONSE=t.LOG_REQUEST=t.SET_MUTATED_REQUEST=t.SET_REQUEST=t.SET_RESPONSE=t.VALIDATE_PARAMS=t.UPDATE_EMPTY_PARAM_INCLUSION=t.UPDATE_PARAM=t.UPDATE_JSON=t.UPDATE_URL=t.UPDATE_SPEC=void 0;var r=b(n(25)),o=b(n(84)),i=b(n(23)),a=b(n(42)),u=b(n(203)),s=b(n(339)),l=b(n(340)),c=b(n(45));t.updateSpec=function(e){var t=L(e).replace(/\t/g," ");if("string"==typeof e)return{type:_,payload:t}},t.updateResolved=function(e){return{type:N,payload:e}},t.updateUrl=function(e){return{type:w,payload:e}},t.updateJsonSpec=function(e){return{type:E,payload:e}},t.changeParam=function(e,t,n,r,o){return{type:x,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}},t.changeParamByIdentity=function(e,t,n,r){return{type:x,payload:{path:e,param:t,value:n,isXml:r}}},t.clearValidateParams=function(e){return{type:I,payload:{pathMethod:e}}},t.changeConsumesValue=function(e,t){return{type:j,payload:{path:e,value:t,key:"consumes_value"}}},t.changeProducesValue=function(e,t){return{type:j,payload:{path:e,value:t,key:"produces_value"}}},t.clearResponse=function(e,t){return{type:T,payload:{path:e,method:t}}},t.clearRequest=function(e,t){return{type:M,payload:{path:e,method:t}}},t.setScheme=function(e,t,n){return{type:D,payload:{scheme:e,path:t,method:n}}};var f=b(n(207)),p=n(7),d=b(n(209)),h=b(n(179)),v=b(n(343)),m=b(n(763)),y=b(n(765)),g=n(12);function b(e){return e&&e.__esModule?e:{default:e}}var _=t.UPDATE_SPEC="spec_update_spec",w=t.UPDATE_URL="spec_update_url",E=t.UPDATE_JSON="spec_update_json",x=t.UPDATE_PARAM="spec_update_param",S=t.UPDATE_EMPTY_PARAM_INCLUSION="spec_update_empty_param_inclusion",C=t.VALIDATE_PARAMS="spec_validate_param",k=t.SET_RESPONSE="spec_set_response",A=t.SET_REQUEST="spec_set_request",O=t.SET_MUTATED_REQUEST="spec_set_mutated_request",P=t.LOG_REQUEST="spec_log_request",T=t.CLEAR_RESPONSE="spec_clear_response",M=t.CLEAR_REQUEST="spec_clear_request",I=t.CLEAR_VALIDATE_PARAMS="spec_clear_validate_param",j=t.UPDATE_OPERATION_META_VALUE="spec_update_operation_meta_value",N=t.UPDATE_RESOLVED="spec_update_resolved",R=t.UPDATE_RESOLVED_SUBTREE="spec_update_resolved_subtree",D=t.SET_SCHEME="set_scheme",L=function(e){return(0,v.default)(e)?e:""};t.parseToJson=function(e){return function(t){var n=t.specActions,r=t.specSelectors,o=t.errActions,i=r.specStr,a=null;try{e=e||i(),o.clear({source:"parser"}),a=f.default.safeLoad(e)}catch(e){return console.error(e),o.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return a&&"object"===(void 0===a?"undefined":(0,c.default)(a))?n.updateJsonSpec(a):{}}};var U=!1,q=(t.resolveSpec=function(e,t){return function(n){var r=n.specActions,o=n.specSelectors,i=n.errActions,a=n.fn,u=a.fetch,s=a.resolve,l=a.AST,c=void 0===l?{}:l,f=n.getConfigs;U||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),U=!0);var p=f(),d=p.modelPropertyMacro,h=p.parameterMacro,v=p.requestInterceptor,m=p.responseInterceptor;void 0===e&&(e=o.specJson()),void 0===t&&(t=o.url());var y=c.getLineNumberForPath?c.getLineNumberForPath:function(){},g=o.specStr();return s({fetch:u,spec:e,baseDoc:t,modelPropertyMacro:d,parameterMacro:h,requestInterceptor:v,responseInterceptor:m}).then(function(e){var t=e.spec,n=e.errors;if(i.clear({type:"thrown"}),Array.isArray(n)&&n.length>0){var o=n.map(function(e){return console.error(e),e.line=e.fullPath?y(g,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e});i.newThrownErrBatch(o)}return r.updateResolved(t)})}},[]),F=(0,m.default)((0,l.default)(s.default.mark(function e(){var t,n,r,o,i,a,c,f,d,h,v,m,g,b,_,w,E;return s.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t=q.system){e.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),e.abrupt("return");case 4:if(n=t.errActions,r=t.errSelectors,o=t.fn,i=o.resolveSubtree,a=o.AST,c=void 0===a?{}:a,f=t.specSelectors,d=t.specActions,i){e.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),e.abrupt("return");case 8:return h=c.getLineNumberForPath?c.getLineNumberForPath:function(){},v=f.specStr(),m=t.getConfigs(),g=m.modelPropertyMacro,b=m.parameterMacro,_=m.requestInterceptor,w=m.responseInterceptor,e.prev=11,e.next=14,q.reduce(function(){var e=(0,l.default)(s.default.mark(function e(t,o){var a,u,l,c,p,d,m;return s.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t;case 2:return a=e.sent,u=a.resultMap,l=a.specWithCurrentSubtrees,e.next=7,i(l,o,{baseDoc:f.url(),modelPropertyMacro:g,parameterMacro:b,requestInterceptor:_,responseInterceptor:w});case 7:return c=e.sent,p=c.errors,d=c.spec,r.allErrors().size&&n.clear({type:"thrown"}),Array.isArray(p)&&p.length>0&&(m=p.map(function(e){return e.line=e.fullPath?h(v,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e}),n.newThrownErrBatch(m)),(0,y.default)(u,o,d),(0,y.default)(l,o,d),e.abrupt("return",{resultMap:u,specWithCurrentSubtrees:l});case 15:case"end":return e.stop()}},e,void 0)}));return function(t,n){return e.apply(this,arguments)}}(),u.default.resolve({resultMap:(f.specResolvedSubtree([])||(0,p.Map)()).toJS(),specWithCurrentSubtrees:f.specJson().toJS()}));case 14:E=e.sent,delete q.system,q=[],e.next=22;break;case 19:e.prev=19,e.t0=e.catch(11),console.error(e.t0);case 22:d.updateResolvedSubtree([],E.resultMap);case 23:case"end":return e.stop()}},e,void 0,[[11,19]])})),35);t.requestResolvedSubtree=function(e){return function(t){q.push(e),q.system=t,F()}};t.updateResolvedSubtree=function(e,t){return{type:R,payload:{path:e,value:t}}},t.invalidateResolvedSubtreeCache=function(){return{type:R,payload:{path:[],value:(0,p.Map)()}}},t.validateParams=function(e,t){return{type:C,payload:{pathMethod:e,isOAS3:t}}},t.updateEmptyParamInclusion=function(e,t,n,r){return{type:S,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}}};t.setResponse=function(e,t,n){return{payload:{path:e,method:t,res:n},type:k}},t.setRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:A}},t.setMutatedRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:O}},t.logRequest=function(e){return{payload:e,type:P}},t.executeRequest=function(e){return function(t){var n=t.fn,r=t.specActions,o=t.specSelectors,u=t.getConfigs,s=t.oas3Selectors,l=e.pathName,c=e.method,f=e.operation,p=u(),v=p.requestInterceptor,m=p.responseInterceptor,y=f.toJS();if(y&&y.parameters&&y.parameters.length&&y.parameters.filter(function(e){return e&&!0===e.allowEmptyValue}).forEach(function(t){if(o.parameterInclusionSettingFor([l,c],t.name,t.in)){e.parameters=e.parameters||{};var n=e.parameters[t.name];(!n||n&&0===n.size)&&(e.parameters[t.name]="")}}),e.contextUrl=(0,d.default)(o.url()).toString(),y&&y.operationId?e.operationId=y.operationId:y&&l&&c&&(e.operationId=n.opId(y,l,c)),o.isOAS3()){var b=l+":"+c;e.server=s.selectedServer(b)||s.selectedServer();var _=s.serverVariables({server:e.server,namespace:b}).toJS(),w=s.serverVariables({server:e.server}).toJS();e.serverVariables=(0,a.default)(_).length?_:w,e.requestContentType=s.requestContentType(l,c),e.responseContentType=s.responseContentType(l,c)||"*/*";var E=s.requestBodyValue(l,c);(0,g.isJSONObject)(E)?e.requestBody=JSON.parse(E):E&&E.toJS?e.requestBody=E.toJS():e.requestBody=E}var x=(0,i.default)({},e);x=n.buildRequest(x),r.setRequest(e.pathName,e.method,x);e.requestInterceptor=function(t){var n=v.apply(this,[t]),o=(0,i.default)({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=m;var S=Date.now();return n.execute(e).then(function(t){t.duration=Date.now()-S,r.setResponse(e.pathName,e.method,t)}).catch(function(t){return r.setResponse(e.pathName,e.method,{error:!0,err:(0,h.default)(t)})})}};t.execute=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,i=(0,o.default)(e,["path","method"]);return function(e){var o=e.fn.fetch,a=e.specSelectors,u=e.specActions,s=a.specJsonWithResolvedSubtrees().toJS(),l=a.operationScheme(t,n),c=a.contentTypeValues([t,n]).toJS(),f=c.requestContentType,p=c.responseContentType,d=/xml/i.test(f),h=a.parameterValues([t,n],d).toJS();return u.executeRequest((0,r.default)({},i,{fetch:o,spec:s,pathName:t,method:n,parameters:h,requestContentType:f,scheme:l,responseContentType:p}))}}},function(e,t,n){e.exports={default:n(732),__esModule:!0}},function(e,t){e.exports=function(e,t,n,r){if(!(e instanceof t)||void 0!==r&&r in e)throw TypeError(n+": incorrect invocation!");return e}},function(e,t,n){"use strict";var r=n(94);e.exports.f=function(e){return new function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=r(t),this.reject=r(n)}(e)}},function(e,t,n){var r=n(50);e.exports=function(e,t,n){for(var o in t)n&&e[o]?e[o]=t[o]:r(e,o,t[o]);return e}},function(e,t,n){"use strict";var r=n(741);e.exports=r},function(e,t,n){"use strict";var r=n(86);e.exports=new r({explicit:[n(744),n(745),n(746)]})},function(e,t,n){"use strict";(function(t){var r=n(761),o=n(762),i=/^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i,a=/^[A-Za-z][A-Za-z0-9+-.]*:\/\//,u=[["#","hash"],["?","query"],["/","pathname"],["@","auth",1],[NaN,"host",void 0,1,1],[/:(\d+)$/,"port",void 0,1],[NaN,"hostname",void 0,1,1]],s={hash:1,query:1};function l(e){var n,r={},o=typeof(e=e||t.location||{});if("blob:"===e.protocol)r=new f(unescape(e.pathname),{});else if("string"===o)for(n in r=new f(e,{}),s)delete r[n];else if("object"===o){for(n in e)n in s||(r[n]=e[n]);void 0===r.slashes&&(r.slashes=a.test(e.href))}return r}function c(e){var t=i.exec(e);return{protocol:t[1]?t[1].toLowerCase():"",slashes:!!t[2],rest:t[3]}}function f(e,t,n){if(!(this instanceof f))return new f(e,t,n);var i,a,s,p,d,h,v=u.slice(),m=typeof t,y=this,g=0;for("object"!==m&&"string"!==m&&(n=t,t=null),n&&"function"!=typeof n&&(n=o.parse),t=l(t),i=!(a=c(e||"")).protocol&&!a.slashes,y.slashes=a.slashes||i&&t.slashes,y.protocol=a.protocol||t.protocol||"",e=a.rest,a.slashes||(v[2]=[/(.*)/,"pathname"]);g-1||r("96",e),!l.plugins[n]){t.extractEvents||r("97",e),l.plugins[n]=t;var a=t.eventTypes;for(var s in a)u(a[s],t,s)||r("98",s,e)}}}function u(e,t,n){l.eventNameDispatchConfigs.hasOwnProperty(n)&&r("99",n),l.eventNameDispatchConfigs[n]=e;var o=e.phasedRegistrationNames;if(o){for(var i in o){if(o.hasOwnProperty(i))s(o[i],t,n)}return!0}return!!e.registrationName&&(s(e.registrationName,t,n),!0)}function s(e,t,n){l.registrationNameModules[e]&&r("100",e),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){o&&r("101"),o=Array.prototype.slice.call(e),a()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];i.hasOwnProperty(n)&&i[n]===o||(i[n]&&r("102",n),i[n]=o,t=!0)}t&&a()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var o=l.registrationNameModules[n[r]];if(o)return o}}return null},_resetEventPlugins:function(){for(var e in o=null,i)i.hasOwnProperty(e)&&delete i[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var a in r)r.hasOwnProperty(a)&&delete r[a]}};e.exports=l},function(e,t,n){"use strict";var r,o,i=n(10),a=n(212);n(8),n(9);function u(e,t,n,r){var o=e.type||"unknown-event";e.currentTarget=s.getNodeFromInstance(r),t?a.invokeGuardedCallbackWithCatch(o,n,e):a.invokeGuardedCallback(o,n,e),e.currentTarget=null}var s={isEndish:function(e){return"topMouseUp"===e||"topTouchEnd"===e||"topTouchCancel"===e},isMoveish:function(e){return"topMouseMove"===e||"topTouchMove"===e},isStartish:function(e){return"topMouseDown"===e||"topTouchStart"===e},executeDirectDispatch:function(e){var t=e._dispatchListeners,n=e._dispatchInstances;Array.isArray(t)&&i("103"),e.currentTarget=t?s.getNodeFromInstance(n):null;var r=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,r},executeDispatchesInOrder:function(e,t){var n=e._dispatchListeners,r=e._dispatchInstances;if(Array.isArray(n))for(var o=0;o0&&r.length<20?n+" (keys: "+r.join(", ")+")":n}function s(e,t){var n=o.get(e);return n||null}var l={isMounted:function(e){var t=o.get(e);return!!t&&!!t._renderedComponent},enqueueCallback:function(e,t,n){l.validateCallback(t,n);var r=s(e);if(!r)return null;r._pendingCallbacks?r._pendingCallbacks.push(t):r._pendingCallbacks=[t],a(r)},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],a(e)},enqueueForceUpdate:function(e){var t=s(e);t&&(t._pendingForceUpdate=!0,a(t))},enqueueReplaceState:function(e,t,n){var r=s(e);r&&(r._pendingStateQueue=[t],r._pendingReplaceState=!0,void 0!==n&&null!==n&&(l.validateCallback(n,"replaceState"),r._pendingCallbacks?r._pendingCallbacks.push(n):r._pendingCallbacks=[n]),a(r))},enqueueSetState:function(e,t){var n=s(e);n&&((n._pendingStateQueue||(n._pendingStateQueue=[])).push(t),a(n))},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,a(e)},validateCallback:function(e,t){e&&"function"!=typeof e&&r("122",t,u(e))}};e.exports=l},function(e,t,n){"use strict";n(13);var r=n(34),o=(n(9),r);e.exports=o},function(e,t,n){"use strict";e.exports=function(e){var t,n=e.keyCode;return"charCode"in e?0===(t=e.charCode)&&13===n&&(t=13):t=n,t>=32||13===t?t:0}},function(e,t,n){var r=n(61),o=n(228),i=n(47),a="[object Object]",u=Function.prototype,s=Object.prototype,l=u.toString,c=s.hasOwnProperty,f=l.call(Object);e.exports=function(e){if(!i(e)||r(e)!=a)return!1;var t=o(e);if(null===t)return!0;var n=c.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&l.call(n)==f}},function(e,t,n){var r=n(298)(Object.getPrototypeOf,Object);e.exports=r},function(e,t,n){var r=n(292);e.exports=function(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}},function(e,t){var n=this&&this.__extends||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);function r(){this.constructor=e}e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)},r=Object.prototype.hasOwnProperty; +/*! + * https://github.com/Starcounter-Jack/JSON-Patch + * (c) 2017 Joachim Wester + * MIT license + */function o(e,t){return r.call(e,t)}function i(e){if(Array.isArray(e)){for(var t=new Array(e.length),n=0;n=48&&t<=57))return!1;n++}return!0},t.escapePathComponent=a,t.unescapePathComponent=function(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")},t._getPathRecursive=u,t.getPath=function(e,t){if(e===t)return"/";var n=u(e,t);if(""===n)throw new Error("Object not found in root");return"/"+n},t.hasUndefined=function e(t){if(void 0===t)return!0;if(t)if(Array.isArray(t)){for(var n=0,r=t.length;nw;w++)if((p||w in g)&&(m=b(v=g[w],w,y),e))if(n)E[w]=m;else if(m)switch(e){case 3:return!0;case 5:return v;case 6:return w;case 2:E.push(v)}else if(c)return!1;return f?-1:l||c?c:E}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.authorizeRequest=t.authorizeAccessCodeWithBasicAuthentication=t.authorizeAccessCodeWithFormParams=t.authorizeApplication=t.authorizePassword=t.preAuthorizeImplicit=t.CONFIGURE_AUTH=t.VALIDATE=t.AUTHORIZE_OAUTH2=t.PRE_AUTHORIZE_OAUTH2=t.LOGOUT=t.AUTHORIZE=t.SHOW_AUTH_POPUP=void 0;var r=l(n(45)),o=l(n(23)),i=l(n(41));t.showDefinitions=function(e){return{type:c,payload:e}},t.authorize=function(e){return{type:f,payload:e}},t.logout=function(e){return{type:p,payload:e}},t.authorizeOauth2=function(e){return{type:d,payload:e}},t.configureAuth=function(e){return{type:h,payload:e}};var a=l(n(209)),u=l(n(32)),s=n(12);function l(e){return e&&e.__esModule?e:{default:e}}var c=t.SHOW_AUTH_POPUP="show_popup",f=t.AUTHORIZE="authorize",p=t.LOGOUT="logout",d=(t.PRE_AUTHORIZE_OAUTH2="pre_authorize_oauth2",t.AUTHORIZE_OAUTH2="authorize_oauth2"),h=(t.VALIDATE="validate",t.CONFIGURE_AUTH="configure_auth");t.preAuthorizeImplicit=function(e){return function(t){var n=t.authActions,r=t.errActions,o=e.auth,a=e.token,s=e.isValid,l=o.schema,c=o.name,f=l.get("flow");delete u.default.swaggerUIRedirectOauth2,"accessCode"===f||s||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),a.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:(0,i.default)(a)}):n.authorizeOauth2({auth:o,token:a})}};t.authorizePassword=function(e){return function(t){var n=t.authActions,r=e.schema,i=e.name,a=e.username,u=e.password,l=e.passwordType,c=e.clientId,f=e.clientSecret,p={grant_type:"password",scope:e.scopes.join(" ")},d={},h={};return"basic"===l?h.Authorization="Basic "+(0,s.btoa)(a+":"+u):((0,o.default)(p,{username:a},{password:u}),"query"===l?(c&&(d.client_id=c),f&&(d.client_secret=f)):h.Authorization="Basic "+(0,s.btoa)(c+":"+f)),n.authorizeRequest({body:(0,s.buildFormData)(p),url:r.get("tokenUrl"),name:i,headers:h,query:d,auth:e})}},t.authorizeApplication=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.scopes,i=e.name,a=e.clientId,u=e.clientSecret,l={Authorization:"Basic "+(0,s.btoa)(a+":"+u)},c={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:(0,s.buildFormData)(c),name:i,url:r.get("tokenUrl"),auth:e,headers:l})}},t.authorizeAccessCodeWithFormParams=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,l={grant_type:"authorization_code",code:t.code,client_id:a,client_secret:u,redirect_uri:n};return r.authorizeRequest({body:(0,s.buildFormData)(l),name:i,url:o.get("tokenUrl"),auth:t})}},t.authorizeAccessCodeWithBasicAuthentication=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,l={Authorization:"Basic "+(0,s.btoa)(a+":"+u)},c={grant_type:"authorization_code",code:t.code,client_id:a,redirect_uri:n};return r.authorizeRequest({body:(0,s.buildFormData)(c),name:i,url:o.get("tokenUrl"),auth:t,headers:l})}},t.authorizeRequest=function(e){return function(t){var n=t.fn,u=t.getConfigs,s=t.authActions,l=t.errActions,c=t.oas3Selectors,f=t.specSelectors,p=t.authSelectors,d=e.body,h=e.query,v=void 0===h?{}:h,m=e.headers,y=void 0===m?{}:m,g=e.name,b=e.url,_=e.auth,w=(p.getConfigs()||{}).additionalQueryStringParams,E=void 0;E=f.isOAS3()?(0,a.default)(b,c.selectedServer(),!0):(0,a.default)(b,f.url(),!0),"object"===(void 0===w?"undefined":(0,r.default)(w))&&(E.query=(0,o.default)({},E.query,w));var x=E.toString(),S=(0,o.default)({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded"},y);n.fetch({url:x,method:"post",headers:S,query:v,body:d,requestInterceptor:u().requestInterceptor,responseInterceptor:u().responseInterceptor}).then(function(e){var t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?l.newAuthErr({authId:g,level:"error",source:"auth",message:(0,i.default)(t)}):s.authorizeOauth2({auth:_,token:t}):l.newAuthErr({authId:g,level:"error",source:"auth",message:e.statusText})}).catch(function(e){var t=new Error(e).message;if(e.response&&e.response.data){var n=e.response.data;try{var r="string"==typeof n?JSON.parse(n):n;r.error&&(t+=", error: "+r.error),r.error_description&&(t+=", description: "+r.error_description)}catch(e){}}l.newAuthErr({authId:g,level:"error",source:"auth",message:t})})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseYamlConfig=void 0;var r,o=n(207),i=(r=o)&&r.__esModule?r:{default:r};t.parseYamlConfig=function(e,t){try{return i.default.safeLoad(e)}catch(e){return t&&t.errActions.newThrownErr(new Error(e)),{}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.loaded=t.TOGGLE_CONFIGS=t.UPDATE_CONFIGS=void 0;var r,o=n(22),i=(r=o)&&r.__esModule?r:{default:r};t.update=function(e,t){return{type:a,payload:(0,i.default)({},e,t)}},t.toggle=function(e){return{type:u,payload:e}};var a=t.UPDATE_CONFIGS="configs_update",u=t.TOGGLE_CONFIGS="configs_toggle";t.loaded=function(){return function(){}}},function(e,t,n){"use strict";function r(e,t,n,r,o){this.src=e,this.env=r,this.options=n,this.parser=t,this.tokens=o,this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache=[],this.isInLabel=!1,this.linkLevel=0,this.linkContent="",this.labelUnmatchedScopes=0}r.prototype.pushPending=function(){this.tokens.push({type:"text",content:this.pending,level:this.pendingLevel}),this.pending=""},r.prototype.push=function(e){this.pending&&this.pushPending(),this.tokens.push(e),this.pendingLevel=this.level},r.prototype.cacheSet=function(e,t){for(var n=this.cache.length;n<=e;n++)this.cache.push(0);this.cache[e]=t},r.prototype.cacheGet=function(e){return es;)r(u,n=t[s++])&&(~i(l,n)||l.push(n));return l}},function(e,t,n){var r=n(21).document;e.exports=r&&r.documentElement},function(e,t,n){var r=n(52),o=n(71),i=n(161)("IE_PROTO"),a=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=o(e),r(e,i)?e[i]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,n){var r=n(33),o=r["__core-js_shared__"]||(r["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){"use strict";var r=n(245)(!0);n(246)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,n=this._i;return n>=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var r=n(119),o=n(53);e.exports=function(e){return function(t,n){var i,a,u=String(o(t)),s=r(n),l=u.length;return s<0||s>=l?e?"":void 0:(i=u.charCodeAt(s))<55296||i>56319||s+1===l||(a=u.charCodeAt(s+1))<56320||a>57343?e?u.charAt(s):i:e?u.slice(s,s+2):a-56320+(i-55296<<10)+65536}}},function(e,t,n){"use strict";var r=n(247),o=n(29),i=n(72),a=n(58),u=n(102),s=n(462),l=n(170),c=n(468),f=n(17)("iterator"),p=!([].keys&&"next"in[].keys()),d=function(){return this};e.exports=function(e,t,n,h,v,m,y){s(n,t,h);var g,b,_,w=function(e){if(!p&&e in C)return C[e];switch(e){case"keys":case"values":return function(){return new n(this,e)}}return function(){return new n(this,e)}},E=t+" Iterator",x="values"==v,S=!1,C=e.prototype,k=C[f]||C["@@iterator"]||v&&C[v],A=k||w(v),O=v?x?w("entries"):A:void 0,P="Array"==t&&C.entries||k;if(P&&(_=c(P.call(new e)))!==Object.prototype&&_.next&&(l(_,E,!0),r||"function"==typeof _[f]||a(_,f,d)),x&&k&&"values"!==k.name&&(S=!0,A=function(){return k.call(this)}),r&&!y||!p&&!S&&C[f]||a(C,f,A),u[t]=A,u[E]=d,v)if(g={values:x?A:w("values"),keys:m?A:w("keys"),entries:O},y)for(b in g)b in C||i(C,b,g[b]);else o(o.P+o.F*(p||S),t,g);return g}},function(e,t){e.exports=!1},function(e,t,n){var r=n(465),o=n(250);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t,n){var r=n(119),o=Math.max,i=Math.min;e.exports=function(e,t){return(e=r(e))<0?o(e+t,0):i(e,t)}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){var r=n(33).document;e.exports=r&&r.documentElement},function(e,t,n){var r=n(59),o=n(121),i=n(17)("species");e.exports=function(e,t){var n,a=r(e).constructor;return void 0===a||void 0==(n=r(a)[i])?t:o(n)}},function(e,t,n){var r,o,i,a=n(120),u=n(480),s=n(251),l=n(168),c=n(33),f=c.process,p=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,v=c.Dispatch,m=0,y={},g=function(){var e=+this;if(y.hasOwnProperty(e)){var t=y[e];delete y[e],t()}},b=function(e){g.call(e.data)};p&&d||(p=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return y[++m]=function(){u("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete y[e]},"process"==n(99)(f)?r=function(e){f.nextTick(a(g,e,1))}:v&&v.now?r=function(e){v.now(a(g,e,1))}:h?(i=(o=new h).port2,o.port1.onmessage=b,r=a(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(r=function(e){c.postMessage(e+"","*")},c.addEventListener("message",b,!1)):r="onreadystatechange"in l("script")?function(e){s.appendChild(l("script")).onreadystatechange=function(){s.removeChild(this),g.call(e)}}:function(e){setTimeout(a(g,e,1),0)}),e.exports={set:p,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,n){var r=n(59),o=n(73),i=n(171);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){var r=n(73),o=n(99),i=n(17)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[i])?!!t:"RegExp"==o(e))}},function(e,t,n){var r=n(20),o=n(15),i=n(51);e.exports=function(e,t){var n=(o.Object||{})[e]||Object[e],a={};a[e]=t(n),r(r.S+r.F*i(function(){n(1)}),"Object",a)}},function(e,t,n){var r=n(93);e.exports=Array.isArray||function(e){return"Array"==r(e)}},function(e,t,n){var r=n(239),o=n(163).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,o)}},function(e,t,n){var r=n(125),o=n(95),i=n(70),a=n(157),u=n(52),s=n(238),l=Object.getOwnPropertyDescriptor;t.f=n(44)?l:function(e,t){if(e=i(e),t=a(t,!0),s)try{return l(e,t)}catch(e){}if(u(e,t))return o(!r.f.call(e,t),e[t])}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){e.exports={default:n(532),__esModule:!0}},function(e,t,n){"use strict";var r=n(96),o=n(176),i=n(125),a=n(71),u=n(154),s=Object.assign;e.exports=!s||n(51)(function(){var e={},t={},n=Symbol(),r="abcdefghijklmnopqrst";return e[n]=7,r.split("").forEach(function(e){t[e]=e}),7!=s({},e)[n]||Object.keys(s({},t)).join("")!=r})?function(e,t){for(var n=a(e),s=arguments.length,l=1,c=o.f,f=i.f;s>l;)for(var p,d=u(arguments[l++]),h=c?r(d).concat(c(d)):r(d),v=h.length,m=0;v>m;)f.call(d,p=h[m++])&&(n[p]=d[p]);return n}:s},function(e,t,n){"use strict";var r=n(104),o=n(13),i=n(265),a=(n(266),n(126));n(8),n(536);function u(e,t,n){this.props=e,this.context=t,this.refs=a,this.updater=n||i}function s(e,t,n){this.props=e,this.context=t,this.refs=a,this.updater=n||i}function l(){}u.prototype.isReactComponent={},u.prototype.setState=function(e,t){"object"!=typeof e&&"function"!=typeof e&&null!=e&&r("85"),this.updater.enqueueSetState(this,e),t&&this.updater.enqueueCallback(this,t,"setState")},u.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this),e&&this.updater.enqueueCallback(this,e,"forceUpdate")},l.prototype=u.prototype,s.prototype=new l,s.prototype.constructor=s,o(s.prototype,u.prototype),s.prototype.isPureReactComponent=!0,e.exports={Component:u,PureComponent:s}},function(e,t,n){"use strict";n(9);var r={isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){},enqueueReplaceState:function(e,t){},enqueueSetState:function(e,t){}};e.exports=r},function(e,t,n){"use strict";var r=!1;e.exports=r},function(e,t,n){"use strict";var r="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;e.exports=r},function(e,t,n){"use strict";var r=n(544);e.exports=function(e){return r(e,!1)}},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(271),o=n(561),i=n(562),a=n(563),u=n(275);n(274);n.d(t,"createStore",function(){return r.b}),n.d(t,"combineReducers",function(){return o.a}),n.d(t,"bindActionCreators",function(){return i.a}),n.d(t,"applyMiddleware",function(){return a.a}),n.d(t,"compose",function(){return u.a})},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),t.b=function e(t,n,a){var u;"function"==typeof n&&void 0===a&&(a=n,n=void 0);if(void 0!==a){if("function"!=typeof a)throw new Error("Expected the enhancer to be a function.");return a(e)(t,n)}if("function"!=typeof t)throw new Error("Expected the reducer to be a function.");var s=t;var l=n;var c=[];var f=c;var p=!1;function d(){f===c&&(f=c.slice())}function h(){return l}function v(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return d(),f.push(e),function(){if(t){t=!1,d();var n=f.indexOf(e);f.splice(n,1)}}}function m(e){if(!r.a(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(p)throw new Error("Reducers may not dispatch actions.");try{p=!0,l=s(l,e)}finally{p=!1}for(var t=c=f,n=0;no?0:o+t),(n=n>o?o:n)<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var i=Array(o);++rp))return!1;var h=c.get(e);if(h&&c.get(t))return h==t;var v=-1,m=!0,y=n&u?new r:void 0;for(c.set(e,t),c.set(t,e);++v0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===l.prototype||(t=function(e){return l.from(e)}(t)),r?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):w(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!n?(t=a.decoder.write(t),a.objectMode||0!==t.length?w(e,a,t,!1):k(e,a)):w(e,a,t,!1))):r||(a.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=E?e=E:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(d("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?o.nextTick(C,e):C(e))}function C(e){d("emit readable"),e.emit("readable"),T(e)}function k(e,t){t.readingMore||(t.readingMore=!0,o.nextTick(A,e,t))}function A(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):n=function(e,t,n){var r;ei.length?i.length:e;if(a===i.length?o+=i:o+=i.slice(0,e),0===(e-=a)){a===i.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(a));break}++r}return t.length-=r,o}(e,t):function(e,t){var n=l.allocUnsafe(e),r=t.head,o=1;r.data.copy(n),e-=r.data.length;for(;r=r.next;){var i=r.data,a=e>i.length?i.length:e;if(i.copy(n,n.length-e,0,a),0===(e-=a)){a===i.length?(++o,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(a));break}++o}return t.length-=o,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function I(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,o.nextTick(j,t,e))}function j(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function N(e,t){for(var n=0,r=e.length;n=t.highWaterMark||t.ended))return d("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?I(this):S(this),null;if(0===(e=x(e,t))&&t.ended)return 0===t.length&&I(this),null;var r,o=t.needReadable;return d("need readable",o),(0===t.length||t.length-e0?M(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&I(this)),null!==r&&this.emit("data",r),r},b.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},b.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,d("pipe count=%d opts=%j",i.pipesCount,t);var s=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?c:b;function l(t,r){d("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,d("cleanup"),e.removeListener("close",y),e.removeListener("finish",g),e.removeListener("drain",f),e.removeListener("error",m),e.removeListener("unpipe",l),n.removeListener("end",c),n.removeListener("end",b),n.removeListener("data",v),p=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function c(){d("onend"),e.end()}i.endEmitted?o.nextTick(s):n.once("end",s),e.on("unpipe",l);var f=function(e){return function(){var t=e._readableState;d("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&u(e,"data")&&(t.flowing=!0,T(e))}}(n);e.on("drain",f);var p=!1;var h=!1;function v(t){d("ondata"),h=!1,!1!==e.write(t)||h||((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==N(i.pipes,e))&&!p&&(d("false write response, pause",n._readableState.awaitDrain),n._readableState.awaitDrain++,h=!0),n.pause())}function m(t){d("onerror",t),b(),e.removeListener("error",m),0===u(e,"error")&&e.emit("error",t)}function y(){e.removeListener("finish",g),b()}function g(){d("onfinish"),e.removeListener("close",y),b()}function b(){d("unpipe"),n.unpipe(e)}return n.on("data",v),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",m),e.once("close",y),e.once("finish",g),e.emit("pipe",n),i.flowing||(d("pipe resume"),n.resume()),e},b.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n),this);if(!e){var r=t.pipes,o=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var i=0;i=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(662),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(31))},function(e,t,n){"use strict";var r=n(141).Buffer,o=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function i(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===o||!o(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=s,this.end=l,t=4;break;case"utf8":this.fillLast=u,t=4;break;case"base64":this.text=c,this.end=f,t=3;break;default:return this.write=p,void(this.end=d)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function u(e){var t=this.lastTotal-this.lastNeed,n=function(e,t,n){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function s(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function l(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function c(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function f(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function p(e){return e.toString(this.encoding)}function d(e){return e&&e.length?this.write(e):""}t.StringDecoder=i,i.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n=0)return o>0&&(e.lastNeed=o-1),o;if(--r=0)return o>0&&(e.lastNeed=o-2),o;if(--r=0)return o>0&&(2===o?o=0:e.lastNeed=o-3),o;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},i.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,n){"use strict";e.exports=i;var r=n(64),o=n(106);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e),this._transformState={afterTransform:function(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(!r)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var o=this._readableState;o.reading=!1,(o.needReadable||o.length=0?n&&o?o-1:o:1:!1!==e&&r(e)}},function(e,t,n){"use strict";e.exports=n(678)()?Object.assign:n(679)},function(e,t,n){"use strict";var r,o,i,a,u,s=n(66),l=function(e,t){return t};try{Object.defineProperty(l,"length",{configurable:!0,writable:!1,enumerable:!1,value:1})}catch(e){}1===l.length?(r={configurable:!0,writable:!1,enumerable:!1},o=Object.defineProperty,e.exports=function(e,t){return t=s(t),e.length===t?e:(r.value=t,o(e,"length",r))}):(a=n(317),u=[],i=function(e){var t,n=0;if(u[e])return u[e];for(t=[];e--;)t.push("a"+(++n).toString(36));return new Function("fn","return function ("+t.join(", ")+") { return fn.apply(this, arguments); };")},e.exports=function(e,t){var n;if(t=s(t),e.length===t)return e;n=i(t)(e);try{a(n,e)}catch(e){}return n})},function(e,t,n){"use strict";var r=n(82),o=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,u=Object.getOwnPropertySymbols;e.exports=function(e,t){var n,s=Object(r(t));if(e=Object(r(e)),a(s).forEach(function(r){try{o(e,r,i(t,r))}catch(e){n=e}}),"function"==typeof u&&u(s).forEach(function(r){try{o(e,r,i(t,r))}catch(e){n=e}}),void 0!==n)throw n;return e}},function(e,t,n){"use strict";var r=n(56),o=n(142),i=Function.prototype.call;e.exports=function(e,t){var n={},a=arguments[2];return r(t),o(e,function(e,r,o,u){n[r]=i.call(t,a,e,r,o,u)}),n}},function(e,t){e.exports=function(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return{statePlugins:{err:{reducers:(0,i.default)(e),actions:a,selectors:u}}}};var r,o=n(321),i=(r=o)&&r.__esModule?r:{default:r},a=s(n(127)),u=s(n(326));function s(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=s(n(22)),o=s(n(23));t.default=function(e){var t;return t={},(0,r.default)(t,i.NEW_THROWN_ERR,function(t,n){var r=n.payload,i=(0,o.default)(l,r,{type:"thrown"});return t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(i))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_THROWN_ERR_BATCH,function(t,n){var r=n.payload;return r=r.map(function(e){return(0,a.fromJS)((0,o.default)(l,e,{type:"thrown"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(r))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_SPEC_ERR,function(t,n){var r=n.payload,o=(0,a.fromJS)(r);return o=o.set("type","spec"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(o)).sortBy(function(e){return e.get("line")})}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_SPEC_ERR_BATCH,function(t,n){var r=n.payload;return r=r.map(function(e){return(0,a.fromJS)((0,o.default)(l,e,{type:"spec"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(r))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_AUTH_ERR,function(t,n){var r=n.payload,i=(0,a.fromJS)((0,o.default)({},r));return i=i.set("type","auth"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(i))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.CLEAR,function(e,t){var n=t.payload;if(!n||!e.get("errors"))return e;var r=e.get("errors").filter(function(e){return e.keySeq().every(function(t){var r=e.get(t),o=n[t];return!o||r!==o})});return e.merge({errors:r})}),(0,r.default)(t,i.CLEAR_BY,function(e,t){var n=t.payload;if(!n||"function"!=typeof n)return e;var r=e.get("errors").filter(function(e){return n(e)});return e.merge({errors:r})}),t};var i=n(127),a=n(7),u=s(n(322));function s(e){return e&&e.__esModule?e:{default:e}}var l={line:0,level:"error",message:"Unknown error"}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n={jsSpec:t.specSelectors.specJson().toJS()};return(0,i.default)(u,function(e,t){try{var r=t.transform(e,n);return r.filter(function(e){return!!e})}catch(t){return console.error("Transformer error:",t),e}},e).filter(function(e){return!!e}).map(function(e){return!e.get("line")&&e.get("path"),e})};var r,o=n(726),i=(r=o)&&r.__esModule?r:{default:r};function a(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var u=[a(n(323)),a(n(324)),a(n(325))]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function(e){return e.map(function(e){var t=e.get("message").indexOf("is not of a type(s)");if(t>-1){var n=e.get("message").slice(t+"is not of a type(s)".length).split(",");return e.set("message",e.get("message").slice(0,t)+function(e){return e.reduce(function(e,t,n,r){return n===r.length-1&&r.length>1?e+"or "+t:r[n+1]&&r.length>2?e+t+", ":r[n+1]?e+t+" ":e+t},"should be a")}(n))}return e})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function(e,t){t.jsSpec;return e};var r,o=n(138);(r=o)&&r.__esModule,n(7)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function(e){return e.map(function(e){return e.set("message",(t=e.get("message"),n="instance.",t.replace(new RegExp(n,"g"),"")));var t,n})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.lastError=t.allErrors=void 0;var r=n(7),o=n(57),i=t.allErrors=(0,o.createSelector)(function(e){return e},function(e){return e.get("errors",(0,r.List)())});t.lastError=(0,o.createSelector)(i,function(e){return e.last()})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{layout:{reducers:i.default,actions:a,selectors:u}}}};var r,o=n(328),i=(r=o)&&r.__esModule?r:{default:r},a=s(n(200)),u=s(n(329));function s(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o,i=n(22),a=(r=i)&&r.__esModule?r:{default:r},u=n(7),s=n(200);t.default=(o={},(0,a.default)(o,s.UPDATE_LAYOUT,function(e,t){return e.set("layout",t.payload)}),(0,a.default)(o,s.UPDATE_FILTER,function(e,t){return e.set("filter",t.payload)}),(0,a.default)(o,s.SHOW,function(e,t){var n=t.payload.shown,r=(0,u.fromJS)(t.payload.thing);return e.update("shown",(0,u.fromJS)({}),function(e){return e.set(r,n)})}),(0,a.default)(o,s.UPDATE_MODE,function(e,t){var n=t.payload.thing,r=t.payload.mode;return e.setIn(["modes"].concat(n),(r||"")+"")}),o)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.showSummary=t.whatMode=t.isShown=t.currentFilter=t.current=void 0;var r,o=n(83),i=(r=o)&&r.__esModule?r:{default:r},a=n(57),u=n(12),s=n(7);t.current=function(e){return e.get("layout")},t.currentFilter=function(e){return e.get("filter")};var l=t.isShown=function(e,t,n){return t=(0,u.normalizeArray)(t),e.get("shown",(0,s.fromJS)({})).get((0,s.fromJS)(t),n)};t.whatMode=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return t=(0,u.normalizeArray)(t),e.getIn(["modes"].concat((0,i.default)(t)),n)},t.showSummary=(0,a.createSelector)(function(e){return e},function(e){return!l(e,"editor")})},function(e,t,n){var r=n(36);e.exports=function(e,t,n,o){try{return o?t(r(n)[0],n[1]):t(n)}catch(t){var i=e.return;throw void 0!==i&&r(i.call(e)),t}}},function(e,t,n){var r=n(69),o=n(19)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(r.Array===e||i[o]===e)}},function(e,t,n){var r=n(19)("iterator"),o=!1;try{var i=[7][r]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var n=!1;try{var i=[7],a=i[r]();a.next=function(){return{done:n=!0}},i[r]=function(){return a},e(i)}catch(e){}return n}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{spec:{wrapActions:s,reducers:i.default,actions:a,selectors:u}}}};var r,o=n(334),i=(r=o)&&r.__esModule?r:{default:r},a=l(n(202)),u=l(n(201)),s=l(n(347));function l(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=p(n(22)),i=p(n(23)),a=p(n(83)),u=n(7),s=n(12),l=p(n(32)),c=n(201),f=n(202);function p(e){return e&&e.__esModule?e:{default:e}}t.default=(r={},(0,o.default)(r,f.UPDATE_SPEC,function(e,t){return"string"==typeof t.payload?e.set("spec",t.payload):e}),(0,o.default)(r,f.UPDATE_URL,function(e,t){return e.set("url",t.payload+"")}),(0,o.default)(r,f.UPDATE_JSON,function(e,t){return e.set("json",(0,s.fromJSOrdered)(t.payload))}),(0,o.default)(r,f.UPDATE_RESOLVED,function(e,t){return e.setIn(["resolved"],(0,s.fromJSOrdered)(t.payload))}),(0,o.default)(r,f.UPDATE_RESOLVED_SUBTREE,function(e,t){var n=t.payload,r=n.value,o=n.path;return e.setIn(["resolvedSubtrees"].concat((0,a.default)(o)),(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.UPDATE_PARAM,function(e,t){var n=t.payload,r=n.path,o=n.paramName,i=n.paramIn,u=n.param,s=n.value,l=n.isXml,c=void 0;c=u&&u.hashCode&&!i&&!o?u.get("name")+"."+u.get("in")+".hash-"+u.hashCode():o+"."+i;var f=l?"value_xml":"value";return e.setIn(["meta","paths"].concat((0,a.default)(r),["parameters",c,f]),s)}),(0,o.default)(r,f.UPDATE_EMPTY_PARAM_INCLUSION,function(e,t){var n=t.payload,r=n.pathMethod,o=n.paramName,i=n.paramIn,u=n.includeEmptyValue;if(!o||!i)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),e;var s=o+"."+i;return e.setIn(["meta","paths"].concat((0,a.default)(r),["parameter_inclusions",s]),u)}),(0,o.default)(r,f.VALIDATE_PARAMS,function(e,t){var n=t.payload,r=n.pathMethod,o=n.isOAS3,i=e.getIn(["meta","paths"].concat((0,a.default)(r)),(0,u.fromJS)({})),l=/xml/i.test(i.get("consumes_value")),f=c.operationWithMeta.apply(void 0,[e].concat((0,a.default)(r)));return e.updateIn(["meta","paths"].concat((0,a.default)(r),["parameters"]),(0,u.fromJS)({}),function(e){return f.get("parameters",(0,u.List)()).reduce(function(e,t){var n=(0,s.validateParam)(t,l,o);return e.setIn([t.get("name")+"."+t.get("in"),"errors"],(0,u.fromJS)(n))},e)})}),(0,o.default)(r,f.CLEAR_VALIDATE_PARAMS,function(e,t){var n=t.payload.pathMethod;return e.updateIn(["meta","paths"].concat((0,a.default)(n),["parameters"]),(0,u.fromJS)([]),function(e){return e.map(function(e){return e.set("errors",(0,u.fromJS)([]))})})}),(0,o.default)(r,f.SET_RESPONSE,function(e,t){var n=t.payload,r=n.res,o=n.path,a=n.method,u=void 0;(u=r.error?(0,i.default)({error:!0,name:r.err.name,message:r.err.message,statusCode:r.err.statusCode},r.err.response):r).headers=u.headers||{};var c=e.setIn(["responses",o,a],(0,s.fromJSOrdered)(u));return l.default.Blob&&r.data instanceof l.default.Blob&&(c=c.setIn(["responses",o,a,"text"],r.data)),c}),(0,o.default)(r,f.SET_REQUEST,function(e,t){var n=t.payload,r=n.req,o=n.path,i=n.method;return e.setIn(["requests",o,i],(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.SET_MUTATED_REQUEST,function(e,t){var n=t.payload,r=n.req,o=n.path,i=n.method;return e.setIn(["mutatedRequests",o,i],(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.UPDATE_OPERATION_META_VALUE,function(e,t){var n=t.payload,r=n.path,o=n.value,i=n.key,s=["paths"].concat((0,a.default)(r)),l=["meta","paths"].concat((0,a.default)(r));return e.getIn(["json"].concat((0,a.default)(s)))||e.getIn(["resolved"].concat((0,a.default)(s)))||e.getIn(["resolvedSubtrees"].concat((0,a.default)(s)))?e.setIn([].concat((0,a.default)(l),[i]),(0,u.fromJS)(o)):e}),(0,o.default)(r,f.CLEAR_RESPONSE,function(e,t){var n=t.payload,r=n.path,o=n.method;return e.deleteIn(["responses",r,o])}),(0,o.default)(r,f.CLEAR_REQUEST,function(e,t){var n=t.payload,r=n.path,o=n.method;return e.deleteIn(["requests",r,o])}),(0,o.default)(r,f.SET_SCHEME,function(e,t){var n=t.payload,r=n.scheme,o=n.path,i=n.method;return o&&i?e.setIn(["scheme",o,i],r):o||i?void 0:e.setIn(["scheme","_defaultScheme"],r)}),r)},function(e,t,n){var r=n(36),o=n(94),i=n(19)("species");e.exports=function(e,t){var n,a=r(e).constructor;return void 0===a||void 0==(n=r(a)[i])?t:o(n)}},function(e,t,n){var r,o,i,a=n(49),u=n(734),s=n(240),l=n(156),c=n(21),f=c.process,p=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,v=c.Dispatch,m=0,y={},g=function(){var e=+this;if(y.hasOwnProperty(e)){var t=y[e];delete y[e],t()}},b=function(e){g.call(e.data)};p&&d||(p=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return y[++m]=function(){u("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete y[e]},"process"==n(93)(f)?r=function(e){f.nextTick(a(g,e,1))}:v&&v.now?r=function(e){v.now(a(g,e,1))}:h?(i=(o=new h).port2,o.port1.onmessage=b,r=a(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(r=function(e){c.postMessage(e+"","*")},c.addEventListener("message",b,!1)):r="onreadystatechange"in l("script")?function(e){s.appendChild(l("script")).onreadystatechange=function(){s.removeChild(this),g.call(e)}}:function(e){setTimeout(a(g,e,1),0)}),e.exports={set:p,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,n){var r=n(36),o=n(28),i=n(205);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){e.exports=n(739)},function(e,t,n){"use strict";t.__esModule=!0;var r,o=n(203),i=(r=o)&&r.__esModule?r:{default:r};t.default=function(e){return function(){var t=e.apply(this,arguments);return new i.default(function(e,n){return function r(o,a){try{var u=t[o](a),s=u.value}catch(e){return void n(e)}if(!u.done)return i.default.resolve(s).then(function(e){r("next",e)},function(e){r("throw",e)});e(s)}("next")})}}},function(e,t,n){"use strict";var r=n(86);e.exports=new r({include:[n(342)]})},function(e,t,n){"use strict";var r=n(86);e.exports=new r({include:[n(208)],implicit:[n(747),n(748),n(749),n(750)]})},function(e,t,n){var r=n(61),o=n(24),i=n(47),a="[object String]";e.exports=function(e){return"string"==typeof e||!o(e)&&i(e)&&r(e)==a}},function(e,t,n){var r=n(146),o=n(79),i=n(135),a=n(38),u=n(80);e.exports=function(e,t,n,s){if(!a(e))return e;for(var l=-1,c=(t=o(t,e)).length,f=c-1,p=e;null!=p&&++l.":"function"==typeof t?" Instead of passing a class like Foo, pass React.createElement(Foo) or .":null!=t&&void 0!==t.props?" This may be caused by unintentionally loading two independent copies of React.":"");var i,u=a.createElement(D,{child:t});if(e){var s=p.get(e);i=s._processChildContext(s._context)}else i=y;var l=N(n);if(l){var c=l._currentElement.props.child;if(_(c,t)){var f=l._renderedComponent.getPublicInstance(),d=o&&function(){o.call(f)};return L._updateRootComponent(l,u,i,n,d),f}L.unmountComponentAtNode(n)}var h=A(n),m=h&&!!O(h),g=I(n),b=m&&!l&&!g,w=L._renderNewRootComponent(u,n,b,i)._renderedComponent.getPublicInstance();return o&&o.call(w),w},render:function(e,t,n){return L._renderSubtreeIntoContainer(null,e,t,n)},unmountComponentAtNode:function(e){j(e)||r("40");var t=N(e);if(!t){I(e),1===e.nodeType&&e.hasAttribute(E);return!1}return delete k[t._instance.rootID],m.batchedUpdates(M,t,e,!1),!0},_mountImageIntoNode:function(e,t,n,i,a){if(j(t)||r("41"),i){var u=A(t);if(d.canReuseMarkup(e,u))return void s.precacheNode(n,u);var l=u.getAttribute(d.CHECKSUM_ATTR_NAME);u.removeAttribute(d.CHECKSUM_ATTR_NAME);var c=u.outerHTML;u.setAttribute(d.CHECKSUM_ATTR_NAME,l);var f=e,p=function(e,t){for(var n=Math.min(e.length,t.length),r=0;r1?r-1:0),a=1;a=o&&(t=console)[e].apply(t,i)}return i.warn=i.bind(null,"warn"),i.error=i.bind(null,"error"),i.info=i.bind(null,"info"),i.debug=i.bind(null,"debug"),{rootInjects:{log:i}}}},function(e,t,n){"use strict";var r,o=n(388),i=(r=o)&&r.__esModule?r:{default:r};e.exports=function(e){var t=e.configs;return{fn:{fetch:i.default.makeHttp(t.preFetch,t.postFetch),buildRequest:i.default.buildRequest,execute:i.default.execute,resolve:i.default.resolve,resolveSubtree:i.default.resolveSubtree,serializeRes:i.default.serializeRes,opId:i.default.helpers.opId}}}},function(e,t,n){e.exports=function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=23)}([function(e,t){e.exports=n(42)},function(e,t){e.exports=n(45)},function(e,t){e.exports=n(23)},function(e,t){e.exports=n(25)},function(e,t){e.exports=n(339)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",r=(arguments.length>3&&void 0!==arguments[3]?arguments[3]:{}).v2OperationIdCompatibilityMode;return e&&"object"===(void 0===e?"undefined":(0,c.default)(e))?(e.operationId||"").replace(/\s/g,"").length?h(e.operationId):i(t,n,{v2OperationIdCompatibilityMode:r}):null}function i(e,t){if((arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).v2OperationIdCompatibilityMode){var n=(t.toLowerCase()+"_"+e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|.\/?,\\'""-]/g,"_");return(n=n||e.substring(1)+"_"+t).replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return""+d(t)+h(e)}function a(e,t){return d(t)+"-"+e}function u(e,t){return s(e,t,!0)||null}function s(e,t,n){if(!e||"object"!==(void 0===e?"undefined":(0,c.default)(e))||!e.paths||"object"!==(0,c.default)(e.paths))return null;var r=e.paths;for(var o in r)for(var i in r[o])if("PARAMETERS"!==i.toUpperCase()){var a=r[o][i];if(a&&"object"===(void 0===a?"undefined":(0,c.default)(a))){var u={spec:e,pathName:o,method:i.toUpperCase(),operation:a},s=t(u);if(n&&s)return u}}}Object.defineProperty(t,"__esModule",{value:!0});var l=r(n(18)),c=r(n(1));t.isOAS3=function(e){var t=e.openapi;return!!t&&(0,p.default)(t,"3")},t.isSwagger2=function(e){var t=e.swagger;return!!t&&(0,p.default)(t,"2")},t.opId=o,t.idFromPathMethod=i,t.legacyIdFromPathMethod=a,t.getOperationRaw=function(e,t){return e&&e.paths?u(e,function(e){var n=e.pathName,r=e.method,i=e.operation;if(!i||"object"!==(void 0===i?"undefined":(0,c.default)(i)))return!1;var u=i.operationId;return[o(i,n,r),a(n,r),u].some(function(e){return e&&e===t})}):null},t.findOperation=u,t.eachOperation=s,t.normalizeSwagger=function(e){var t=e.spec,n=t.paths,r={};if(!n||t.$$normalized)return e;for(var i in n){var a=n[i];if((0,f.default)(a)){var u=a.parameters;for(var s in a)!function(e){var n=a[e];if(!(0,f.default)(n))return"continue";var s=o(n,i,e);if(s){r[s]?r[s].push(n):r[s]=[n];var c=r[s];if(c.length>1)c.forEach(function(e,t){e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId=""+s+(t+1)});else if(void 0!==n.operationId){var p=c[0];p.__originalOperationId=p.__originalOperationId||n.operationId,p.operationId=s}}if("parameters"!==e){var d=[],h={};for(var v in t)"produces"!==v&&"consumes"!==v&&"security"!==v||(h[v]=t[v],d.push(h));if(u&&(h.parameters=u,d.push(h)),d.length){var m=!0,y=!1,g=void 0;try{for(var b,_=(0,l.default)(d);!(m=(b=_.next()).done);m=!0){var w=b.value;for(var E in w)if(n[E]){if("parameters"===E){var x=!0,S=!1,C=void 0;try{for(var k,A=(0,l.default)(w[E]);!(x=(k=A.next()).done);x=!0)!function(){var e=k.value;n[E].some(function(t){return t.name&&t.name===e.name||t.$ref&&t.$ref===e.$ref||t.$$ref&&t.$$ref===e.$$ref||t===e})||n[E].push(e)}()}catch(e){S=!0,C=e}finally{try{!x&&A.return&&A.return()}finally{if(S)throw C}}}}else n[E]=w[E]}}catch(e){y=!0,g=e}finally{try{!m&&_.return&&_.return()}finally{if(y)throw g}}}}}(s)}}return t.$$normalized=!0,e};var f=r(n(47)),p=r(n(14)),d=function(e){return String.prototype.toLowerCase.call(e)},h=function(e){return e.replace(/[^\w]/gi,"_")}},function(e,t){e.exports=n(892)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).loadSpec,r=void 0!==n&&n,o={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:i(e.headers)},a=o.headers["content-type"],u=r||_(a);return(u?e.text:e.blob||e.buffer).call(e).then(function(e){if(o.text=e,o.data=e,u)try{var t=function(e,t){return"application/json"===t?JSON.parse(e):y.default.safeLoad(e)}(e,a);o.body=t,o.obj=t}catch(e){o.parseError=e}return o})}function i(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={};return"function"==typeof e.forEach?(e.forEach(function(e,n){void 0!==t[n]?(t[n]=Array.isArray(t[n])?t[n]:[t[n]],t[n].push(e)):t[n]=e}),t):t}function a(e){return"undefined"!=typeof File?e instanceof File:null!==e&&"object"===(void 0===e?"undefined":(0,h.default)(e))&&"function"==typeof e.pipe}function u(e,t){var n=e.collectionFormat,r=e.allowEmptyValue,o="object"===(void 0===e?"undefined":(0,h.default)(e))?e.value:e;if(void 0===o&&r)return"";if(a(o)||"boolean"==typeof o)return o;var i=encodeURIComponent;return t&&(i=(0,g.default)(o)?function(e){return e}:function(e){return(0,p.default)(e)}),"object"!==(void 0===o?"undefined":(0,h.default)(o))||Array.isArray(o)?Array.isArray(o)?Array.isArray(o)&&!n?o.map(i).join(","):"multi"===n?o.map(i):o.map(i).join({csv:",",ssv:"%20",tsv:"%09",pipes:"|"}[n]):i(o):""}function s(e){var t=(0,f.default)(e).reduce(function(t,n){var r=e[n],o=!!r.skipEncoding,i=o?n:encodeURIComponent(n),a=function(e){return e&&"object"===(void 0===e?"undefined":(0,h.default)(e))}(r)&&!Array.isArray(r);return t[i]=u(a?r:{value:r},o),t},{});return m.default.stringify(t,{encode:!1,indices:!1})||""}function l(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,r=void 0===t?"":t,o=e.query,i=e.form;if(i){var l=(0,f.default)(i).some(function(e){return a(i[e].value)}),p=e.headers["content-type"]||e.headers["Content-Type"];if(l||/multipart\/form-data/i.test(p)){var d=n(30);e.body=new d,(0,f.default)(i).forEach(function(t){e.body.append(t,u(i[t],!0))})}else e.body=s(i);delete e.form}if(o){var h=r.split("?"),v=(0,c.default)(h,2),y=v[0],g=v[1],b="";if(g){var _=m.default.parse(g);(0,f.default)(o).forEach(function(e){return delete _[e]}),b=m.default.stringify(_,{encode:!0})}var w=function(){for(var e=arguments.length,t=Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:{};return d.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if("object"===(void 0===t?"undefined":(0,h.default)(t))&&(t=(a=t).url),a.headers=a.headers||{},b.mergeInQueryOrForm(a),!a.requestInterceptor){e.next=10;break}return e.next=6,a.requestInterceptor(a);case 6:if(e.t0=e.sent,e.t0){e.next=9;break}e.t0=a;case 9:a=e.t0;case 10:return n=a.headers["content-type"]||a.headers["Content-Type"],/multipart\/form-data/i.test(n)&&(delete a.headers["content-type"],delete a.headers["Content-Type"]),r=void 0,e.prev=13,e.next=16,(a.userFetch||fetch)(a.url,a);case 16:return r=e.sent,e.next=19,b.serializeRes(r,t,a);case 19:if(r=e.sent,!a.responseInterceptor){e.next=27;break}return e.next=23,a.responseInterceptor(r);case 23:if(e.t1=e.sent,e.t1){e.next=26;break}e.t1=r;case 26:r=e.t1;case 27:e.next=37;break;case 29:if(e.prev=29,e.t2=e.catch(13),r){e.next=33;break}throw e.t2;case 33:throw(o=new Error(r.statusText)).statusCode=o.status=r.status,o.responseError=e.t2,o;case 37:if(r.ok){e.next=42;break}throw(i=new Error(r.statusText)).statusCode=i.status=r.status,i.response=r,i;case 42:return e.abrupt("return",r);case 43:case"end":return e.stop()}},e,this,[[13,29]])}));return function(t){return e.apply(this,arguments)}}();var _=t.shouldDownloadAsText=function(){return/(json|xml|yaml|text)\b/.test(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")}},function(e,t){e.exports=n(41)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){return Array.isArray(e)?e.length<1?"":"/"+e.map(function(e){return(e+"").replace(/~/g,"~0").replace(/\//g,"~1")}).join("/"):e}function i(e,t,n){return{op:"replace",path:e,value:t,meta:n}}function a(e,t,n){return f(c(e.filter(m).map(function(e){return t(e.value,n,e.path)})||[]))}function u(e,t,n){return n=n||[],Array.isArray(e)?e.map(function(e,r){return u(e,t,n.concat(r))}):p(e)?(0,w.default)(e).map(function(r){return u(e[r],t,n.concat(r))}):t(e,n[n.length-1],n)}function s(e,t,n){var r=[];if((n=n||[]).length>0){var o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(Array.isArray(e)){var i=e.map(function(e,r){return s(e,t,n.concat(r))});i&&(r=r.concat(i))}else if(p(e)){var a=(0,w.default)(e).map(function(r){return s(e[r],t,n.concat(r))});a&&(r=r.concat(a))}return c(r)}function l(e){return Array.isArray(e)?e:[e]}function c(e){var t;return(t=[]).concat.apply(t,(0,_.default)(e.map(function(e){return Array.isArray(e)?c(e):e})))}function f(e){return e.filter(function(e){return void 0!==e})}function p(e){return e&&"object"===(void 0===e?"undefined":(0,b.default)(e))}function d(e){return e&&"function"==typeof e}function h(e){if(y(e)){var t=e.op;return"add"===t||"remove"===t||"replace"===t}return!1}function v(e){return h(e)||y(e)&&"mutation"===e.type}function m(e){return v(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function y(e){return e&&"object"===(void 0===e?"undefined":(0,b.default)(e))}function g(e,t){try{return S.default.getValueByPointer(e,t)}catch(e){return console.error(e),{}}}Object.defineProperty(t,"__esModule",{value:!0});var b=r(n(1)),_=r(n(34)),w=r(n(0)),E=r(n(35)),x=r(n(2)),S=r(n(36)),C=r(n(4)),k=r(n(37)),A=r(n(38));t.default={add:function(e,t){return{op:"add",path:e,value:t}},replace:i,remove:function(e,t){return{op:"remove",path:e}},merge:function(e,t){return{type:"mutation",op:"merge",path:e,value:t}},mergeDeep:function(e,t){return{type:"mutation",op:"mergeDeep",path:e,value:t}},context:function(e,t){return{type:"context",path:e,value:t}},getIn:function(e,t){return t.reduce(function(e,t){return void 0!==t&&e?e[t]:e},e)},applyPatch:function(e,t,n){if(n=n||{},"merge"===(t=(0,x.default)({},t,{path:t.path&&o(t.path)})).op){var r=g(e,t.path);(0,x.default)(r,t.value),S.default.applyPatch(e,[i(t.path,r)])}else if("mergeDeep"===t.op){var a=g(e,t.path);for(var u in t.value){var s=t.value[u],l=Array.isArray(s);if(l){var c=a[u]||[];a[u]=c.concat(s)}else if(p(s)&&!l){var f=(0,x.default)({},a[u]);for(var d in s){if(Object.prototype.hasOwnProperty.call(f,d)){f=(0,k.default)((0,A.default)({},f),s);break}(0,x.default)(f,(0,E.default)({},d,s[d]))}a[u]=f}else a[u]=s}}else if("add"===t.op&&""===t.path&&p(t.value)){var h=(0,w.default)(t.value).reduce(function(e,n){return e.push({op:"add",path:"/"+o(n),value:t.value[n]}),e},[]);S.default.applyPatch(e,h)}else if("replace"===t.op&&""===t.path){var v=t.value;n.allowMetaPatches&&t.meta&&m(t)&&(Array.isArray(t.value)||p(t.value))&&(v=(0,x.default)({},v,t.meta)),e=v}else if(S.default.applyPatch(e,[t]),n.allowMetaPatches&&t.meta&&m(t)&&(Array.isArray(t.value)||p(t.value))){var y=g(e,t.path),b=(0,x.default)({},y,t.meta);S.default.applyPatch(e,[i(t.path,b)])}return e},parentPathMatch:function(e,t){if(!Array.isArray(t))return!1;for(var n=0,r=t.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.requestInterceptor,r=t.responseInterceptor,o=e.withCredentials?"include":"same-origin";return function(t){return e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:"application/json"},credentials:o}).then(function(e){return e.body})}}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(4)),a=r(n(11));t.makeFetchJSON=o,t.clearCache=function(){s.plugins.refs.clearCache()},t.default=function(e){function t(e){var t=this;E&&(s.plugins.refs.docCache[E]=e),s.plugins.refs.fetchJSON=o(w,{requestInterceptor:g,responseInterceptor:b});var n=[s.plugins.refs];return"function"==typeof y&&n.push(s.plugins.parameters),"function"==typeof m&&n.push(s.plugins.properties),"strict"!==p&&n.push(s.plugins.allOf),(0,l.default)({spec:e,context:{baseDoc:E},plugins:n,allowMetaPatches:h,pathDiscriminator:v,parameterMacro:y,modelPropertyMacro:m}).then(_?function(){var e=(0,a.default)(i.default.mark(function e(n){return i.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n);case 1:case"end":return e.stop()}},e,t)}));return function(t){return e.apply(this,arguments)}}():c.normalizeSwagger)}var n=e.fetch,r=e.spec,f=e.url,p=e.mode,d=e.allowMetaPatches,h=void 0===d||d,v=e.pathDiscriminator,m=e.modelPropertyMacro,y=e.parameterMacro,g=e.requestInterceptor,b=e.responseInterceptor,_=e.skipNormalization,w=e.http,E=e.baseDoc;return E=E||f,w=n||w||u.default,r?t(r):o(w,{requestInterceptor:g,responseInterceptor:b})(E).then(t)};var u=r(n(7)),s=n(31),l=r(s),c=n(5)},function(e,t){e.exports=n(203)},function(e,t){e.exports=n(91)},function(e,t){e.exports=n(2)},function(e,t){e.exports=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=Array(e),r=0;r-1||o.indexOf(n)>-1||i.some(function(e){return n.indexOf(e)>-1})};var r=["properties"],o=["definitions","parameters","responses","securityDefinitions","components/schemas","components/responses","components/parameters","components/securitySchemes"],i=["schema/example"]},function(e,t,n){e.exports=n(24)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e?n.url=e:n=e,!(this instanceof o))return new o(n);(0,a.default)(this,n);var r=this.resolve().then(function(){return t.disableInterfaces||(0,a.default)(t,o.makeApisTagOperation(t)),t});return r.client=this,r}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(3)),a=r((r(n(25)),n(6))),u=r(n(14)),s=r(n(10)),l=n(7),c=r(l),f=n(16),p=r(f),d=r(n(48)),h=n(49),v=n(51),m=n(5);o.http=c.default,o.makeHttp=l.makeHttp.bind(null,o.http),o.resolve=p.default,o.resolveSubtree=d.default,o.execute=v.execute,o.serializeRes=l.serializeRes,o.serializeHeaders=l.serializeHeaders,o.clearCache=f.clearCache,o.parameterBuilders=v.PARAMETER_BUILDERS,o.makeApisTagOperation=h.makeApisTagOperation,o.buildRequest=v.buildRequest,o.helpers={opId:m.opId},o.prototype={http:c.default,execute:function(e){return this.applyDefaults(),o.execute((0,i.default)({spec:this.spec,http:this.http,securities:{authorized:this.authorizations},contextUrl:"string"==typeof this.url?this.url:void 0},e))},resolve:function(){var e=this;return o.resolve({spec:this.spec,url:this.url,allowMetaPatches:this.allowMetaPatches,requestInterceptor:this.requestInterceptor||null,responseInterceptor:this.responseInterceptor||null}).then(function(t){return e.originalSpec=e.spec,e.spec=t.spec,e.errors=t.errors,e})}},o.prototype.applyDefaults=function(){var e=this.spec,t=this.url;if(t&&(0,u.default)(t,"http")){var n=s.default.parse(t);e.host||(e.host=n.host),e.schemes||(e.schemes=[n.protocol.replace(":","")]),e.basePath||(e.basePath="/")}},t.default=o,e.exports=t.default},function(e,t){e.exports=n(904)},function(e,t){e.exports=n(18)},function(e,t){e.exports=n(905)},function(e,t){e.exports=n(906)},function(e,t){e.exports=n(343)},function(e,t){e.exports=n(909)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0}),t.plugins=t.SpecMap=void 0;var o=r(n(8)),i=r(n(1)),a=r(n(17)),u=r(n(4)),s=r(n(0)),l=r(n(18)),c=r(n(32)),f=r(n(2)),p=r(n(19)),d=r(n(20));t.default=function(e){return new w(e).dispatch()};var h=r(n(33)),v=r(n(9)),m=r(n(39)),y=r(n(43)),g=r(n(44)),b=r(n(45)),_=r(n(46)),w=function(){function e(t){(0,p.default)(this,e),(0,f.default)(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new _.default,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:(0,f.default)((0,c.default)(this),v.default),allowMetaPatches:!1},t),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(v.default.isFunction),this.patches.push(v.default.add([],this.spec)),this.patches.push(v.default.context([],this.context)),this.updatePatches(this.patches)}return(0,d.default)(e,[{key:"debug",value:function(e){if(this.debugLevel===e){for(var t,n=arguments.length,r=Array(n>1?n-1:0),o=1;o1?n-1:0),o=1;o0})}},{key:"nextPromisedPatch",value:function(){if(this.promisedPatches.length>0)return a.default.race(this.promisedPatches.map(function(e){return e.value}))}},{key:"getPluginHistory",value:function(e){var t=this.getPluginName(e);return this.pluginHistory[t]||[]}},{key:"getPluginRunCount",value:function(e){return this.getPluginHistory(e).length}},{key:"getPluginHistoryTip",value:function(e){var t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}},{key:"getPluginMutationIndex",value:function(e){var t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}},{key:"getPluginName",value:function(e){return e.pluginName}},{key:"updatePluginHistory",value:function(e,t){var n=this.getPluginName(e);(this.pluginHistory[n]=this.pluginHistory[n]||[]).push(t)}},{key:"updatePatches",value:function(e,t){var n=this;v.default.normalizeArray(e).forEach(function(e){if(e instanceof Error)n.errors.push(e);else try{if(!v.default.isObject(e))return void n.debug("updatePatches","Got a non-object patch",e);if(n.showDebug&&n.allPatches.push(e),v.default.isPromise(e.value))return n.promisedPatches.push(e),void n.promisedPatchThen(e);if(v.default.isContextPatch(e))return void n.setContext(e.path,e.value);if(v.default.isMutation(e))return void n.updateMutations(e)}catch(e){console.error(e),n.errors.push(e)}})}},{key:"updateMutations",value:function(e){"object"===(0,i.default)(e.value)&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value=(0,f.default)({},e.value));var t=v.default.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}},{key:"removePromisedPatch",value:function(e){var t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}},{key:"promisedPatchThen",value:function(e){var t=this;return e.value=e.value.then(function(n){var r=(0,f.default)({},e,{value:n});t.removePromisedPatch(e),t.updatePatches(r)}).catch(function(n){t.removePromisedPatch(e),t.updatePatches(n)})}},{key:"getMutations",value:function(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}},{key:"getCurrentMutations",value:function(){return this.getMutationsForPlugin(this.getCurrentPlugin())}},{key:"getMutationsForPlugin",value:function(e){var t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}},{key:"getCurrentPlugin",value:function(){return this.currentPlugin}},{key:"getPatchesOfType",value:function(e,t){return e.filter(t)}},{key:"getLib",value:function(){return this.libMethods}},{key:"_get",value:function(e){return v.default.getIn(this.state,e)}},{key:"_getContext",value:function(e){return this.contextTree.get(e)}},{key:"setContext",value:function(e,t){return this.contextTree.set(e,t)}},{key:"_hasRun",value:function(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}},{key:"_clone",value:function(e){return JSON.parse((0,o.default)(e))}},{key:"dispatch",value:function(){function e(e){e&&(e=v.default.fullyNormalizeArray(e),n.updatePatches(e,r))}var t=this,n=this,r=this.nextPlugin();if(!r){var o=this.nextPromisedPatch();if(o)return o.then(function(){return t.dispatch()}).catch(function(){return t.dispatch()});var i={spec:this.state,errors:this.errors};return this.showDebug&&(i.patches=this.allPatches),a.default.resolve(i)}if(n.pluginCount=n.pluginCount||{},n.pluginCount[r]=(n.pluginCount[r]||0)+1,n.pluginCount[r]>100)return a.default.resolve({spec:n.state,errors:n.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(r!==this.currentPlugin&&this.promisedPatches.length){var u=this.promisedPatches.map(function(e){return e.value});return a.default.all(u.map(function(e){return e.then(Function,Function)})).then(function(){return t.dispatch()})}return function(){n.currentPlugin=r;var t=n.getCurrentMutations(),o=n.mutations.length-1;try{if(r.isGenerator){var i=!0,a=!1,u=void 0;try{for(var s,p=(0,l.default)(r(t,n.getLib()));!(i=(s=p.next()).done);i=!0)e(s.value)}catch(e){a=!0,u=e}finally{try{!i&&p.return&&p.return()}finally{if(a)throw u}}}else e(r(t,n.getLib()))}catch(t){console.error(t),e([(0,f.default)((0,c.default)(t),{plugin:r})])}finally{n.updatePluginHistory(r,{mutationIndex:o})}return n.dispatch()}()}}]),e}(),E={refs:m.default,allOf:y.default,parameters:g.default,properties:b.default};t.SpecMap=w,t.plugins=E},function(e,t){e.exports=n(350)},function(e,t){e.exports=n(288)},function(e,t){e.exports=n(83)},function(e,t){e.exports=n(22)},function(e,t){e.exports=n(910)},function(e,t){e.exports=n(178)},function(e,t){e.exports=n(913)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!A.test(e)){if(!t)throw new O("Tried to resolve a relative URL, without having a basePath. path: '"+e+"' basePath: '"+t+"'");return x.default.resolve(t,e)}return e}function i(e,t){return new O("Could not resolve reference because of: "+e.message,t,e)}function a(e){return(e+"").split("#")}function u(e,t){var n=P[e];if(n&&!S.default.isPromise(n))try{var r=l(t,n);return(0,b.default)(y.default.resolve(r),{__value:r})}catch(e){return y.default.reject(e)}return s(e).then(function(e){return l(t,e)})}function s(e){var t=P[e];return t?S.default.isPromise(t)?t:y.default.resolve(t):(P[e]=I.fetchJSON(e).then(function(t){return P[e]=t,t}),P[e])}function l(e,t){var n=c(e);if(n.length<1)return t;var r=S.default.getIn(t,n);if(void 0===r)throw new O("Could not resolve pointer: "+e+" does not exist in document",{pointer:e});return r}function c(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a "+(void 0===e?"undefined":(0,v.default)(e)));return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(f)}function f(e){return"string"!=typeof e?e:E.default.unescape(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function p(e){return E.default.escape(e.replace(/~/g,"~0").replace(/\//g,"~1"))}function d(e,t){if(j(t))return!0;var n=e.charAt(t.length),r=t.slice(-1);return 0===e.indexOf(t)&&(!n||"/"===n||"#"===n)&&"#"!==r}function h(e,t,n,r){var o=T.get(r);o||(o={},T.set(r,o));var i=function(e){return 0===e.length?"":"/"+e.map(p).join("/")}(n),a=(t||"")+"#"+e;if(t==r.contextTree.get([]).baseDoc&&d(i,e))return!0;var u="";if(n.some(function(e){return u=u+"/"+p(e),o[u]&&o[u].some(function(e){return d(e,a)||d(a,e)})}))return!0;o[i]=(o[i]||[]).concat(a)}Object.defineProperty(t,"__esModule",{value:!0});var v=r(n(1)),m=r(n(0)),y=r(n(17)),g=r(n(40)),b=r(n(2)),_=n(41),w=r(n(15)),E=r(n(42)),x=r(n(10)),S=r(n(9)),C=r(n(21)),k=n(22),A=new RegExp("^([a-z]+://|//)","i"),O=(0,C.default)("JSONRefError",function(e,t,n){this.originalError=n,(0,b.default)(this,t||{})}),P={},T=new g.default,M={key:"$ref",plugin:function(e,t,n,r){var s=n.slice(0,-1);if(!(0,k.isFreelyNamed)(s)){var l=r.getContext(n).baseDoc;if("string"!=typeof e)return new O("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:l,fullPath:n});var f=a(e),p=f[0],d=f[1]||"",v=void 0;try{v=l||p?o(p,l):null}catch(t){return i(t,{pointer:d,$ref:e,basePath:v,fullPath:n})}var y=void 0,g=void 0;if(!h(d,v,s,r)){if(null==v?(g=c(d),void 0===(y=r.get(g))&&(y=new O("Could not resolve reference: "+e,{pointer:d,$ref:e,baseDoc:l,fullPath:n}))):y=null!=(y=u(v,d)).__value?y.__value:y.catch(function(t){throw i(t,{pointer:d,$ref:e,baseDoc:l,fullPath:n})}),y instanceof Error)return[S.default.remove(n),y];var b=S.default.replace(s,y,{$$ref:e});if(v&&v!==l)return[b,S.default.context(s,{baseDoc:v})];try{if(!function(e,t){var n=[e];return t.path.reduce(function(e,t){return n.push(e[t]),e[t]},e),function e(t){return S.default.isObject(t)&&(n.indexOf(t)>=0||(0,m.default)(t).some(function(n){return e(t[n])}))}(t.value)}(r.state,b))return b}catch(e){return null}}}}},I=(0,b.default)(M,{docCache:P,absoluteify:o,clearCache:function(e){void 0!==e?delete P[e]:(0,m.default)(P).forEach(function(e){delete P[e]})},JSONRefError:O,wrapError:i,getDoc:s,split:a,extractFromDoc:u,fetchJSON:function(e){return(0,_.fetch)(e,{headers:{Accept:"application/json, application/yaml"},loadSpec:!0}).then(function(e){return e.text()}).then(function(e){return w.default.safeLoad(e)})},extract:l,jsonPointerToArray:c,unescapeJsonPointerToken:f});t.default=I;var j=function(e){return!e||"/"===e||"#"===e};e.exports=t.default},function(e,t){e.exports=n(914)},function(e,t){e.exports=n(925)},function(e,t){e.exports=n(926)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){return e&&e.__esModule?e:{default:e}}(n(2)),o=n(22);t.default={key:"allOf",plugin:function(e,t,n,i,a){if(!a.meta||!a.meta.$$ref){var u=n.slice(0,-1);if(!(0,o.isFreelyNamed)(u)){if(!Array.isArray(e)){var s=new TypeError("allOf must be an array");return s.fullPath=n,s}var l=!1,c=a.value;u.forEach(function(e){c&&(c=c[e])}),delete(c=(0,r.default)({},c)).allOf;var f=[i.replace(u,{})].concat(e.map(function(e,t){if(!i.isObject(e)){if(l)return null;l=!0;var r=new TypeError("Elements in allOf must be objects");return r.fullPath=n,r}return i.mergeDeep(u,e)}));return f.push(i.mergeDeep(u,c)),c.$$ref||f.push(i.remove([].concat(u,"$$ref"))),f}}}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(2)),i=r(n(9));t.default={key:"parameters",plugin:function(e,t,n,r,a){if(Array.isArray(e)&&e.length){var u=(0,o.default)([],e),s=n.slice(0,-1),l=(0,o.default)({},i.default.getIn(r.spec,s));return e.forEach(function(e,t){try{u[t].default=r.parameterMacro(l,e)}catch(e){var o=new Error(e);return o.fullPath=n,o}}),i.default.replace(n,u)}return i.default.replace(n,e)}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(2)),i=r(n(9));t.default={key:"properties",plugin:function(e,t,n,r){var a=(0,o.default)({},e);for(var u in e)try{a[u].default=r.modelPropertyMacro(a[u])}catch(e){var s=new Error(e);return s.fullPath=n,s}return i.default.replace(n,a)}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){return i({children:{}},e,t)}function i(e,t,n){return e.value=t||{},e.protoValue=n?(0,u.default)({},n.protoValue,e.value):e.value,(0,a.default)(e.children).forEach(function(t){var n=e.children[t];e.children[t]=i(n,n.value,e)}),e}Object.defineProperty(t,"__esModule",{value:!0});var a=r(n(0)),u=r(n(3)),s=r(n(19)),l=r(n(20)),c=function(){function e(t){(0,s.default)(this,e),this.root=o(t||{})}return(0,l.default)(e,[{key:"set",value:function(e,t){var n=this.getParent(e,!0);if(n){var r=e[e.length-1],a=n.children;a[r]?i(a[r],t,n):a[r]=o(t,n)}else i(this.root,t,null)}},{key:"get",value:function(e){if((e=e||[]).length<1)return this.root.value;for(var t=this.root,n=void 0,r=void 0,o=0;o2&&void 0!==arguments[2]?arguments[2]:{};return o.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return r=g.returnEntireTree,a=g.baseDoc,c=g.requestInterceptor,f=g.responseInterceptor,p=g.parameterMacro,d=g.modelPropertyMacro,h={pathDiscriminator:n,baseDoc:a,requestInterceptor:c,responseInterceptor:f,parameterMacro:p,modelPropertyMacro:d},v=(0,l.normalizeSwagger)({spec:t}),m=v.spec,e.next=5,(0,s.default)((0,i.default)({},h,{spec:m,allowMetaPatches:!0,skipNormalization:!0}));case 5:return y=e.sent,!r&&Array.isArray(n)&&n.length&&(y.spec=(0,u.default)(y.spec,n)||null),e.abrupt("return",y);case 8:case"end":return e.stop()}},e,this)}));return function(t,n){return e.apply(this,arguments)}}(),e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=t.pathName,r=t.method,o=t.operationId;return function(t){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.execute((0,a.default)({spec:e.spec},(0,u.default)(e,"requestInterceptor","responseInterceptor","userFetch"),{pathName:n,method:r,parameters:t,operationId:o},i))}}}function i(e){var t=e.spec,n=e.cb,r=void 0===n?l:n,o=e.defaultTag,i=void 0===o?"default":o,a=e.v2OperationIdCompatibilityMode,u={},f={};return(0,s.eachOperation)(t,function(e){var n=e.pathName,o=e.method,l=e.operation;(l.tags?c(l.tags):[i]).forEach(function(e){if("string"==typeof e){var i=f[e]=f[e]||{},c=(0,s.opId)(l,n,o,{v2OperationIdCompatibilityMode:a}),p=r({spec:t,pathName:n,method:o,operation:l,operationId:c});if(u[c])u[c]++,i[""+c+u[c]]=p;else if(void 0!==i[c]){var d=u[c]||1;u[c]=d+1,i[""+c+u[c]]=p;var h=i[c];delete i[c],i[""+c+d]=h}else i[c]=p}})}),f}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var a=r(n(3));t.makeExecute=o,t.makeApisTagOperationsOperationExecute=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=f.makeExecute(e),n=f.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t}),r={};for(var o in n)for(var i in r[o]={operations:{}},n[o])r[o].operations[i]={execute:n[o][i]};return{apis:r}},t.makeApisTagOperation=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=f.makeExecute(e);return{apis:f.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t})}},t.mapTagOperations=i;var u=r(n(50)),s=n(5),l=function(){return null},c=function(e){return Array.isArray(e)?e:[e]},f=t.self={mapTagOperations:i,makeExecute:o}},function(e,t){e.exports=n(927)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.spec,n=e.operationId,r=(e.securities,e.requestContentType,e.responseContentType),o=e.scheme,a=e.requestInterceptor,s=e.responseInterceptor,c=e.contextUrl,f=e.userFetch,p=(e.requestBody,e.server),d=e.serverVariables,h=e.http,y=e.parameters,g=e.parameterBuilders,O=(0,x.isOAS3)(t);g||(g=O?_.default:b.default);var P={url:"",credentials:h&&h.withCredentials?"include":"same-origin",headers:{},cookies:{}};a&&(P.requestInterceptor=a),s&&(P.responseInterceptor=s),f&&(P.userFetch=f);var T=(0,x.getOperationRaw)(t,n);if(!T)throw new C("Operation "+n+" not found");var M=T.operation,I=void 0===M?{}:M,j=T.method,N=T.pathName;if(P.url+=i({spec:t,scheme:o,contextUrl:c,server:p,serverVariables:d,pathName:N,method:j}),!n)return delete P.cookies,P;P.url+=N,P.method=(""+j).toUpperCase(),y=y||{};var R=t.paths[N]||{};r&&(P.headers.accept=r);var D=A([].concat(S(I.parameters)).concat(S(R.parameters)));D.forEach(function(e){var n=g[e.in],r=void 0;if("body"===e.in&&e.schema&&e.schema.properties&&(r=y),void 0===(r=e&&e.name&&y[e.name])?r=e&&e.name&&y[e.in+"."+e.name]:k(e.name,D).length>1&&console.warn("Parameter '"+e.name+"' is ambiguous because the defined spec has more than one parameter with the name: '"+e.name+"' and the passed-in parameter values did not define an 'in' value."),null!==r){if(void 0!==e.default&&void 0===r&&(r=e.default),void 0===r&&e.required&&!e.allowEmptyValue)throw new Error("Required parameter "+e.name+" is not provided");if(O&&e.schema&&"object"===e.schema.type&&"string"==typeof r)try{r=JSON.parse(r)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}n&&n({req:P,parameter:e,value:r,operation:I,spec:t})}});var L=(0,u.default)({},e,{operation:I});if((P=O?(0,w.default)(L,P):(0,E.default)(L,P)).cookies&&(0,l.default)(P.cookies).length){var U=(0,l.default)(P.cookies).reduce(function(e,t){var n=P.cookies[t];return e+(e?"&":"")+v.default.serialize(t,n)},"");P.headers.Cookie=U}return P.cookies&&delete P.cookies,(0,m.mergeInQueryOrForm)(P),P}function i(e){return(0,x.isOAS3)(e.spec)?function(e){var t=e.spec,n=e.pathName,r=e.method,o=e.server,i=e.contextUrl,a=e.serverVariables,u=void 0===a?{}:a,s=(0,f.default)(t,["paths",n,(r||"").toLowerCase(),"servers"])||(0,f.default)(t,["paths",n,"servers"])||(0,f.default)(t,["servers"]),l="",c=null;if(o&&s&&s.length){var p=s.map(function(e){return e.url});p.indexOf(o)>-1&&(l=o,c=s[p.indexOf(o)])}!l&&s&&s.length&&(l=s[0].url,c=s[0]),l.indexOf("{")>-1&&function(e){for(var t=[],n=/{([^}]+)}/g,r=void 0;r=n.exec(e);)t.push(r[1]);return t}(l).forEach(function(e){if(c.variables&&c.variables[e]){var t=c.variables[e],n=u[e]||t.default,r=new RegExp("{"+e+"}","g");l=l.replace(r,n)}});return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=h.default.parse(e),r=h.default.parse(t),o=P(n.protocol)||P(r.protocol)||"",i=n.host||r.host,a=n.pathname||"",u=void 0;return"/"===(u=o&&i?o+"://"+(i+a):a)[u.length-1]?u.slice(0,-1):u}(l,i)}(e):function(e){var t=e.spec,n=e.scheme,r=e.contextUrl,o=void 0===r?"":r,i=h.default.parse(o),a=Array.isArray(t.schemes)?t.schemes[0]:null,u=n||a||P(i.protocol)||"http",s=t.host||i.host||"",l=t.basePath||"",c=void 0;return"/"===(c=u&&s?u+"://"+(s+l):l)[c.length-1]?c.slice(0,-1):c}(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var a=r(n(8)),u=r(n(3)),s=r(n(52)),l=r(n(0)),c=r(n(2));t.execute=function(e){var t=e.http,n=e.fetch,r=e.spec,o=e.operationId,i=e.pathName,l=e.method,c=e.parameters,f=e.securities,h=(0,s.default)(e,["http","fetch","spec","operationId","pathName","method","parameters","securities"]),v=t||n||y.default;i&&l&&!o&&(o=(0,x.legacyIdFromPathMethod)(i,l));var m=O.buildRequest((0,u.default)({spec:r,operationId:o,parameters:c,securities:f,http:v},h));return m.body&&((0,p.default)(m.body)||(0,d.default)(m.body))&&(m.body=(0,a.default)(m.body)),v(m)},t.buildRequest=o,t.baseUrl=i;var f=r((r(n(6)),n(12))),p=r(n(53)),d=r(n(54)),h=r((r(n(13)),n(10))),v=r(n(55)),m=n(7),y=r(m),g=r(n(21)),b=r(n(56)),_=r(n(57)),w=r(n(62)),E=r(n(64)),x=n(5),S=function(e){return Array.isArray(e)?e:[]},C=(0,g.default)("OperationNotFoundError",function(e,t,n){this.originalError=n,(0,c.default)(this,t||{})}),k=function(e,t){return t.filter(function(t){return t.name===e})},A=function(e){var t={};e.forEach(function(e){t[e.in]||(t[e.in]={}),t[e.in][e.name]=e});var n=[];return(0,l.default)(t).forEach(function(e){(0,l.default)(t[e]).forEach(function(r){n.push(t[e][r])})}),n},O=t.self={buildRequest:o},P=function(e){return e?e.replace(/\W/g,""):null}},function(e,t){e.exports=n(84)},function(e,t){e.exports=n(227)},function(e,t){e.exports=n(24)},function(e,t){e.exports=n(930)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={body:function(e){var t=e.req,n=e.value;t.body=n},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false"),0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0"),n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){var o=r.name;t.query[o]=t.query[o]||{},t.query[o].allowEmptyValue=!0}},path:function(e){var t=e.req,n=e.value,r=e.parameter;t.url=t.url.replace("{"+r.name+"}",encodeURIComponent(n))},formData:function(e){var t=e.req,n=e.value,r=e.parameter;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(0)),i=r(n(1)),a=r(n(58));t.default={path:function(e){var t=e.req,n=e.value,r=e.parameter,o=r.name,i=r.style,u=r.explode,s=(0,a.default)({key:r.name,value:n,style:i||"simple",explode:u||!1,escape:!1});t.url=t.url.replace("{"+o+"}",s)},query:function(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},!1===n&&(n="false"),0===n&&(n="0"),n){var u=void 0===n?"undefined":(0,i.default)(n);"deepObject"===r.style?(0,o.default)(n).forEach(function(e){var o=n[e];t.query[r.name+"["+e+"]"]={value:(0,a.default)({key:e,value:o,style:"deepObject",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}}):"object"!==u||Array.isArray(n)||"form"!==r.style&&r.style||!r.explode&&void 0!==r.explode?t.query[r.name]={value:(0,a.default)({key:r.name,value:n,style:r.style||"form",explode:void 0===r.explode||r.explode,escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}:(0,o.default)(n).forEach(function(e){var o=n[e];t.query[e]={value:(0,a.default)({key:e,value:o,style:r.style||"form",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}})}else if(r.allowEmptyValue&&void 0!==n){var s=r.name;t.query[s]=t.query[s]||{},t.query[s].allowEmptyValue=!0}},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},u.indexOf(n.name.toLowerCase())>-1||void 0!==r&&(t.headers[n.name]=(0,a.default)({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))},cookie:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{};var o=void 0===r?"undefined":(0,i.default)(r);if("undefined"!==o){var u="object"===o&&!Array.isArray(r)&&n.explode?"":n.name+"=";t.headers.Cookie=u+(0,a.default)({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}};var u=["accept","authorization","content-type"];e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).escape,n=arguments[2];return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?n?JSON.parse(e):(0,s.stringToCharArray)(e).map(function(e){return c(e)?e:l(e)&&"unsafe"===t?e:((0,u.default)(e)||[]).map(function(e){return e.toString(16).toUpperCase()}).map(function(e){return"%"+e}).join("")}).join(""):e}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(0)),a=r(n(1));t.encodeDisallowedCharacters=o,t.default=function(e){var t=e.value;return Array.isArray(t)?function(e){var t=e.key,n=e.value,r=e.style,i=e.explode,a=e.escape,u=function(e){return o(e,{escape:a})};if("simple"===r)return n.map(function(e){return u(e)}).join(",");if("label"===r)return"."+n.map(function(e){return u(e)}).join(".");if("matrix"===r)return n.map(function(e){return u(e)}).reduce(function(e,n){return!e||i?(e||"")+";"+t+"="+n:e+","+n},"");if("form"===r){var s=i?"&"+t+"=":",";return n.map(function(e){return u(e)}).join(s)}if("spaceDelimited"===r){var l=i?t+"=":"";return n.map(function(e){return u(e)}).join(" "+l)}if("pipeDelimited"===r){var c=i?t+"=":"";return n.map(function(e){return u(e)}).join("|"+c)}}(e):"object"===(void 0===t?"undefined":(0,a.default)(t))?function(e){var t=e.key,n=e.value,r=e.style,a=e.explode,u=e.escape,s=function(e){return o(e,{escape:u})},l=(0,i.default)(n);return"simple"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+",":"")+t+(a?"=":",")+r},""):"label"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+".":".")+t+(a?"=":".")+r},""):"matrix"===r&&a?l.reduce(function(e,t){var r=s(n[t]);return(e?e+";":";")+t+"="+r},""):"matrix"===r?l.reduce(function(e,r){var o=s(n[r]);return(e?e+",":";"+t+"=")+r+","+o},""):"form"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+(a?"&":","):"")+t+(a?"=":",")+r},""):void 0}(e):function(e){var t=e.key,n=e.value,r=e.style,i=e.escape,a=function(e){return o(e,{escape:i})};return"simple"===r?a(n):"label"===r?"."+a(n):"matrix"===r?";"+t+"="+a(n):"form"===r?a(n):"deepObject"===r?a(n):void 0}(e)};var u=r((r(n(59)),n(60))),s=n(61),l=function(e){return":/?#[]@!$&'()*+,;=".indexOf(e)>-1},c=function(e){return/^[a-z0-9\-._~]+$/i.test(e)}},function(e,t){e.exports=n(931)},function(e,t){e.exports=n(932)},function(e,t){e.exports=n(933)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,f=(0,s.default)({},t),p=r.authorized,d=void 0===p?{}:p,h=i.security||a.security||[],v=d&&!!(0,u.default)(d).length,m=(0,l.default)(a,["components","securitySchemes"])||{};return f.headers=f.headers||{},f.query=f.query||{},(0,u.default)(r).length&&v&&h&&(!Array.isArray(i.security)||i.security.length)?(h.forEach(function(e,t){for(var n in e){var r=d[n],o=m[n];if(r){var i=r.value||r,a=o.type;if(r)if("apiKey"===a)"query"===o.in&&(f.query[o.name]=i),"header"===o.in&&(f.headers[o.name]=i),"cookie"===o.in&&(f.cookies[o.name]=i);else if("http"===a){if("basic"===o.scheme){var u=i.username,s=i.password,l=(0,c.default)(u+":"+s);f.headers.Authorization="Basic "+l}"bearer"===o.scheme&&(f.headers.Authorization="Bearer "+i)}else if("oauth2"===a){var p=r.token||{},h=p.access_token,v=p.token_type;v&&"bearer"!==v.toLowerCase()||(v="Bearer"),f.headers.Authorization=v+" "+h}}}}),f):t}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(8)),a=r(n(1)),u=r(n(0));t.default=function(e,t){var n=e.operation,r=e.requestBody,s=e.securities,l=e.spec,c=e.attachContentTypeForEmptyPayload,p=e.requestContentType;t=o({request:t,securities:s,operation:n,spec:l});var d=n.requestBody||{},h=(0,u.default)(d.content||{}),v=p&&h.indexOf(p)>-1;if(r||c){if(p&&v)t.headers["Content-Type"]=p;else if(!p){var m=h[0];m&&(t.headers["Content-Type"]=m,p=m)}}else p&&v&&(t.headers["Content-Type"]=p);return r&&(p?h.indexOf(p)>-1&&("application/x-www-form-urlencoded"===p||0===p.indexOf("multipart/")?"object"===(void 0===r?"undefined":(0,a.default)(r))?(t.form={},(0,u.default)(r).forEach(function(e){var n,o=r[e],u=void 0;"undefined"!=typeof File&&(u=o instanceof File),"undefined"!=typeof Blob&&(u=u||o instanceof Blob),void 0!==f.Buffer&&(u=u||f.Buffer.isBuffer(o)),n="object"!==(void 0===o?"undefined":(0,a.default)(o))||u?o:Array.isArray(o)?o.toString():(0,i.default)(o),t.form[e]={value:n}})):t.form=r:t.body=r):t.body=r),t},t.applySecurities=o;var s=r(n(6)),l=r(n(12)),c=r(n(13)),f=n(63)},function(e,t){e.exports=n(54)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,s=void 0===o?{}:o,l=e.spec,c=(0,u.default)({},t),f=r.authorized,p=void 0===f?{}:f,d=r.specSecurity,h=void 0===d?[]:d,v=s.security||h,m=p&&!!(0,i.default)(p).length,y=l.securityDefinitions;return c.headers=c.headers||{},c.query=c.query||{},(0,i.default)(r).length&&m&&v&&(!Array.isArray(s.security)||s.security.length)?(v.forEach(function(e,t){for(var n in e){var r=p[n];if(r){var o=r.token,i=r.value||r,u=y[n],s=u.type,l=o&&o.access_token,f=o&&o.token_type;if(r)if("apiKey"===s){var d="query"===u.in?"query":"headers";c[d]=c[d]||{},c[d][u.name]=i}else"basic"===s?i.header?c.headers.authorization=i.header:(i.base64=(0,a.default)(i.username+":"+i.password),c.headers.authorization="Basic "+i.base64):"oauth2"===s&&l&&(f=f&&"bearer"!==f.toLowerCase()?f:"Bearer",c.headers.authorization=f+" "+l)}}}),c):t}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(0));t.default=function(e,t){var n=e.spec,r=e.operation,i=e.securities,a=e.requestContentType,u=e.attachContentTypeForEmptyPayload;if((t=o({request:t,securities:i,operation:r,spec:n})).body||t.form||u)a?t.headers["Content-Type"]=a:Array.isArray(r.consumes)?t.headers["Content-Type"]=r.consumes[0]:Array.isArray(n.consumes)?t.headers["Content-Type"]=n.consumes[0]:r.parameters&&r.parameters.filter(function(e){return"file"===e.type}).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(a){var s=r.parameters&&r.parameters.filter(function(e){return"body"===e.in}).length>0,l=r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length>0;(s||l)&&(t.headers["Content-Type"]=a)}return t},t.applySecurities=o;var a=r(n(13)),u=r(n(6));r(n(7))}])},function(e,t,n){"use strict";var r=Object.prototype.hasOwnProperty,o=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}();t.arrayToObject=function(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r=48&&i<=57||i>=65&&i<=90||i>=97&&i<=122?n+=t.charAt(r):i<128?n+=o[i]:i<2048?n+=o[192|i>>6]+o[128|63&i]:i<55296||i>=57344?n+=o[224|i>>12]+o[128|i>>6&63]+o[128|63&i]:(r+=1,i=65536+((1023&i)<<10|1023&t.charCodeAt(r)),n+=o[240|i>>18]+o[128|i>>12&63]+o[128|i>>6&63]+o[128|63&i])}return n},t.compact=function(e){for(var t=[{obj:{o:e},prop:"o"}],n=[],r=0;r=0;l--)if(f[l]!=p[l])return!1;for(l=f.length-1;l>=0;l--)if(c=f[l],!a(e[c],t[c],n))return!1;return typeof e==typeof t}(e,t,n))};function u(e){return null===e||void 0===e}function s(e){return!(!e||"object"!=typeof e||"number"!=typeof e.length)&&("function"==typeof e.copy&&"function"==typeof e.slice&&!(e.length>0&&"number"!=typeof e[0]))}},function(e,t,n){var r={strict:!0},o=n(391),i=function(e,t){return o(e,t,r)},a=n(230);t.JsonPatchError=a.PatchError,t.deepClone=a._deepClone;var u={add:function(e,t,n){return e[t]=this.value,{newDocument:n}},remove:function(e,t,n){var r=e[t];return delete e[t],{newDocument:n,removed:r}},replace:function(e,t,n){var r=e[t];return e[t]=this.value,{newDocument:n,removed:r}},move:function(e,t,n){var r=l(n,this.path);r&&(r=a._deepClone(r));var o=c(n,{op:"remove",path:this.from}).removed;return c(n,{op:"add",path:this.path,value:o}),{newDocument:n,removed:r}},copy:function(e,t,n){var r=l(n,this.from);return c(n,{op:"add",path:this.path,value:a._deepClone(r)}),{newDocument:n}},test:function(e,t,n){return{newDocument:n,test:i(e[t],this.value)}},_get:function(e,t,n){return this.value=e[t],{newDocument:n}}},s={add:function(e,t,n){return a.isInteger(t)?e.splice(t,0,this.value):e[t]=this.value,{newDocument:n,index:t}},remove:function(e,t,n){return{newDocument:n,removed:e.splice(t,1)[0]}},replace:function(e,t,n){var r=e[t];return e[t]=this.value,{newDocument:n,removed:r}},move:u.move,copy:u.copy,test:u.test,_get:u._get};function l(e,t){if(""==t)return e;var n={op:"_get",path:t};return c(e,n),n.value}function c(e,n,r,o){if(void 0===r&&(r=!1),void 0===o&&(o=!0),r&&("function"==typeof r?r(n,0,e,n.path):p(n,0)),""===n.path){var c={newDocument:e};if("add"===n.op)return c.newDocument=n.value,c;if("replace"===n.op)return c.newDocument=n.value,c.removed=e,c;if("move"===n.op||"copy"===n.op)return c.newDocument=l(e,n.from),"move"===n.op&&(c.removed=e),c;if("test"===n.op){if(c.test=i(e,n.value),!1===c.test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c.newDocument=e,c}if("remove"===n.op)return c.removed=e,c.newDocument=null,c;if("_get"===n.op)return n.value=e,c;if(r)throw new t.JsonPatchError("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",0,n,e);return c}o||(e=a._deepClone(e));var f=(n.path||"").split("/"),d=e,h=1,v=f.length,m=void 0,y=void 0,g=void 0;for(g="function"==typeof r?r:p;;){if(y=f[h],r&&void 0===m&&(void 0===d[y]?m=f.slice(0,h).join("/"):h==v-1&&(m=n.path),void 0!==m&&g(n,0,e,m)),h++,Array.isArray(d)){if("-"===y)y=d.length;else{if(r&&!a.isInteger(y))throw new t.JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",0,n.path,n);a.isInteger(y)&&(y=~~y)}if(h>=v){if(r&&"add"===n.op&&y>d.length)throw new t.JsonPatchError("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",0,n.path,n);if(!1===(c=s[n.op].call(n,d,y,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c}}else if(y&&-1!=y.indexOf("~")&&(y=a.unescapePathComponent(y)),h>=v){if(!1===(c=u[n.op].call(n,d,y,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c}d=d[y]}}function f(e,n,r,o){if(void 0===o&&(o=!0),r&&!Array.isArray(n))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");o||(e=a._deepClone(e));for(var i=new Array(n.length),u=0,s=n.length;u0)throw new t.JsonPatchError('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",n,e,r);if(("move"===e.op||"copy"===e.op)&&"string"!=typeof e.from)throw new t.JsonPatchError("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",n,e,r);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&void 0===e.value)throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",n,e,r);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&a.hasUndefined(e.value))throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",n,e,r);if(r)if("add"==e.op){var i=e.path.split("/").length,s=o.split("/").length;if(i!==s+1&&i!==s)throw new t.JsonPatchError("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",n,e,r)}else if("replace"===e.op||"remove"===e.op||"_get"===e.op){if(e.path!==o)throw new t.JsonPatchError("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",n,e,r)}else if("move"===e.op||"copy"===e.op){var l=d([{op:"_get",path:e.from,value:void 0}],r);if(l&&"OPERATION_PATH_UNRESOLVABLE"===l.name)throw new t.JsonPatchError("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",n,e,r)}}function d(e,n,r){try{if(!Array.isArray(e))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(n)f(a._deepClone(n),a._deepClone(e),r||!0);else{r=r||p;for(var o=0;o1&&void 0!==arguments[1]?arguments[1]:(0,a.List)();return function(e){return(e.authSelectors.definitionsToAuthorize()||(0,a.List)()).filter(function(e){return t.some(function(t){return t.get(e.keySeq().first())})})}},t.authorized=(0,i.createSelector)(s,function(e){return e.get("authorized")||(0,a.Map)()}),t.isAuthorized=function(e,t){return function(e){var n=e.authSelectors.authorized();return a.List.isList(t)?!!t.toJS().filter(function(e){return-1===(0,r.default)(e).map(function(e){return!!n.get(e)}).indexOf(!1)}).length:null}},t.getConfigs=(0,i.createSelector)(s,function(e){return e.get("configs")})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=void 0;var r,o=n(25),i=(r=o)&&r.__esModule?r:{default:r};t.execute=function(e,t){var n=t.authSelectors,r=t.specSelectors;return function(t){var o=t.path,a=t.method,u=t.operation,s=t.extras,l={authorized:n.authorized()&&n.authorized().toJS(),definitions:r.securityDefinitions()&&r.securityDefinitions().toJS(),specSecurity:r.security()&&r.security().toJS()};return e((0,i.default)({path:o,method:a,operation:u,securities:l},s))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{fn:{shallowEqualKeys:r.shallowEqualKeys}}};var r=n(12)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=s(n(41)),o=s(n(23));t.default=function(e){var t=e.fn,n={download:function(e){return function(n){var r=n.errActions,i=n.specSelectors,a=n.specActions,s=n.getConfigs,l=t.fetch,c=s();function f(t){if(t instanceof Error||t.status>=400)return a.updateLoadingStatus("failed"),r.newThrownErr((0,o.default)(new Error((t.message||t.statusText)+" "+e),{source:"fetch"})),void(!t.status&&t instanceof Error&&function(){try{var t=void 0;if("URL"in u.default?t=new URL(e):(t=document.createElement("a")).href=e,"https:"!==t.protocol&&"https:"===u.default.location.protocol){var n=(0,o.default)(new Error("Possible mixed-content issue? The page was loaded over https:// but a "+t.protocol+"// URL was specified. Check that you are not attempting to load mixed content."),{source:"fetch"});return void r.newThrownErr(n)}if(t.origin!==u.default.location.origin){var i=(0,o.default)(new Error("Possible cross-origin (CORS) issue? The URL origin ("+t.origin+") does not match the page ("+u.default.location.origin+"). Check the server returns the correct 'Access-Control-Allow-*' headers."),{source:"fetch"});r.newThrownErr(i)}}catch(e){return}}());a.updateLoadingStatus("success"),a.updateSpec(t.text),i.url()!==e&&a.updateUrl(e)}e=e||i.url(),a.updateLoadingStatus("loading"),r.clear({source:"fetch"}),l({url:e,loadSpec:!0,requestInterceptor:c.requestInterceptor||function(e){return e},responseInterceptor:c.responseInterceptor||function(e){return e},credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(f,f)}},updateLoadingStatus:function(e){var t=[null,"loading","failed","success","failedConfig"];return-1===t.indexOf(e)&&console.error("Error: "+e+" is not one of "+(0,r.default)(t)),{type:"spec_update_loading_status",payload:e}}},s={loadingStatus:(0,i.createSelector)(function(e){return e||(0,a.Map)()},function(e){return e.get("loadingStatus")||null})};return{statePlugins:{spec:{actions:n,reducers:{spec_update_loading_status:function(e,t){return"string"==typeof t.payload?e.set("loadingStatus",t.payload):e}},selectors:s}}}};var i=n(57),a=n(7),u=s(n(32));function s(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{spec:{actions:a,selectors:f},configs:{reducers:s.default,actions:i,selectors:u}}}};var r=c(n(934)),o=n(233),i=l(n(234)),a=l(n(401)),u=l(n(402)),s=c(n(403));function l(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function c(e){return e&&e.__esModule?e:{default:e}}var f={getLocalConfig:function(){return(0,o.parseYamlConfig)(r.default)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getConfigByUrl=t.downloadConfig=void 0;var r=n(233);t.downloadConfig=function(e){return function(t){return(0,t.fn.fetch)(e)}},t.getConfigByUrl=function(e,t){return function(n){var o=n.specActions;if(e)return o.downloadConfig(e).then(i,i);function i(n){n instanceof Error||n.status>=400?(o.updateLoadingStatus("failedConfig"),o.updateLoadingStatus("failedConfig"),o.updateUrl(""),console.error(n.statusText+" "+e.url),t(null)):t((0,r.parseYamlConfig)(n.text))}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.get=function(e,t){return e.getIn(Array.isArray(t)?t:[t])}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o,i=n(22),a=(r=i)&&r.__esModule?r:{default:r},u=n(7),s=n(234);t.default=(o={},(0,a.default)(o,s.UPDATE_CONFIGS,function(e,t){return e.merge((0,u.fromJS)(t.payload))}),(0,a.default)(o,s.TOGGLE_CONFIGS,function(e,t){var n=t.payload,r=e.get(n);return e.set(n,!r)}),o)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return[r.default,{statePlugins:{configs:{wrapActions:{loaded:function(e,t){return function(){e.apply(void 0,arguments);var n=window.location.hash;t.layoutActions.parseDeepLinkHash(n)}}}}},wrapComponents:{operation:o.default,OperationTag:i.default}}]};var r=a(n(405)),o=a(n(407)),i=a(n(408));function a(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.clearScrollTo=t.scrollToElement=t.readyToScroll=t.parseDeepLinkHash=t.scrollTo=t.show=void 0;var r,o=c(n(22)),i=c(n(18)),a=n(406),u=c(n(935)),s=n(7),l=c(s);function c(e){return e&&e.__esModule?e:{default:e}}var f=t.show=function(e,t){var n=t.getConfigs,r=t.layoutSelectors;return function(){for(var t=arguments.length,o=Array(t),u=0;u",Gt:"≫",gt:">",gtcc:"⪧",gtcir:"⩺",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",HARDcy:"Ъ",hardcy:"ъ",hArr:"⇔",harr:"↔",harrcir:"⥈",harrw:"↭",Hat:"^",hbar:"ℏ",Hcirc:"Ĥ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",Hfr:"ℌ",hfr:"𝔥",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",Hopf:"ℍ",hopf:"𝕙",horbar:"―",HorizontalLine:"─",Hscr:"ℋ",hscr:"𝒽",hslash:"ℏ",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",Iacute:"Í",iacute:"í",ic:"⁣",Icirc:"Î",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",Ifr:"ℑ",ifr:"𝔦",Igrave:"Ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",IJlig:"IJ",ijlig:"ij",Im:"ℑ",Imacr:"Ī",imacr:"ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",imof:"⊷",imped:"Ƶ",Implies:"⇒",in:"∈",incare:"℅",infin:"∞",infintie:"⧝",inodot:"ı",Int:"∬",int:"∫",intcal:"⊺",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",IOcy:"Ё",iocy:"ё",Iogon:"Į",iogon:"į",Iopf:"𝕀",iopf:"𝕚",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",Iscr:"ℐ",iscr:"𝒾",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",Itilde:"Ĩ",itilde:"ĩ",Iukcy:"І",iukcy:"і",Iuml:"Ï",iuml:"ï",Jcirc:"Ĵ",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"𝔍",jfr:"𝔧",jmath:"ȷ",Jopf:"𝕁",jopf:"𝕛",Jscr:"𝒥",jscr:"𝒿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"є",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"ķ",Kcy:"К",kcy:"к",Kfr:"𝔎",kfr:"𝔨",kgreen:"ĸ",KHcy:"Х",khcy:"х",KJcy:"Ќ",kjcy:"ќ",Kopf:"𝕂",kopf:"𝕜",Kscr:"𝒦",kscr:"𝓀",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",Lambda:"Λ",lambda:"λ",Lang:"⟪",lang:"⟨",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",Larr:"↞",lArr:"⇐",larr:"←",larrb:"⇤",larrbfs:"⤟",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",lAtail:"⤛",latail:"⤙",late:"⪭",lates:"⪭︀",lBarr:"⤎",lbarr:"⤌",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ļ",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",lE:"≦",le:"≤",LeftAngleBracket:"⟨",LeftArrow:"←",Leftarrow:"⇐",leftarrow:"←",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangle:"⊲",LeftTriangleBar:"⧏",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"𝔏",lfr:"𝔩",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",LJcy:"Љ",ljcy:"љ",Ll:"⋘",ll:"≪",llarr:"⇇",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",Lmidot:"Ŀ",lmidot:"ŀ",lmoust:"⎰",lmoustache:"⎰",lnap:"⪉",lnapprox:"⪉",lnE:"≨",lne:"⪇",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftarrow:"⟵",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longleftrightarrow:"⟷",longmapsto:"⟼",LongRightArrow:"⟶",Longrightarrow:"⟹",longrightarrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"𝕃",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",Lscr:"ℒ",lscr:"𝓁",Lsh:"↰",lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Ł",lstrok:"ł",LT:"<",Lt:"≪",lt:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",mfr:"𝔪",mho:"℧",micro:"µ",mid:"∣",midast:"*",midcir:"⫰",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"𝕄",mopf:"𝕞",mp:"∓",Mscr:"ℳ",mscr:"𝓂",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"♮",natural:"♮",naturals:"ℕ",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Ņ",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",Ncy:"Н",ncy:"н",ndash:"–",ne:"≠",nearhk:"⤤",neArr:"⇗",nearr:"↗",nearrow:"↗",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"𝔑",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nhArr:"⇎",nharr:"↮",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"њ",nlArr:"⇍",nlarr:"↚",nldr:"‥",nlE:"≦̸",nle:"≰",nLeftarrow:"⇍",nleftarrow:"↚",nLeftrightarrow:"⇎",nleftrightarrow:"↮",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",Nopf:"ℕ",nopf:"𝕟",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangle:"⋪",NotLeftTriangleBar:"⧏̸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangle:"⋫",NotRightTriangleBar:"⧐̸",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrArr:"⇏",nrarr:"↛",nrarrc:"⤳̸",nrarrw:"↝̸",nRightarrow:"⇏",nrightarrow:"↛",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",Nscr:"𝒩",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",Nu:"Ν",nu:"ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nVDash:"⊯",nVdash:"⊮",nvDash:"⊭",nvdash:"⊬",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwArr:"⇖",nwarr:"↖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",ocir:"⊚",Ocirc:"Ô",ocirc:"ô",Ocy:"О",ocy:"о",odash:"⊝",Odblac:"Ő",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Œ",oelig:"œ",ofcir:"⦿",Ofr:"𝔒",ofr:"𝔬",ogon:"˛",Ograve:"Ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",Omacr:"Ō",omacr:"ō",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"𝕆",oopf:"𝕠",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",Or:"⩔",or:"∨",orarr:"↻",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",Oscr:"𝒪",oscr:"ℴ",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",Otimes:"⨷",otimes:"⊗",otimesas:"⨶",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",par:"∥",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"𝔓",pfr:"𝔭",Phi:"Φ",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",Popf:"ℙ",popf:"𝕡",pound:"£",Pr:"⪻",pr:"≺",prap:"⪷",prcue:"≼",prE:"⪳",pre:"⪯",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",Prime:"″",prime:"′",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportion:"∷",Proportional:"∝",propto:"∝",prsim:"≾",prurel:"⊰",Pscr:"𝒫",pscr:"𝓅",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"𝔔",qfr:"𝔮",qint:"⨌",Qopf:"ℚ",qopf:"𝕢",qprime:"⁗",Qscr:"𝒬",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",QUOT:'"',quot:'"',rAarr:"⇛",race:"∽̱",Racute:"Ŕ",racute:"ŕ",radic:"√",raemptyv:"⦳",Rang:"⟫",rang:"⟩",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",Rarr:"↠",rArr:"⇒",rarr:"→",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"↝",rAtail:"⤜",ratail:"⤚",ratio:"∶",rationals:"ℚ",RBarr:"⤐",rBarr:"⤏",rbarr:"⤍",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",Rcaron:"Ř",rcaron:"ř",Rcedil:"Ŗ",rcedil:"ŗ",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",Re:"ℜ",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",rect:"▭",REG:"®",reg:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",Rfr:"ℜ",rfr:"𝔯",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"ρ",rhov:"ϱ",RightAngleBracket:"⟩",RightArrow:"→",Rightarrow:"⇒",rightarrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangle:"⊳",RightTriangleBar:"⧐",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoust:"⎱",rmoustache:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",Ropf:"ℝ",ropf:"𝕣",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",Rscr:"ℛ",rscr:"𝓇",Rsh:"↱",rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",Sacute:"Ś",sacute:"ś",sbquo:"‚",Sc:"⪼",sc:"≻",scap:"⪸",Scaron:"Š",scaron:"š",sccue:"≽",scE:"⪴",sce:"⪰",Scedil:"Ş",scedil:"ş",Scirc:"Ŝ",scirc:"ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"с",sdot:"⋅",sdotb:"⊡",sdote:"⩦",searhk:"⤥",seArr:"⇘",searr:"↘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"𝔖",sfr:"𝔰",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ь",sol:"/",solb:"⧄",solbar:"⌿",Sopf:"𝕊",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",squ:"□",Square:"□",square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squf:"▪",srarr:"→",Sscr:"𝒮",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",Sub:"⋐",sub:"⊂",subdot:"⪽",subE:"⫅",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",Subset:"⋐",subset:"⊂",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",Sum:"∑",sum:"∑",sung:"♪",Sup:"⋑",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",Supset:"⋑",supset:"⊃",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swArr:"⇙",swarr:"↙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"τ",tbrk:"⎴",Tcaron:"Ť",tcaron:"ť",Tcedil:"Ţ",tcedil:"ţ",Tcy:"Т",tcy:"т",tdot:"⃛",telrec:"⌕",Tfr:"𝔗",tfr:"𝔱",there4:"∴",Therefore:"∴",therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",thinsp:" ",ThinSpace:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",Tilde:"∼",tilde:"˜",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",Topf:"𝕋",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",TRADE:"™",trade:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",Tscr:"𝒯",tscr:"𝓉",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"ћ",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",Uarr:"↟",uArr:"⇑",uarr:"↑",Uarrocir:"⥉",Ubrcy:"Ў",ubrcy:"ў",Ubreve:"Ŭ",ubreve:"ŭ",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"𝔘",ufr:"𝔲",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",Umacr:"Ū",umacr:"ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"𝕌",uopf:"𝕦",UpArrow:"↑",Uparrow:"⇑",uparrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",UpDownArrow:"↕",Updownarrow:"⇕",updownarrow:"↕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",Upsi:"ϒ",upsi:"υ",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",UpTee:"⊥",UpTeeArrow:"↥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",Uring:"Ů",uring:"ů",urtri:"◹",Uscr:"𝒰",uscr:"𝓊",utdot:"⋰",Utilde:"Ũ",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",vArr:"⇕",varr:"↕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",Vbar:"⫫",vBar:"⫨",vBarv:"⫩",Vcy:"В",vcy:"в",VDash:"⊫",Vdash:"⊩",vDash:"⊨",vdash:"⊢",Vdashl:"⫦",Vee:"⋁",vee:"∨",veebar:"⊻",veeeq:"≚",vellip:"⋮",Verbar:"‖",verbar:"|",Vert:"‖",vert:"|",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"𝕍",vopf:"𝕧",vprop:"∝",vrtri:"⊳",Vscr:"𝒱",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Ŵ",wcirc:"ŵ",wedbar:"⩟",Wedge:"⋀",wedge:"∧",wedgeq:"≙",weierp:"℘",Wfr:"𝔚",wfr:"𝔴",Wopf:"𝕎",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"𝒲",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",Xfr:"𝔛",xfr:"𝔵",xhArr:"⟺",xharr:"⟷",Xi:"Ξ",xi:"ξ",xlArr:"⟸",xlarr:"⟵",xmap:"⟼",xnis:"⋻",xodot:"⨀",Xopf:"𝕏",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrArr:"⟹",xrarr:"⟶",Xscr:"𝒳",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",Yacute:"Ý",yacute:"ý",YAcy:"Я",yacy:"я",Ycirc:"Ŷ",ycirc:"ŷ",Ycy:"Ы",ycy:"ы",yen:"¥",Yfr:"𝔜",yfr:"𝔶",YIcy:"Ї",yicy:"ї",Yopf:"𝕐",yopf:"𝕪",Yscr:"𝒴",yscr:"𝓎",YUcy:"Ю",yucy:"ю",Yuml:"Ÿ",yuml:"ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Ż",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",Zfr:"ℨ",zfr:"𝔷",ZHcy:"Ж",zhcy:"ж",zigrarr:"⇝",Zopf:"ℤ",zopf:"𝕫",Zscr:"𝒵",zscr:"𝓏",zwj:"‍",zwnj:"‌"}},function(e,t,n){"use strict";var r=n(419),o=n(27).unescapeMd;e.exports=function(e,t){var n,i,a,u=t,s=e.posMax;if(60===e.src.charCodeAt(t)){for(t++;t8&&n<14);)if(92===n&&t+11)break;if(41===n&&--i<0)break;t++}return u!==t&&(a=o(e.src.slice(u,t)),!!e.parser.validateLink(a)&&(e.linkContent=a,e.pos=t,!0))}},function(e,t,n){"use strict";var r=n(27).replaceEntities;e.exports=function(e){var t=r(e);try{t=decodeURI(t)}catch(e){}return encodeURI(t)}},function(e,t,n){"use strict";var r=n(27).unescapeMd;e.exports=function(e,t){var n,o=t,i=e.posMax,a=e.src.charCodeAt(t);if(34!==a&&39!==a&&40!==a)return!1;for(t++,40===a&&(a=41);t1?r-1:0),i=1;i1?t-1:0),r=1;r0?Array(e+1).join(" ")+t:t}).join("\n")}(0,(0,r.default)(a,null,2))||"{}",c.default.createElement("br",null)))}}]),t}(l.Component);t.default=p},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=c(n(4)),o=c(n(2)),i=c(n(3)),a=c(n(5)),u=c(n(6)),s=c(n(0)),l=n(7);c(n(1)),c(n(11));function c(e){return e&&e.__esModule?e:{default:e}}var f=function(e){function t(){var e,n,i,u;(0,o.default)(this,t);for(var s=arguments.length,l=Array(s),c=0;c=e.length?(this._t=void 0,o(1)):o(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){"use strict";var r=n(159),o=n(95),i=n(97),a={};n(50)(a,n(19)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var r=n(40),o=n(36),i=n(96);e.exports=n(44)?Object.defineProperties:function(e,t){o(e);for(var n,a=i(t),u=a.length,s=0;u>s;)r.f(e,n=a[s++],t[n]);return e}},function(e,t,n){var r=n(70),o=n(115),i=n(455);e.exports=function(e){return function(t,n,a){var u,s=r(t),l=o(s.length),c=i(a,l);if(e&&n!=n){for(;l>c;)if((u=s[c++])!=u)return!0}else for(;l>c;c++)if((e||c in s)&&s[c]===n)return e||c||0;return!e&&-1}}},function(e,t,n){var r=n(160),o=Math.max,i=Math.min;e.exports=function(e,t){return(e=r(e))<0?o(e+t,0):i(e,t)}},function(e,t,n){var r=n(160),o=n(155);e.exports=function(e){return function(t,n){var i,a,u=String(o(t)),s=r(n),l=u.length;return s<0||s>=l?e?"":void 0:(i=u.charCodeAt(s))<55296||i>56319||s+1===l||(a=u.charCodeAt(s+1))<56320||a>57343?e?u.charAt(s):i:e?u.slice(s,s+2):a-56320+(i-55296<<10)+65536}}},function(e,t,n){var r=n(36),o=n(164);e.exports=n(15).getIterator=function(e){var t=o(e);if("function"!=typeof t)throw TypeError(e+" is not iterable!");return r(t.call(e))}},function(e,t,n){n(459),n(244),n(470),n(474),n(485),n(486),e.exports=n(60).Promise},function(e,t,n){"use strict";var r=n(166),o={};o[n(17)("toStringTag")]="z",o+""!="[object z]"&&n(72)(Object.prototype,"toString",function(){return"[object "+r(this)+"]"},!0)},function(e,t,n){e.exports=!n(100)&&!n(101)(function(){return 7!=Object.defineProperty(n(168)("div"),"a",{get:function(){return 7}}).a})},function(e,t,n){var r=n(73);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if("function"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t,n){"use strict";var r=n(463),o=n(243),i=n(170),a={};n(58)(a,n(17)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var r=n(59),o=n(464),i=n(250),a=n(169)("IE_PROTO"),u=function(){},s=function(){var e,t=n(168)("iframe"),r=i.length;for(t.style.display="none",n(251).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write("