From 493961ad0b011dbd052094eabf76bec09e4f184c Mon Sep 17 00:00:00 2001 From: Alfredo Oliviero Date: Tue, 6 Feb 2024 16:26:56 +0100 Subject: [PATCH] first commit. smartgear service, keycloack client prototipe --- .gitignore | 171 +++ .vscode/settings.json | 4 + CHANGELOG.md | 9 + Dockerfile | 11 + FUNDING.md | 26 + LICENSE.md | 312 +++++ README.md | 49 + api.md | 21 + appunti.md | 23 + docker/container.ini | 25 + docker/logback.xml | 25 + docs/.gitignore | 1 + docs/Makefile | 20 + docs/conf.py | 54 + docs/index.md | 484 +++++++ docs/index.rst | 510 +++++++ docs/make.bat | 35 + docs/tests.rst | 23 + enunciate.xml | 23 + gcube/extra-resources/WEB-INF/gcube-app.xml | 10 + gcube/extra-resources/WEB-INF/web.xml | 16 + gcube/extra-resources/application.yaml | 6 + pom.xml | 209 +++ .../idm/IdentityManagerExceptionMapper.java | 38 + .../IdentityManagerResourceInitializer.java | 25 + .../idm/IdentityManagerdInitializator.java | 68 + .../org/gcube/idm/rest/GreetingsRest.java | 69 + .../java/org/gcube/idm/rest/UsersRest.java | 153 +++ .../org/gcube/keycloack/ErrorMessages.java | 34 + .../gcube/keycloack/KeycloackApiClient.java | 23 + .../KeycloackClientParams_UNUSED.java | 102 ++ .../gcube/keycloack/KeycloakAPIFactory.java | 147 ++ .../java/org/gcube/rest/ResponseBean.java | 71 + .../java/org/gcube/rest/annotation/PATCH.java | 17 + .../java/org/gcube/rest/annotation/PURGE.java | 17 + .../org/gcube/rest/utils/HTTPUtility.java | 66 + .../utils/IdmConstantsaa/IdmConstants.java | 12 + .../META-INF/enunciate/d4science_docs.fmt | 1183 +++++++++++++++++ src/main/webapp/WEB-INF/.gitignore | 5 + src/main/webapp/WEB-INF/web.xml | 19 + .../docs/css/d4science_enunciate_custom.css | 25 + 41 files changed, 4141 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 CHANGELOG.md create mode 100644 Dockerfile create mode 100644 FUNDING.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 api.md create mode 100644 appunti.md create mode 100644 docker/container.ini create mode 100644 docker/logback.xml create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.md create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/tests.rst create mode 100644 enunciate.xml create mode 100644 gcube/extra-resources/WEB-INF/gcube-app.xml create mode 100644 gcube/extra-resources/WEB-INF/web.xml create mode 100644 gcube/extra-resources/application.yaml create mode 100644 pom.xml create mode 100644 src/main/java/org/gcube/idm/IdentityManagerExceptionMapper.java create mode 100644 src/main/java/org/gcube/idm/IdentityManagerResourceInitializer.java create mode 100644 src/main/java/org/gcube/idm/IdentityManagerdInitializator.java create mode 100644 src/main/java/org/gcube/idm/rest/GreetingsRest.java create mode 100644 src/main/java/org/gcube/idm/rest/UsersRest.java create mode 100644 src/main/java/org/gcube/keycloack/ErrorMessages.java create mode 100644 src/main/java/org/gcube/keycloack/KeycloackApiClient.java create mode 100644 src/main/java/org/gcube/keycloack/KeycloackClientParams_UNUSED.java create mode 100644 src/main/java/org/gcube/keycloack/KeycloakAPIFactory.java create mode 100644 src/main/java/org/gcube/rest/ResponseBean.java create mode 100644 src/main/java/org/gcube/rest/annotation/PATCH.java create mode 100644 src/main/java/org/gcube/rest/annotation/PURGE.java create mode 100644 src/main/java/org/gcube/rest/utils/HTTPUtility.java create mode 100644 src/main/java/org/gcube/utils/IdmConstantsaa/IdmConstants.java create mode 100644 src/main/resources/META-INF/enunciate/d4science_docs.fmt create mode 100644 src/main/webapp/WEB-INF/.gitignore create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/docs/css/d4science_enunciate_custom.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..413a1b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,171 @@ +# originale +target +.classpath +.visual +.project +.settings +/**/.DS_Store + +# Created by https://www.toptal.com/developers/gitignore/api/java,macos,linux,visualstudiocode,eclipse +# Edit at https://www.toptal.com/developers/gitignore?templates=java,macos,linux,visualstudiocode,eclipse + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +### Eclipse Patch ### +# Spring Boot Tooling +.sts4-cache/ + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/java,macos,linux,visualstudiocode,eclipse diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d53ecaf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.compile.nullAnalysis.mode": "automatic", + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b140c02 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +# Changelog for Identity Manager Service + + +## [v1.0.0-SNAPSHOT] + +- First Version + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d601f78 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM smartgears-distribution:4.0.0-java11-tomcat9 +ARG REPOUSER=admin +ARG REPOPWD=admin +COPY ./target/identity-manager.war /tomcat/webapps/ +# COPY ./docker/storagehub.xml /tomcat/conf/Catalina/localhost/ +COPY ./docker/logback.xml /etc/ +COPY ./docker/container.ini /etc/ +RUN unzip /tomcat/webapps/identity-manager.war -d /tomcat/webapps/identity-manager +RUN rm /tomcat/webapps/identity-manager.war +# COPY ./docker/storage-settings.properties /tomcat/webapps/identity-manager/WEB-INF/classes/ +RUN sed -i "s/{{adminId}}/$REPOUSER/g; s/{{adminPwd}}/$REPOPWD/g" /tomcat/webapps/storagehub/WEB-INF/web.xml diff --git a/FUNDING.md b/FUNDING.md new file mode 100644 index 0000000..9e48b94 --- /dev/null +++ b/FUNDING.md @@ -0,0 +1,26 @@ +# Acknowledgments + +The projects leading to this software have received funding from a series of European Union programmes including: + +- the Sixth Framework Programme for Research and Technological Development + - [DILIGENT](https://cordis.europa.eu/project/id/004260) (grant no. 004260). +- the Seventh Framework Programme for research, technological development and demonstration + - [D4Science](https://cordis.europa.eu/project/id/212488) (grant no. 212488); + - [D4Science-II](https://cordis.europa.eu/project/id/239019) (grant no.239019); + - [ENVRI](https://cordis.europa.eu/project/id/283465) (grant no. 283465); + - [iMarine](https://cordis.europa.eu/project/id/283644) (grant no. 283644); + - [EUBrazilOpenBio](https://cordis.europa.eu/project/id/288754) (grant no. 288754). +- the H2020 research and innovation programme + - [SoBigData](https://cordis.europa.eu/project/id/654024) (grant no. 654024); + - [PARTHENOS](https://cordis.europa.eu/project/id/654119) (grant no. 654119); + - [EGI-Engage](https://cordis.europa.eu/project/id/654142) (grant no. 654142); + - [ENVRI PLUS](https://cordis.europa.eu/project/id/654182) (grant no. 654182); + - [BlueBRIDGE](https://cordis.europa.eu/project/id/675680) (grant no. 675680); + - [PerformFISH](https://cordis.europa.eu/project/id/727610) (grant no. 727610); + - [AGINFRA PLUS](https://cordis.europa.eu/project/id/731001) (grant no. 731001); + - [DESIRA](https://cordis.europa.eu/project/id/818194) (grant no. 818194); + - [ARIADNEplus](https://cordis.europa.eu/project/id/823914) (grant no. 823914); + - [RISIS 2](https://cordis.europa.eu/project/id/824091) (grant no. 824091); + - [EOSC-Pillar](https://cordis.europa.eu/project/id/857650) (grant no. 857650); + - [Blue Cloud](https://cordis.europa.eu/project/id/862409) (grant no. 862409); + - [SoBigData-PlusPlus](https://cordis.europa.eu/project/id/871042) (grant no. 871042); diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3af0507 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,312 @@ +# European Union Public Licence V. 1.1 + + +EUPL © the European Community 2007 + + +This European Union Public Licence (the “EUPL”) applies to the Work or Software +(as defined below) which is provided under the terms of this Licence. Any use of +the Work, other than as authorised under this Licence is prohibited (to the +extent such use is covered by a right of the copyright holder of the Work). + +The Original Work is provided under the terms of this Licence when the Licensor +(as defined below) has placed the following notice immediately following the +copyright notice for the Original Work: + +Licensed under the EUPL V.1.1 + +or has expressed by any other mean his willingness to license under the EUPL. + + + +## 1. Definitions + +In this Licence, the following terms have the following meaning: + +- The Licence: this Licence. + +- The Original Work or the Software: the software distributed and/or + communicated by the Licensor under this Licence, available as Source Code and + also as Executable Code as the case may be. + +- Derivative Works: the works or software that could be created by the Licensee, + based upon the Original Work or modifications thereof. This Licence does not + define the extent of modification or dependence on the Original Work required + in order to classify a work as a Derivative Work; this extent is determined by + copyright law applicable in the country mentioned in Article 15. + +- The Work: the Original Work and/or its Derivative Works. + +- The Source Code: the human-readable form of the Work which is the most + convenient for people to study and modify. + +- The Executable Code: any code which has generally been compiled and which is + meant to be interpreted by a computer as a program. + +- The Licensor: the natural or legal person that distributes and/or communicates + the Work under the Licence. + +- Contributor(s): any natural or legal person who modifies the Work under the + Licence, or otherwise contributes to the creation of a Derivative Work. + +- The Licensee or “You”: any natural or legal person who makes any usage of the + Software under the terms of the Licence. + +- Distribution and/or Communication: any act of selling, giving, lending, + renting, distributing, communicating, transmitting, or otherwise making + available, on-line or off-line, copies of the Work or providing access to its + essential functionalities at the disposal of any other natural or legal + person. + + + +## 2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, +sub-licensable licence to do the following, for the duration of copyright vested +in the Original Work: + +- use the Work in any circumstance and for all usage, reproduce the Work, modify +- the Original Work, and make Derivative Works based upon the Work, communicate +- to the public, including the right to make available or display the Work or +- copies thereof to the public and perform publicly, as the case may be, the +- Work, distribute the Work or copies thereof, lend and rent the Work or copies +- thereof, sub-license rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether now +known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non exclusive usage rights to +any patents held by the Licensor, to the extent necessary to make use of the +rights granted on the Work under this Licence. + + + +## 3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as +Executable Code. If the Work is provided as Executable Code, the Licensor +provides in addition a machine-readable copy of the Source Code of the Work +along with each copy of the Work that the Licensor distributes or indicates, in +a notice following the copyright notice attached to the Work, a repository where +the Source Code is easily and freely accessible for as long as the Licensor +continues to distribute and/or communicate the Work. + + + +## 4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits from +any exception or limitation to the exclusive rights of the rights owners in the +Original Work or Software, of the exhaustion of those rights or of other +applicable limitations thereto. + + + +## 5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: the Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the +disclaimer of warranties. The Licensee must include a copy of such notices and a +copy of the Licence with every copy of the Work he/she distributes and/or +communicates. The Licensee must cause any Derivative Work to carry prominent +notices stating that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes and/or communicates copies of the +Original Works or Derivative Works based upon the Original Work, this +Distribution and/or Communication will be done under the terms of this Licence +or of a later version of this Licence unless the Original Work is expressly +distributed only under this version of the Licence. The Licensee (becoming +Licensor) cannot offer or impose any additional terms or conditions on the Work +or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes and/or Communicates Derivative +Works or copies thereof based upon both the Original Work and another work +licensed under a Compatible Licence, this Distribution and/or Communication can +be done under the terms of this Compatible Licence. For the sake of this clause, +“Compatible Licence” refers to the licences listed in the appendix attached to +this Licence. Should the Licensee’s obligations under the Compatible Licence +conflict with his/her obligations under this Licence, the obligations of the +Compatible Licence shall prevail. + +Provision of Source Code: When distributing and/or communicating copies of the +Work, the Licensee will provide a machine-readable copy of the Source Code or +indicate a repository where this Source will be easily and freely available for +as long as the Licensee continues to distribute and/or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade names, +trademarks, service marks, or 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 copyright notice. + + + +## 6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent +Contributors grant You a licence to their contributions to the Work, under the +terms of this Licence. + + + +## 7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +contributors. It is not a finished work and may therefore contain defects or +“bugs” inherent to this type of software development. + +For the above reason, the Work is provided under the Licence on an “as is” basis +and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other than +copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + + + +## 8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the use +of the Work, including without limitation, damages for loss of goodwill, work +stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such +damage. However, the Licensor will be liable under statutory product liability +laws as far such laws apply to the Work. + + + +## 9. Additional agreements + +While distributing the Original Work or Derivative Works, You may choose to +conclude an additional agreement to offer, and charge a fee for, acceptance of +support, warranty, indemnity, or other liability obligations and/or services +consistent with this Licence. However, in accepting such obligations, You may +act only on your own behalf and on your sole responsibility, not on behalf of +the original Licensor or 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 the fact You have accepted +any such warranty or additional liability. + + + +## 10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon “I agree” +placed under the bottom of a window displaying the text of this Licence or by +affirming consent in any other similar way, in accordance with the rules of +applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and +conditions by exercising any rights granted to You by Article 2 of this Licence, +such as the use of the Work, the creation by You of a Derivative Work or the +Distribution and/or Communication by You of the Work or copies thereof. + + + +## 11. Information to the public + +In case of any Distribution and/or Communication of the Work by means of +electronic communication by You (for example, by offering to download the Work +from a remote location) the distribution channel or media (for example, a +website) must at least provide to the public the information requested by the +applicable law regarding the Licensor, the Licence and the way it may be +accessible, concluded, stored and reproduced by the Licensee. + + + +## 12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically upon +any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has +received the Work from the Licensee under the Licence, provided such persons +remain in full compliance with the Licence. + + + +## 13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work licensed hereunder. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as a +whole. Such provision will be construed and/or reformed so as necessary to make +it valid and enforceable. + +The European Commission may publish other linguistic versions and/or new +versions of this Licence, so far this is required and reasonable, without +reducing the scope of the rights granted by the Licence. New versions of the +Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version of +their choice. + + + +## 14. Jurisdiction + +Any litigation resulting from the interpretation of this License, arising +between the European Commission, as a Licensor, and any Licensee, will be +subject to the jurisdiction of the Court of Justice of the European Communities, +as laid down in article 238 of the Treaty establishing the European Community. + +Any litigation arising between Parties, other than the European Commission, and +resulting from the interpretation of this License, will be subject to the +exclusive jurisdiction of the competent court where the Licensor resides or +conducts its primary business. + + + +## 15. Applicable Law + +This Licence shall be governed by the law of the European Union country where +the Licensor resides or has his registered office. + +This licence shall be governed by the Belgian law if: + +- a litigation arises between the European Commission, as a Licensor, and any +- Licensee; the Licensor, other than the European Commission, has no residence +- or registered office inside a European Union country. + + + +## Appendix + + + +“Compatible Licences” according to article 5 EUPL are: + + +- GNU General Public License (GNU GPL) v. 2 + +- Open Software License (OSL) v. 2.1, v. 3.0 + +- Common Public License v. 1.0 + +- Eclipse Public License v. 1.0 + +- Cecill v. 2.0 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5b2e3a --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Identity Manager Service + +This service allows any client to publish on the gCube Catalogue. + +## Built With + +* [OpenJDK](https://openjdk.java.net/) - The JDK used +* [Maven](https://maven.apache.org/) - Dependency Management + +## Documentation + +[Identity Manager Service](https://wiki.gcube-system.org/gcube/SmartGears) + +## Change log + +See [CHANGELOG.md](CHANGELOG.md). + +## Authors + +* **Alfredo Oliviero** [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience) +* **Luca Frosini** ([ORCID](https://orcid.org/0000-0003-3183-2291)) - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience) + +## How to Cite this Software + +Tell people how to cite this software. +* Cite an associated paper? +* Use a specific BibTeX entry for the software? + + @software{gcat, + author = {{Alfredo Oliviero}}, + title = {Identity Manager Service}, + abstract = {This is an Identity Manager smargears service}, + url = {doi Zenodo URL} + keywords = {D4Science, gCube} + } + +## License + +This project is licensed under the EUPL V.1.1 License - see the [LICENSE.md](LICENSE.md) file for details. + + +## About the gCube Framework +This software is part of the [gCubeFramework](https://www.gcube-system.org/ "gCubeFramework"): an +open-source software toolkit used for building and operating Hybrid Data +Infrastructures enabling the dynamic deployment of Virtual Research Environments +by favouring the realisation of reuse oriented policies. + +The projects leading to this software have received funding from a series of European Union programmes see [FUNDING.md](FUNDING.md) + diff --git a/api.md b/api.md new file mode 100644 index 0000000..76dfd02 --- /dev/null +++ b/api.md @@ -0,0 +1,21 @@ + + +/2/users/get-profile // profilo utente corrente +/2/users/get-email // utente corrente +/2/users/get-fullname // utente corrente + +/2/users/get-all-usernames +/2/users/get-all-fullnames-and-usernames +/2/users/get-usernames-by-role +/2/users/user-exists + +// attenzione al risultato. vedere in seguito +/2/users/get-oauth-profile + +// eventualemente in seguito. da approfondire +/2/users/get-custom-attribute +/2/users/get-usernames-by-global-role + +rif. https://api.dev.d4science.org/social-networking-library-ws/api-docs/index.html + +implementazione social: https://code-repo.d4science.org/gCubeSystem/social-networking-library-ws/src/branch/master/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java diff --git a/appunti.md b/appunti.md new file mode 100644 index 0000000..84867e5 --- /dev/null +++ b/appunti.md @@ -0,0 +1,23 @@ +eseguendo ```mvn install``` su gcat, ricevo questo errore + +[ERROR] Failed to execute goal org.codehaus.gmaven:groovy-maven-plugin:2.1.1:execute (default) on project gcat: Execution default of goal org.codehaus.gmaven:groovy-maven-plugin:2.1.1:execute failed: startup failed: +[ERROR] script1.groovy: 1: expecting EOF, found 'matcher' @ line 1, column 100. +[ERROR] ANGELOG.md").getText('UTF-8') matcher = +[ERROR] ^ +[ERROR] +[ERROR] 1 error +[ERROR] + + +https://maven.d4science.org/nexus/content/repositories/gcube-externals/org/gcube/tools/maven-parent/1.1.0/maven-parent-1.1.0.pom + + + +cd ~/.m2/repository +grep -R "getText('UTF-8') matcher" . + + + def fileContents = new File("${project.basedir}/CHANGELOG.md").getText('UTF-8') matcher = (fileContents =~ /(?s).\[v$project.version\].*?/) if (!matcher.find()) { throw new IllegalArgumentException("Tag [v$project.version] not found in ${project.basedir}/CHANGELOG.md") } assert matcher[0][1]: "Tag [v$project.version] not found in ${project.basedir}/CHANGELOG.md" + + + diff --git a/docker/container.ini b/docker/container.ini new file mode 100644 index 0000000..4de8496 --- /dev/null +++ b/docker/container.ini @@ -0,0 +1,25 @@ +[node] +mode = online +hostname = alfredo-idm-service-dev +protocol= http +port = 8080 +infrastructure = gcube +authorizeChildrenContext = true +publicationFrequencyInSeconds = 60 + +[properties] +SmartGearsDistribution = 4.0.0-SNAPSHOT +SmartGearsDistributionBundle = UnBundled + +[site] +country = it +location = pisa + +[authorization] +factory = org.gcube.smartgears.security.defaults.DefaultAuthorizationProviderFactory +factory.endpoint = https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token +credentials.class = org.gcube.smartgears.security.SimpleCredentials +; credentials.clientID = node-whn-test-uno-d-d4s.d4science.org +; credentials.secret = 979bd3bc-5cc4-11ec-bf63-0242ac130002 +credentials.clientID = alfredo-idm-service-dev +credentials.secret = 979bd3bc-5cc4-11ec-bf63-0242ac130002 \ No newline at end of file diff --git a/docker/logback.xml b/docker/logback.xml new file mode 100644 index 0000000..906b20e --- /dev/null +++ b/docker/logback.xml @@ -0,0 +1,25 @@ + + + + Ï + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..ba65b13 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..cd70927 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,54 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'gCube Catalogue (gCat) Service' +copyright = '2022, Luca Frosini (ISTI-CNR)' +author = 'Luca Frosini (ISTI-CNR)' + +# The full version, including alpha/beta/rc tags +release = '2.5.1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinxdoc' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..0b79774 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,484 @@ +--- +title: Welcome to gCube Catalogue Service (aka gCat) documentation +--- + +gCat is a RESTful application which exposes operations via REST-API. + +See the available [REST-API docs](../api-docs/index.html). + +Base URL +======== + +In the production environment, its current value is + + +Authorization +============= + +D4Science adopts state-of-the-art industry standards for authentication +and authorization. Specifically, the implementation fully adopts [OIDC +(OpenID Connect)](https://openid.net/connect) for authentication and UMA +2 (User-Managed Authorization) for authorization flows. [JSON Web Token +(JWT) Access token](https://jwt.io/) are used for both authentication +and authorization. + +Obtain your Bearer token here: + + +Service +======= + +You can call the methods of the Web Service by writing your own REST +client application or using existing REST client plugins. + +HTTP Statuses +------------- + +Any successful operation returns *200 OK* HTTP status code. The create +operation returns *201 Created*. Any Background operation returns *202 +Accepted*. Any operation which does not provide any content return *204 +No Content*. + +The most common error status a client can obtain are: + +- **400 Bad Request** used to indicate a clients error + ; +- **401 Unauthorized** used to indicate that the client does not + provide the authorization token in the HTTP Header or the client has + not enough right to perform such request + ; +- **404 Not Found** used to indicate that the requested instance does + not exist ; +- **405 Method Not Allowed** the used HTTP method is not supported for + the requested URL + . The response + contains the *Allow* HTTP Header indicating the supported HTTP + method for such URL + ; +- **409 Conflict** the request could not be completed due to a + conflict with the current state of the target resource (e.g. the + name of the resource already exists) + ; +- **500 Internal Server Error** indicate a server failure + . + +You can find a complete list of HTTP Status at + + +If you get a *500 Internal Server Error*, please report it in the [gCube +ticketing system](https://support.d4science.org). + +Please use this checklist before reporting an error: + +- Replicate the request; +- The failure could be temporal due to network error, server issue and + many other temporal issues. For this reason, please retry the + request after a certain amount of time before reporting the issue; +- indicate how to replicate the error; +- indicate the time when the error occurred (this simplifies + identifying the issue). + +HTTP Methods +------------ + +gCat is a pure RESTful service. It uses standard HTTP Methods to perform +listing of collections and CRUD (Create Read Update Delete) operations +on instances. + +:::{table} Supported operations +:align: center +:widths: grid + + +| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent | +|-----------|-------------|-----|---------------------|------|------------| +| **Supported
HTTP Methods** | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y | +| **List** | GET | /{COLLECTION} | 200 OK | Y | Y | +| **Count** | GET | /{COLLECTION}?count=true | 200 OK | Y | Y | +| **Exists** | HEAD | /{COLLECTION} | 204 No Content | Y | Y | +| **Create** | POST | /{COLLECTION} | 201 Created | N | N | +| **Supported
HTTP Methods** | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | +| **Exist** | HEAD | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | +| **Read** | GET | /{COLLECTION}/{INSTANCE_ID} | 200 OK | Y | Y | +| **Update** | PUT | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | +| **Patch** | PATCH | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | +| **Delete** | DELETE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N | +| **Purge** | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N | +| **Purge** | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N | + + +### About URL + +The presented URL uses the following convention: + +- **{COLLECTION}** is the plural name of the entity type; +- **{INSTANCE\_ID}** is an identification that enables univocally + identifying the instance in the collection. + +### About Safety and Idempotency properties + +- A method is *Safe* if it does not produce any side effects. \"This + does not prevent an implementation from including behaviour that is + potentially harmful, that is not entirely read-only, or that causes + side effects while invoking a safe method\" + ; +- A method is *Idempotent* if the same operation repeated multiple + times has the same side effect than using it one time. \"repeating + the request will have the same intended effect, even if the original + request succeeded, though the response might differ\" + . + +You can find more information about HTTP Methods at + + +### Uncommon HTTP Methods + +- PATCH method allows to perform a differential update (i.e. an update + which provides only the differences and not the whole new + representation); +- PURGE method is not a standard but is widely used in service which + requires this action (e.g. + [Varnish](https://varnish-cache.org/docs/3.0/tutorial/purging.html), + [Squid](https://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F)). + gCat provides support for this method, but to support a wider range + of clients, it also provides the Purge action via *DELETE* with the + additional get parameter `purge=true`. + +Content-Type +------------ + +Any request must contain an indication of the interesting content type. + +The client must specify the **Accept** HTTP Header for any operation +returning a result. + +``` {.rest} +Accept: application/json +``` + +For any operation sending content to the service, it is necessary to +specify the **Content-Type** HTTP Header. + +``` {.rest} +Content-Type: application/json +``` + +The service accepts and returns only JSON objects. + +[Profile Collection](../api-docs/resource\_Profile.html) instead can be +manipulated in XML only. + +Collections +----------- + +The following collections are available to any user. Catalogue-Editor or +above can invoke Non-safe methods only. + +- [Item Collection](../api-docs/resource_Item.html); + - [Resource Collection](../api-docs/resource_Resource.html); +- [Profile Collection](../api-docs/resource_Profile.html); +- [Namespace Collection](../api-docs/resource_Namespace.html); +- [License Collection](../api-docs/resource_License.html); +- [Trash Collection](../api-docs/resource_Trash.html); + +The following collections are available for Catalogue-Admins or above +only: + +- [Group Collection](../api-docs/resource_Group.html); +- [Organization Collection](../api-docs/resource_Organization.html); +- [User Collection](../api-docs/resource_User.html); +- [Configuration Collection](../api-docs/resource_Configuration.html). + +An overview of the available collections is available at +[../api-docs/index.html](../api-docs/index.html); + +Roles +----- + +Any user has one or more roles in the catalogue. Only the VRE Manager +can assign roles to VRE users. + +The catalogue uses the following hierarchic roles: + +**Catalogue-Member**: + +: A user with such a role is mainly capable of listing and reading + items; + +**Catalogue-Editor**: + +: A user with such a role is capable of managing the items he/she + creates and capable of using other safe APIs; + +**Catalogue-Admin**: + +: A user with such a role is capable of administrating many aspects of + the catalogue; + +**Catalogue-Manager**: + +: A user with such a role can use all the APIs exposed by the service + except item moderation APIs (e.g. approve, reject, \...). + +Another role that is not in the role hierarchy: + +**Catalogue-Moderator**: + +: A user with such a role is capable of invoking the item moderation + APIs. + + ::: {.tip} + ::: {.title} + Tip + ::: + ::: + + Please note that not all catalogues are moderated. + +Moderated Catalogues +==================== + +Any catalogues can be declared as moderated. This means that, a +**Catalogue-Moderator** must approve any submitted items to make them +available to the other users of the catalogue. + +In a moderated catalogue, an item can be in the following states: + +**pending**: + +: The item published by any allowed author (a Catalogue-Editor or + above) but not available to the other users of the catalogue. A + Catalogue-Moderator has to approve or reject it; + +**approved**: + +: A Catalogue-Moderator has approved the item published by any allowed + users; + +**rejected**: + +: A Catalogue-Moderator has rejected the item published by any allowed + users. + +The following are the moderation operations that an allowed user can +perform on an item. To present the moderation operations, we use the +following convention: + +> `initial_state` \-\--**operation** (*User/Role performing the +> operation*)\-\--\> `final_state` + +`initial_state` can be `none`, meaning the item does not exist. + +The following are the allowed moderation operation on an item + +> `none` \-\--**create** (*Author*)\-\--\> `pending` +> +> `pending` \-\--**reject** (*Catalogue-Moderator*)\-\--\> `rejected` +> +> `pending` \-\--**approve** (*Catalogue-Moderator*)\-\--\> `approved` +> +> `rejected` \-\--**update** (*Author*)\-\--\> `pending` +> +> `approved` \-\--**update** (*Author*)\-\--\> `pending` + +Please check the table below whcih summarise the item collection +operation and the allowed users/roles. + +In a moderated catalogue, both the Catalogue-Moderators and the item +author can send messages to discuss the approval process of the item. +The messages are related to a specific item. Any Catalogue-Moderators +receive a message sent by an Author. The author receives a message sent +by a Catalogue-Moderator as well as the other Catalogue-Moderators (if +any). + +Messages can be sent both with an action which changes the status of the +item or as explicit action which does not change the status of the item: + +> `pending` \-\--**message** (*Author OR Catalogue-Moderator*)\-\--\> +> `pending` +> +> `rejected` \-\--**message** (*Author OR Catalogue-Moderator*)\-\--\> +> `rejected` +> +> `approved` \-\--**message** (*Author OR Catalogue-Moderator*)\-\--\> +> `approved` + +The following table summarize the allowed/forbidden operations depending +on: the role of the user and the state of the item. + +The Moderation process has associated notification to authors and +Catalogue-Moderators. Please note that the user who has acted is not +self-notified, e.g. approve operation made by a Catalogue-Moderator +notifies the item author and the other Catalogue-Moderators of the VRE. + +The following table summarises the addressee of the notification for any +action. + +Java Client +=========== + +We provide the following Java Client out-of-the-box. + +> ::: {.tip} +> ::: {.title} +> Tip +> ::: +> +> If you\'re coding in Java, it is recommended that you use this Java +> Client. +> ::: + +**Maven Coordinates** + +``` {.xml} +org.gcube.data-catalogue +gcat-client +[2.2.0, 3.0.0-SNAPSHOT) +``` + +**Methods Result** + +The service exposes [its methods](../api-docs/index.html) using a +standard naming approach. Moreover, they accept (in the case of HTTP +POST/PUT methods) JSON objects. + +> ::: {.important} +> ::: {.title} +> Important +> ::: +> +> The result of all methods is always a JSON object as per below: +> ::: + +``` {.javascript} +{ + "rating": 0.0, + "license_title": "Creative Commons Attribution Share-Alike 4.0", + "maintainer": "Frosini Luca", + "relationships_as_object": [], + "private": false, + "maintainer_email": "luca.frosini@isti.cnr.it", + "num_tags": 1, + "id": "17051d86-c127-4928-9296-d3d7590161fe", + "metadata_created": "2022-10-17T12:45:53.118318", + "metadata_modified": "2022-10-18T10:30:03.362756", + "author": "Frosini Luca", + "author_email": "luca.frosini@isti.cnr.it", + "state": "active", + "version": null, + "creator_user_id": "f1b0265c-9983-4f97-a7b6-be3cc0544b27", + "type": "dataset", + "resources": [], + "num_resources": 0, + "tags": [ + { + "vocabulary_id": null, + "state": "active", + "display_name": "Test", + "id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d", + "name": "Test" + } + ], + "groups": [], + "license_id": "CC-BY-SA-4.0", + "relationships_as_subject": [], + "organization": { + "description": "", + "created": "2016-05-30T11:30:41.710079", + "title": "devVRE", + "name": "devvre", + "is_organization": true, + "state": "active", + "image_url": "", + "revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03", + "type": "organization", + "id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5", + "approval_status": "approved" + }, + "name": "my_test_item_devvre", + "isopen": true, + "url": "http://www.d4science.org", + "notes": "A test item of Luca Frosini", + "extras": [ + { + "key": "Item URL", + "value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item_devvre" + }, + { + "key": "Language", + "value": "EN" + }, + { + "key": "system:cm_item_status", + "value": "approved" + }, + { + "key": "system:cm_item_visibility", + "value": "public" + }, + { + "key": "system:type", + "value": "EmptyProfile" + } + ], + "license_url": "https://creativecommons.org/licenses/by-sa/4.0/", + "ratings_count": 0, + "title": "My Test Item", + "revision_id": "bc0d1f2a-4e97-4810-b951-8b72e8279719" +} +``` + +*Inputs are automatically validated before the request is served.* + +**Usage examples** + +- Example 1 + +``` {.java} +import org.gcube.gcat.client.Item; + +// count item number +Item item = new Item(); +int count = item.count(); +... +``` + +Service Discovery on IS +======================= + +The service can be discovered in the gCore IS as gCore Endpoint with the +following parameter: + +``` {.xml} +org.gcube.data-catalogue +gcat +``` + +The service can be discovered in the Facet Based IS as EService with the +following json query: + +``` {.json} +{ + "@class": "EService", + "consistsOf": [ + { + "@class": "IsIdentifiedBy", + "target": { + "@class": "SoftwareFacet", + "group": "org.gcube.data-catalogue", + "name": "gcat" + } + } + ] +} +``` + +Service Maven Coordinates +========================= + +The maven coordinates of gCat service are: + +``` {.xml} +org.gcube.data-catalogue +gcat +``` diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..0ec47bf --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,510 @@ + +*********************************************************** +Welcome to gCube Catalogue Service (aka gCat) documentation +*********************************************************** + +gCat is a RESTful application which exposes operations via REST-API. + +See the available `REST-API docs <../api-docs/index.html>`_. + +Base URL +======== + +In the production environment, its current value is https://api.d4science.org/gcat + + +Authorization +============= + +D4Science adopts state-of-the-art industry standards for authentication and authorization. +Specifically, the implementation fully adopts `OIDC (OpenID Connect) `_ for authentication and UMA 2 (User-Managed Authorization) for authorization flows. +`JSON Web Token (JWT) Access token `_ are used for both authentication and authorization. + +Obtain your Bearer token here: https://dev.d4science.org/how-to-access-resources + +Service +======= + +You can call the methods of the Web Service by writing your own REST client application or using existing REST client plugins. + + +HTTP Statuses +------------- + +Any successful operation returns *200 OK* HTTP status code. +The create operation returns *201 Created*. +Any Background operation returns *202 Accepted*. +Any operation which does not provide any content return *204 No Content*. + + + +The most common error status a client can obtain are: + +* **400 Bad Request** used to indicate a clients error ``_; +* **401 Unauthorized** used to indicate that the client does not provide the authorization token in the HTTP Header or the client has not enough right to perform such request ``_; +* **404 Not Found** used to indicate that the requested instance does not exist ``_; +* **405 Method Not Allowed** the used HTTP method is not supported for the requested URL ``_. + The response contains the *Allow* HTTP Header indicating the supported HTTP method for such URL ``_; +* **409 Conflict** the request could not be completed due to a conflict with the current state of the target resource (e.g. the name of the resource already exists) ``_; +* **500 Internal Server Error** indicate a server failure ``_. + +You can find a complete list of HTTP Status at ``_ + +If you get a *500 Internal Server Error*, please report it in the `gCube ticketing system `_. + +Please use this checklist before reporting an error: + +* Replicate the request; +* The failure could be temporal due to network error, server issue and many other temporal issues. For this reason, please retry the request after a certain amount of time before reporting the issue; +* indicate how to replicate the error; +* indicate the time when the error occurred (this simplifies identifying the issue). + +HTTP Methods +------------ + +gCat is a pure RESTful service. It uses standard HTTP Methods to perform a listing of collections and CRUD (Create Read Update Delete) operations on instances. + + +.. table:: + + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent | + +==============+=============+========================================+=====================+========+============+ + | Supported | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y | + | HTTP Methods | | | [#allow]_ | | | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | List | GET | /{COLLECTION} | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Count | GET | /{COLLECTION}?count=true | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Exists | HEAD | /{COLLECTION} | 204 No Content | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Create | POST | /{COLLECTION} | 201 Created | N | N | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Supported | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | + | HTTP Methods | | | [#allow]_ | | | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Exist | HEAD | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Read | GET | /{COLLECTION}/{INSTANCE_ID} | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Update | PUT | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Patch | PATCH | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Delete | DELETE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Purge | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N [#del]_ | + + +-------------+----------------------------------------+---------------------+--------+------------+ + | | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + +.. [#allow] Supported HTTP Methods in **Allow** HTTP Header + +.. [#del] DELETE has been defined as idempotent. + + *Allamaraju* [#Allamaraju]_ argues that DELETE idempotency should be accomplished client-side. + The server should inform the client if the delete operation succeeded because the resource was really deleted or it was not found, i.e., **404 Not Found** error is suggested instead of **204 No Content**. + The latter situation should be treated as idempotent by the client. + + We share the same vision. For this reason, gCat does not provide server-side idempotency for DELETE and PURGE operations. + +.. [#Allamaraju] Allamaraju S. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity . O’Reilly. first ed. 2010 + + +About URL +^^^^^^^^^ + +The presented URL uses the following convention: + +* **{COLLECTION}** is the plural name of the entity type; +* **{INSTANCE_ID}** is an identification that enables univocally identifying the instance in the collection. + + +About Safety and Idempotency properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +* A method is *Safe* if it does not produce any side effects. + "This does not prevent an implementation from including behaviour that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method" + ``_; +* A method is *Idempotent* if the same operation repeated multiple times has the same side effect than using it one time. + "repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ" + ``_. + +You can find more information about HTTP Methods at ``_ + +Uncommon HTTP Methods +^^^^^^^^^^^^^^^^^^^^^ + +* PATCH method allows to perform a differential update (i.e. an update which provides only the differences and not the whole new representation); +* PURGE method is not a standard but is widely used in service which requires this action + (e.g. `Varnish `_, `Squid `_). + gCat provides support for this method, but to support a wider range of clients, it also provides the Purge action via *DELETE* with the additional get parameter ``purge=true``. + + +Content-Type +------------ + +Any request must contain an indication of the interesting content type. + +The client must specify the **Accept** HTTP Header for any operation returning a result. + +.. code-block:: rest + + Accept: application/json + +For any operation sending content to the service, it is necessary to specify the **Content-Type** HTTP Header. + +.. code-block:: rest + + Content-Type: application/json + +The service accepts and returns only JSON objects. + +`Profile Collection <../api-docs/resource\_Profile.html>`_ instead can be manipulated in XML only. + +Collections +----------- + +The following collections are available to any user. +Catalogue-Editor or above can invoke Non-safe methods only. + +* `Item Collection <../api-docs/resource_Item.html>`_; + + * `Resource Collection <../api-docs/resource_Resource.html>`_; + +* `Profile Collection <../api-docs/resource_Profile.html>`_; +* `Namespace Collection <../api-docs/resource_Namespace.html>`_; +* `License Collection <../api-docs/resource_License.html>`_; +* `Trash Collection <../api-docs/resource_Trash.html>`_; + +The following collections are available for Catalogue-Admins or above only: + +* `Group Collection <../api-docs/resource_Group.html>`_; +* `Organization Collection <../api-docs/resource_Organization.html>`_; +* `User Collection <../api-docs/resource_User.html>`_; +* `Configuration Collection <../api-docs/resource_Configuration.html>`_. + +An overview of the available collections is available at `<../api-docs/index.html>`_; + + +Roles +----- + +Any user has one or more roles in the catalogue. +Only the VRE Manager can assign roles to VRE users. + + +The catalogue uses the following hierarchic roles: + +**Catalogue-Member**: + A user with such a role is mainly capable of listing and reading items; + +**Catalogue-Editor**: + A user with such a role is capable of managing the items he/she creates and capable of using other safe APIs; + +**Catalogue-Admin**: + A user with such a role is capable of administrating many aspects of the catalogue; + +**Catalogue-Manager**: + A user with such a role can use all the APIs exposed by the service except item moderation APIs (e.g. approve, reject, ...). + + +Another role that is not in the role hierarchy: + +**Catalogue-Moderator**: + A user with such a role is capable of invoking the item moderation APIs. + + + .. TIP:: + Please note that not all catalogues are moderated. + +Moderated Catalogues +==================== + + +Any catalogues can be declared as moderated. +This means that, a **Catalogue-Moderator** must approve any submitted items to make them available to the other users of the catalogue. + +In a moderated catalogue, an item can be in the following states: + +**pending**: + The item published by any allowed author (a Catalogue-Editor or above) but not available to the other users of the catalogue. + A Catalogue-Moderator has to approve or reject it; + +**approved**: + A Catalogue-Moderator has approved the item published by any allowed users; + +**rejected**: + A Catalogue-Moderator has rejected the item published by any allowed users. + + + +The following are the moderation operations that an allowed user can perform on an item. +To present the moderation operations, we use the following convention: + + ``initial_state`` ---**operation** (*User/Role performing the operation*)---> ``final_state`` + + +``initial_state`` can be ``none``, meaning the item does not exist. + + +The following are the allowed moderation operation on an item + + ``none`` ---**create** (*Author*)---> ``pending`` + + ``pending`` ---**reject** (*Catalogue-Moderator*)---> ``rejected`` + + ``pending`` ---**approve** (*Catalogue-Moderator*)---> ``approved`` + + ``rejected`` ---**update** (*Author*)---> ``pending`` + + ``approved`` ---**update** (*Author*)---> ``pending`` + + +Please check the table below whcih summarise the item collection operation and the allowed users/roles. + +In a moderated catalogue, both the Catalogue-Moderators and the item author can send messages to +discuss the approval process of the item. The messages are related to a specific item. +Any Catalogue-Moderators receive a message sent by an Author. +The author receives a message sent by a Catalogue-Moderator as well as the other Catalogue-Moderators (if any). + +Messages can be sent both with an action which changes the status of the item or as explicit action which does not change the status of the item: + + ``pending`` ---**message** (*Author OR Catalogue-Moderator*)---> ``pending`` + + ``rejected`` ---**message** (*Author OR Catalogue-Moderator*)---> ``rejected`` + + ``approved`` ---**message** (*Author OR Catalogue-Moderator*)---> ``approved`` + + + +The following table summarize the allowed/forbidden operations depending on: the role of the user and the state of the item. + +.. table:: + + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Operation | Item State | Roles | + + + +----------------------+--------------------------+------------------------------------------+-------------------+ + | | | Catalogue Moderator | Catalogue Admin/Manager | Catalogue Editor | Catalogue Member | + +=====================================+=============+======================+==========================+==========================================+===================+ + | List | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Count | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Create | 403 Forbidden | Yes -> Pending | Yes -> Pending | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Read | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Update | Pending | Yes -> Pending | Yes if Author -> Pending | Yes if Author -> Pending | 403 Forbidden | + + +-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | | Rejected | Yes -> Pending | Yes if Author -> Pending | Yes if Author -> Pending | 403 Forbidden | + + +-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | | Approved | 403 Forbidden | Yes -> Approved | Yes if Author -> Pending | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Delete/Purge | Pending | Yes | Yes | Yes if Author | 403 Forbidden | + + +-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | | Rejected | Yes | Yes | Yes if Author | 403 Forbidden | + + +-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | | Approved | 403 Forbidden | Yes | Yes if Author | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Approve a pending item | Yes | 403 Forbidden | 403 Forbidden | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Reject a pending item | Yes | 403 Forbidden | 403 Forbidden | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + | Message about an item | Yes | Yes if Author | Yes if Author | 403 Forbidden | + +-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+ + + +The Moderation process has associated notification to authors and Catalogue-Moderators. +Please note that the user who has acted is not self-notified, e.g. +approve operation made by a Catalogue-Moderator notifies the item author and the other Catalogue-Moderators of the VRE. + +The following table summarises the addressee of the notification for any action. + + +.. table:: + + +--------------+----------------------+--------+----------------------+ + | Operation | Notified user/role | + + +----------------------+--------+----------------------+ + | | Catalogue-Moderators | Author | User made the action | + +==============+======================+========+======================+ + | Create | Yes | Yes | Yes (custom message) | + +--------------+----------------------+--------+----------------------+ + | Update | Yes | Yes | Yes (custom message) | + +--------------+----------------------+--------+----------------------+ + | Approve | Yes + Social Post if | No | + | | requested (social_post=true) | | + | | and enabled for the VRE | | + +--------------+----------------------+--------+----------------------+ + | Reject | Yes | Yes | No | + +--------------+----------------------+--------+----------------------+ + | Delete/Purge | Yes | Yes | No | + +--------------+----------------------+--------+----------------------+ + | Message | Yes | Yes | No | + +--------------+----------------------+--------+----------------------+ + + + +Java Client +=========== + +We provide the following Java Client out-of-the-box. + + .. TIP:: + If you're coding in Java, it is recommended that you use this Java Client. + +**Maven Coordinates** + +.. code:: xml + + org.gcube.data-catalogue + gcat-client + [2.2.0, 3.0.0-SNAPSHOT) + +**Methods Result** + +The service exposes `its methods <../api-docs/index.html>`_ using a standard naming approach. Moreover, they accept (in the case of HTTP POST/PUT methods) JSON objects. + + .. IMPORTANT:: + The result of all methods is always a JSON object as per below: + +.. code:: javascript + + { + "rating": 0.0, + "license_title": "Creative Commons Attribution Share-Alike 4.0", + "maintainer": "Frosini Luca", + "relationships_as_object": [], + "private": false, + "maintainer_email": "luca.frosini@isti.cnr.it", + "num_tags": 1, + "id": "17051d86-c127-4928-9296-d3d7590161fe", + "metadata_created": "2022-10-17T12:45:53.118318", + "metadata_modified": "2022-10-18T10:30:03.362756", + "author": "Frosini Luca", + "author_email": "luca.frosini@isti.cnr.it", + "state": "active", + "version": null, + "creator_user_id": "f1b0265c-9983-4f97-a7b6-be3cc0544b27", + "type": "dataset", + "resources": [], + "num_resources": 0, + "tags": [ + { + "vocabulary_id": null, + "state": "active", + "display_name": "Test", + "id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d", + "name": "Test" + } + ], + "groups": [], + "license_id": "CC-BY-SA-4.0", + "relationships_as_subject": [], + "organization": { + "description": "", + "created": "2016-05-30T11:30:41.710079", + "title": "devVRE", + "name": "devvre", + "is_organization": true, + "state": "active", + "image_url": "", + "revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03", + "type": "organization", + "id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5", + "approval_status": "approved" + }, + "name": "my_test_item_devvre", + "isopen": true, + "url": "http://www.d4science.org", + "notes": "A test item of Luca Frosini", + "extras": [ + { + "key": "Item URL", + "value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item_devvre" + }, + { + "key": "Language", + "value": "EN" + }, + { + "key": "system:cm_item_status", + "value": "approved" + }, + { + "key": "system:cm_item_visibility", + "value": "public" + }, + { + "key": "system:type", + "value": "EmptyProfile" + } + ], + "license_url": "https://creativecommons.org/licenses/by-sa/4.0/", + "ratings_count": 0, + "title": "My Test Item", + "revision_id": "bc0d1f2a-4e97-4810-b951-8b72e8279719" + } + +*Inputs are automatically validated before the request is served.* + + +**Usage examples** + +- Example 1 + +.. code:: java + + import org.gcube.gcat.client.Item; + + // count item number + Item item = new Item(); + int count = item.count(); + ... + + + +Service Discovery on IS +======================= + +The service can be discovered in the gCore IS as gCore Endpoint with the following parameter: + +.. code:: xml + + org.gcube.data-catalogue + gcat + + +The service can be discovered in the Facet Based IS as EService with the following json query: + +.. code:: json + + { + "@class": "EService", + "consistsOf": [ + { + "@class": "IsIdentifiedBy", + "target": { + "@class": "SoftwareFacet", + "group": "org.gcube.data-catalogue", + "name": "gcat" + } + } + ] + } + + + +Service Maven Coordinates +========================= + +The maven coordinates of gCat service are: + +.. code:: xml + + org.gcube.data-catalogue + gcat + \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..153be5e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/tests.rst b/docs/tests.rst new file mode 100644 index 0000000..fca9245 --- /dev/null +++ b/docs/tests.rst @@ -0,0 +1,23 @@ +Dev and Pre Users used for moderation tests +======== + +To perform moderation tests in dev and preproduction infrastructure we use different users with the indicated roles. + +.. table:: + + +---------------+---------------+----------------------------------------+ + | User | Username | Role | + +===============+===============+========================================+ + | Mister Blonde | mister.blonde | Catalogue-Admin + Catalogue-Moderator | + +---------------+---------------+----------------------------------------+ + | Mister Blue | mister.blue | Catalogue-Admin | + +---------------+---------------+----------------------------------------+ + | Mister Brown | mister.brown | Catalogue-Moderator | + +---------------+---------------+----------------------------------------+ + | Mister Orange | mister.orange | Catalogue-Editor | + +---------------+---------------+----------------------------------------+ + | Mister Pink | mister.pink | NO ROLE (means Catalogue-Member) | + +---------------+---------------+----------------------------------------+ + | Mister White | mister.white | Catalogue-Manager | + +---------------+---------------+----------------------------------------+ + diff --git a/enunciate.xml b/enunciate.xml new file mode 100644 index 0000000..eb0f240 --- /dev/null +++ b/enunciate.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gcube/extra-resources/WEB-INF/gcube-app.xml b/gcube/extra-resources/WEB-INF/gcube-app.xml new file mode 100644 index 0000000..c056b4f --- /dev/null +++ b/gcube/extra-resources/WEB-INF/gcube-app.xml @@ -0,0 +1,10 @@ + + + + ${project.artifactId} + ${project.groupId} + ${project.version} + ${project.description} + /api-docs.* + /docs.* + \ No newline at end of file diff --git a/gcube/extra-resources/WEB-INF/web.xml b/gcube/extra-resources/WEB-INF/web.xml new file mode 100644 index 0000000..9435c69 --- /dev/null +++ b/gcube/extra-resources/WEB-INF/web.xml @@ -0,0 +1,16 @@ + + + + + org.gcube.data.access.storagehub.StorageHub + + + + org.gcube.data.access.storagehub.StorageHub + /workspace/* + + + \ No newline at end of file diff --git a/gcube/extra-resources/application.yaml b/gcube/extra-resources/application.yaml new file mode 100644 index 0000000..5be945c --- /dev/null +++ b/gcube/extra-resources/application.yaml @@ -0,0 +1,6 @@ +name: IdentityManagerService +group: IAM +version: ${version} +description: ${description} +excludes: + - path: /workspace/api-docs/* \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3b21f70 --- /dev/null +++ b/pom.xml @@ -0,0 +1,209 @@ + + 4.0.0 + + org.gcube.tools + maven-parent + 1.2.0 + + org.gcube.idm + identity-manager + 1.0.0-SNAPSHOT + war + Identity Manager Service + Identity Manager Smargears Service + + + UTF-8 + 11 + 11 + + ${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF + 2.14.0 + + + + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + https://code-repo.d4science.org/gCubeSystem/${project.artifactId} + + + + + + org.gcube.distribution + gcube-smartgears-bom + 3.0.1-SNAPSHOT + pom + import + + + + + + + org.slf4j + slf4j-api + + + org.gcube.common + common-security + + + org.gcube.common + keycloak-client + + + org.keycloak + keycloak-admin-client + 21.0.1 + + + org.glassfish.jersey.media + jersey-media-multipart + + + org.glassfish.jersey.containers + jersey-container-servlet + + + javax.ws.rs + javax.ws.rs-api + + + + org.gcube.core + common-smartgears-app + + + + org.gcube.core + common-smartgears + + + + + + com.webcohesion.enunciate + enunciate-core-annotations + ${enunciate.version} + provided + + + com.webcohesion.enunciate + enunciate-rt-util + ${enunciate.version} + provided + + + javax.servlet + javax.servlet-api + 3.1.0 + provided + + + + + + junit + junit + 4.11 + test + + + ch.qos.logback + logback-classic + test + + + org.gcube.portlets.user + uri-resolver-manager + [1.0.0, 2.0.0-SNAPSHOT) + + + + + + ${project.artifactId} + + + + kr.motd.maven + sphinx-maven-plugin + 2.10.0 + + + ${project.build.directory}/${project.artifactId}-${project.version}/docs + html + ${basedir}/docs + ${basedir}/docs + + + + process-resources + + generate + + + + + + + + + com.webcohesion.enunciate + enunciate-maven-plugin + ${enunciate.version} + + + assemble + + assemble + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-enunciate-docs + process-resources + + copy-resources + + + target + + + + ${project.build.directory}/${project.artifactId}-${project.version}/api-docs + + ${project.build.directory}/api-docs + true + + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + + true + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/idm/IdentityManagerExceptionMapper.java b/src/main/java/org/gcube/idm/IdentityManagerExceptionMapper.java new file mode 100644 index 0000000..fd68b3e --- /dev/null +++ b/src/main/java/org/gcube/idm/IdentityManagerExceptionMapper.java @@ -0,0 +1,38 @@ +package org.gcube.idm; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +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; + +/** + * @author Alfredo Oliviero (ISTI - CNR) + */ +@Provider +public class IdentityManagerExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(Exception exception) { + + Status status = Status.INTERNAL_SERVER_ERROR; + String exceptionMessage = exception.getMessage(); + try { + if(exception.getCause() != null) { + exceptionMessage = exception.getCause().getMessage(); + } + } catch(Exception e) { + exceptionMessage = exception.getMessage(); + } + MediaType mediaType = MediaType.TEXT_PLAIN_TYPE; + + if(WebApplicationException.class.isAssignableFrom(exception.getClass())) { + Response gotResponse = ((WebApplicationException) exception).getResponse(); + status = Status.fromStatusCode(gotResponse.getStatusInfo().getStatusCode()); + } + + return Response.status(status).entity(exceptionMessage).type(mediaType).build(); + } + +} diff --git a/src/main/java/org/gcube/idm/IdentityManagerResourceInitializer.java b/src/main/java/org/gcube/idm/IdentityManagerResourceInitializer.java new file mode 100644 index 0000000..6b873a8 --- /dev/null +++ b/src/main/java/org/gcube/idm/IdentityManagerResourceInitializer.java @@ -0,0 +1,25 @@ +package org.gcube.idm; + +import javax.ws.rs.ApplicationPath; + +import org.gcube.idm.rest.GreetingsRest; +import org.gcube.smartgears.annotations.ManagedBy; +import org.glassfish.jersey.server.ResourceConfig; + +/** + * @author Alfredo Oliviero (ISTI - CNR) + */ + + + // SMARTGEARS +// legge i parametri del service da application.yaml + +@ApplicationPath("/") +@ManagedBy(IdentityManagerdInitializator.class) +public class IdentityManagerResourceInitializer extends ResourceConfig { + + public IdentityManagerResourceInitializer() { + packages(GreetingsRest.class.getPackage().toString()); + } + +} diff --git a/src/main/java/org/gcube/idm/IdentityManagerdInitializator.java b/src/main/java/org/gcube/idm/IdentityManagerdInitializator.java new file mode 100644 index 0000000..a7fb592 --- /dev/null +++ b/src/main/java/org/gcube/idm/IdentityManagerdInitializator.java @@ -0,0 +1,68 @@ +package org.gcube.idm; + +import org.gcube.common.security.providers.SecretManagerProvider; +import org.gcube.smartgears.ApplicationManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Alfredo Oliviero (ISTI - CNR) + */ +public class IdentityManagerdInitializator implements ApplicationManager { + + /** + * Logger + */ + private static Logger logger = LoggerFactory.getLogger(IdentityManagerdInitializator.class); + + public static boolean initialised; + + /** + * {@inheritDoc} + */ + @Override + public synchronized void onInit() { + + String context = SecretManagerProvider.instance.get().getContext(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Identity Manager Service is Starting on context {}\n" + + "-------------------------------------------------------", + context); + +// ApplicationContext applicationContext = ContextProvider.get(); +// String helloWorldEServiceID = applicationContext.id(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Identity Manager Service Started Successfully on context {}\n" + + "-------------------------------------------------------", + context); + + + + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void onShutdown(){ + + String context = SecretManagerProvider.instance.get().getContext(); + + logger.trace( + "\n-------------------------------------------------------\n" + + "Identity Manager Service is Stopping on context {}\n" + + "-------------------------------------------------------", + context); + + + logger.trace( + "\n-------------------------------------------------------\n" + + "Identity Manager Service Stopped Successfully on context {}\n" + + "-------------------------------------------------------", + context); + } +} diff --git a/src/main/java/org/gcube/idm/rest/GreetingsRest.java b/src/main/java/org/gcube/idm/rest/GreetingsRest.java new file mode 100644 index 0000000..3867527 --- /dev/null +++ b/src/main/java/org/gcube/idm/rest/GreetingsRest.java @@ -0,0 +1,69 @@ +package org.gcube.idm.rest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; +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 com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; +import com.webcohesion.enunciate.metadata.rs.ResponseCode; +import com.webcohesion.enunciate.metadata.rs.StatusCodes; + +@Path("greetings") +@ResourceGroup("Greetings APIs") +@ResourceLabel("Greetings APIs") +@RequestHeaders ({ + @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class GreetingsRest { + + @GET + @Produces({"application/json;charset=UTF-8", "application/vnd.api+json"}) + public String list(@QueryParam("limit") @DefaultValue("10") int limit, + @QueryParam("offset") @DefaultValue("0") int offset) { + + return "[\"saluti\",\"saluti_volgari\"]"; + } + + @POST + @Produces("application/json;charset=UTF-8") + @Consumes("application/json;charset=UTF-8") + public String create(String json) { + //Greeting g = new Greeting(); + //return g.create(json); + return "{\"text\":\"hi\"}"; + + } + + @PUT + @Path("/{greeting_name}") + @Consumes("application/json;charset=UTF-8") + @Produces("application/json;charset=UTF-8") + @StatusCodes ({ + @ResponseCode ( code = 200, condition = "The greeting has been updated successfully.") + }) +// @AuthorizationControl(allowedRoles={"boss"}, exception=NotAuthorizedException.class) + public String update(@PathParam("greeting_name") String name, String json) { + return "{}"; + } + + + @DELETE + @Path("/{greeting_name}") + @StatusCodes ({ + @ResponseCode ( code = 204, condition = "The item has been deleted successfully."), + @ResponseCode ( code = 404, condition = "The item was not found.") + }) + public String delete(@PathParam("greeting_name") String name) { + return "{}"; + } +} diff --git a/src/main/java/org/gcube/idm/rest/UsersRest.java b/src/main/java/org/gcube/idm/rest/UsersRest.java new file mode 100644 index 0000000..c454b6a --- /dev/null +++ b/src/main/java/org/gcube/idm/rest/UsersRest.java @@ -0,0 +1,153 @@ +package org.gcube.idm.rest; + +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import org.gcube.common.authorization.library.policies.Users; +import org.gcube.common.security.Owner; +import org.gcube.common.security.providers.SecretManagerProvider; +import org.gcube.keycloack.KeycloackApiClient; +import org.gcube.keycloack.KeycloakAPIFactory; +import org.gcube.rest.ResponseBean; +import org.gcube.smartgears.ContextProvider; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.security.SimpleCredentials; +import org.gcube.smartgears.security.defaults.DefaultAuthorizationProvider; +import org.gcube.smartgears.utils.InnerMethodName; +import org.jboss.resteasy.spi.NotImplementedYetException; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.slf4j.LoggerFactory; + +import com.webcohesion.enunciate.metadata.rs.RequestHeader; +import com.webcohesion.enunciate.metadata.rs.RequestHeaders; +import com.webcohesion.enunciate.metadata.rs.ResourceGroup; +import com.webcohesion.enunciate.metadata.rs.ResourceLabel; + +@Path("2/users") +@ResourceGroup("Users APIs") +@ResourceLabel("Greetings APIs") +@RequestHeaders({ + @RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources") +}) +public class UsersRest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Users.class); + + @GET + @Path("/{get-usernames-by-role}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public Response getUsernamesByRole( + @QueryParam("role-name") String roleName) { + Status status = Status.OK; + ResponseBean responseBean = new ResponseBean(); + + List usernames = new ArrayList(); + try { + String ctx = SecretManagerProvider.instance.get().getContext(); + KeycloackApiClient keycloackApiClient = KeycloakAPIFactory.getSingleton().createtKeycloakInstance(ctx); + + List users = searchByRole(keycloackApiClient, roleName); + if (users != null) { + for (UserRepresentation user : users) { + usernames.add(user.getUsername()); + } + } + responseBean.setResult(usernames); + responseBean.setSuccess(true); + } catch (Exception e) { + logger.error("Unable to retrieve user with the requested role", e); + responseBean.setMessage(e.getMessage()); + status = Status.INTERNAL_SERVER_ERROR; + } + + return Response.status(status).entity(responseBean).build(); + + } + + private static List searchByRole(KeycloackApiClient keycloackApiClient, String roleName) { + logger.info("Searching by role: {}", roleName); + + List clients = keycloackApiClient.kclient.realm(keycloackApiClient.realmName) + .clients().findByClientId(keycloackApiClient.clientIdContext); + + String id = ""; + for (ClientRepresentation client : clients) { + logger.info("found client =" + client.getClientId()); + logger.info("found client id=" + client.getId()); + id = client.getId(); + } + + List users = keycloackApiClient.kclient.realm(keycloackApiClient.realmName) + .clients() + .get(id).roles().get(roleName) + .getUserMembers(0, 100000); + return users; + } + + @GET + @Path("/{get-profile}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public String getCurrentProfile() { + // SMARTGEARS Specializza il tracciamento della chiamata su Accounting + InnerMethodName.instance.set("getCurrentProfile"); + Owner owner = SecretManagerProvider.instance.get().getOwner(); + + ApplicationContext appContext = ContextProvider.get(); + SimpleCredentials credentials = ((DefaultAuthorizationProvider) appContext.container().authorizationProvider()) + .getCredentials(); + + String ctx = SecretManagerProvider.instance.get().getContext(); + KeycloackApiClient keycloackApiClient = KeycloakAPIFactory.getSingleton().createtKeycloakInstance(ctx); + return null; + } + + @GET + @Path("/{get-email}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public String getCurrentEmail() { + throw new NotImplementedYetException(); + } + + @GET + @Path("/{get-fullname}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public String getCurrentFullname() { + throw new NotImplementedYetException(); + } + + @GET + @Path("/{get-all-usernames}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public String getAllUsernames() { + throw new NotImplementedYetException(); + } + + @GET + @Path("/{get-all-fullnames-and-usernames}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public String getAllUsernamesFullnames() { + throw new NotImplementedYetException(); + } + + @GET + @Path("/{user-exists}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public boolean checkUserExists() { + throw new NotImplementedYetException(); + } + + @GET + @Path("/{get-oauth-profile}") + @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" }) + public boolean getCurrentOAuthProfile() { + throw new NotImplementedYetException(); + } + +} diff --git a/src/main/java/org/gcube/keycloack/ErrorMessages.java b/src/main/java/org/gcube/keycloack/ErrorMessages.java new file mode 100644 index 0000000..538dcdc --- /dev/null +++ b/src/main/java/org/gcube/keycloack/ErrorMessages.java @@ -0,0 +1,34 @@ +package org.gcube.keycloack; + +public class ErrorMessages { + + protected static final String NOT_USER_TOKEN_CONTEXT_USED = "User's information can only be retrieved through a user token (not qualified)"; + protected static final String CANNOT_RETRIEVE_SERVICE_ENDPOINT_INFORMATION = "Unable to retrieve such service endpoint information"; + + private static final String NO_RUNTIME_RESOURCE_TEMPLATE_NAME_CATEGORY = "There is no Runtime Resource having name %s and Category %s in this scope"; + + protected static final String no_runtime_category(String runtime, String category) { + return String.format(NO_RUNTIME_RESOURCE_TEMPLATE_NAME_CATEGORY, runtime, category); + } + + // public static final String MISSING_TOKEN = "Missing token."; + // public static final String MISSING_PARAMETERS = "Missing request + // parameters."; + // public static final String INVALID_TOKEN = "Invalid token."; + // public static final String TOKEN_GENERATION_APP_FAILED = "Token generation + // failed."; + // public static final String NOT_APP_TOKEN = "Invalid token: not belonging to + // an application."; + // public static final String NOT_APP_ID = "Invalid application id: it doesn't + // belong to an application."; + // public static final String NO_APP_PROFILE_FOUND = "There is no application + // profile for this app id/scope."; + // public static final String BAD_REQUEST = "Please check the parameter you + // passed, it seems a bad request"; + // public static final String ERROR_IN_API_RESULT = "The error is reported into + // the 'message' field of the returned object"; + // public static final String POST_OUTSIDE_VRE = "A post cannot be written into + // a context that is not a VRE"; + // public static final String DEPRECATED_METHOD = "This method is deprecated, + // must use version 2"; +} diff --git a/src/main/java/org/gcube/keycloack/KeycloackApiClient.java b/src/main/java/org/gcube/keycloack/KeycloackApiClient.java new file mode 100644 index 0000000..20df1f5 --- /dev/null +++ b/src/main/java/org/gcube/keycloack/KeycloackApiClient.java @@ -0,0 +1,23 @@ +package org.gcube.keycloack; + +import org.keycloak.admin.client.Keycloak; + +public class KeycloackApiClient { + public Keycloak kclient; + public String realmName; + public String clientIdContext; + public String context; + + public static String getClientIdContext(String context){ + return context.replace("/", "%2F"); + } + + public KeycloackApiClient(Keycloak kclient, String realmName, String context) { + this.clientIdContext = getClientIdContext(context); + this.context = context; + this.kclient = kclient; + this.realmName = realmName; + // ClientsResource clients = kclient.realm(realmName).clients(); + + } +} diff --git a/src/main/java/org/gcube/keycloack/KeycloackClientParams_UNUSED.java b/src/main/java/org/gcube/keycloack/KeycloackClientParams_UNUSED.java new file mode 100644 index 0000000..738de48 --- /dev/null +++ b/src/main/java/org/gcube/keycloack/KeycloackClientParams_UNUSED.java @@ -0,0 +1,102 @@ +package org.gcube.keycloack; + +import java.io.InputStream; +import java.net.URL; +import java.util.AbstractMap.SimpleEntry; +import java.util.Map.Entry; +import java.util.Properties; + +import javax.ws.rs.InternalServerErrorException; + +import org.gcube.common.security.providers.SecretManagerProvider; +import org.gcube.common.keycloak.DefaultKeycloakClient; +import org.gcube.common.keycloak.KeycloakClientException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KeycloackClientParams_UNUSED { + + private static final Logger logger = LoggerFactory.getLogger(KeycloackClientParams_UNUSED.class); + + public static final String CATALOGUE_NAME = "IDM"; + + protected static final String CLIENT_ID_SECRET_FILENAME = "config.properties"; + protected static final String CLIENT_ID_PROPERTY_NAME = "clientId"; + + public String context; + public String clientId; + public String clientSecret; + public DefaultKeycloakClient gcubeKeycloakClient; + + // Reads the property file and extracts the keycloack configuration params + protected static Entry getClientIdAndClientSecret(String context) { + try { + Properties properties = new Properties(); + ClassLoader classLoader = KeycloackClientParams_UNUSED.class.getClassLoader(); + URL url = classLoader.getResource(CLIENT_ID_SECRET_FILENAME); + logger.trace("Going to read {} at {}", CLIENT_ID_SECRET_FILENAME, url.toString()); + InputStream input = classLoader.getResourceAsStream(CLIENT_ID_SECRET_FILENAME); + properties.load(input); + + String clientId = "IDM"; + if (properties.containsKey(CLIENT_ID_PROPERTY_NAME)) { + clientId = properties.getProperty(CLIENT_ID_PROPERTY_NAME); + } + + int index = context.indexOf('/', 1); + String root = context.substring(0, index == -1 ? context.length() : index); + String clientSecret = properties.getProperty(root); + + SimpleEntry entry = new SimpleEntry(clientId, clientSecret); + return entry; + } catch (Exception e) { + throw new InternalServerErrorException( + "Unable to retrieve Application Token for context " + + SecretManagerProvider.instance.get().getContext(), + e); + } + } + + // TODO: VERIFICARE + public URL getRealmBaseURL() throws KeycloakClientException { + return this.gcubeKeycloakClient.getRealmBaseURL(this.context); + } + + public URL getRealmBaseURL(String realm) throws KeycloakClientException { + return this.gcubeKeycloakClient.getRealmBaseURL(this.context, realm); + } + + public URL getServerURL() { + try { + return this.getRealmBaseURL(); + } catch (KeycloakClientException e) { + // That should be almost impossible + logger.warn("Cannot create base URL", e); + return null; + } + } + + public String getClientid() { + return clientId; + } + + // TODO: serve? implementare + public String getPassword() { + return null; + } + + // TODO: VERIFICARE + public String getRealm() { + return this.context; + } + + public KeycloackClientParams_UNUSED(String context) { + this.context = context; + Entry params = getClientIdAndClientSecret(context); + this.clientId = params.getKey(); + this.clientSecret = params.getKey(); + + this.gcubeKeycloakClient = new DefaultKeycloakClient(); + } + +} diff --git a/src/main/java/org/gcube/keycloack/KeycloakAPIFactory.java b/src/main/java/org/gcube/keycloack/KeycloakAPIFactory.java new file mode 100644 index 0000000..8ea513e --- /dev/null +++ b/src/main/java/org/gcube/keycloack/KeycloakAPIFactory.java @@ -0,0 +1,147 @@ +package org.gcube.keycloack; + +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.util.Iterator; +import java.util.List; + +import org.gcube.common.encryption.encrypter.StringEncrypter; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.gcube.smartgears.ContextProvider; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.keycloak.OAuth2Constants; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.KeycloakBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KeycloakAPIFactory { + private static final Logger logger = LoggerFactory.getLogger(KeycloakAPIFactory.class); + + private final static String RUNTIME_RESOURCE_NAME = "IAM"; + private final static String CATEGORY = "Service"; + + // the singleton obj + + private static KeycloakAPIFactory singleton = new KeycloakAPIFactory(); + + // properties that it contains + private String keycloakURL; + private String realm; + private String clientid; + private String password; + + /** + * Private constructor + */ + private KeycloakAPIFactory() { + logger.info("Building KeycloakAPICredentials object"); + + lookupPropertiesFromIs(); + logger.info("KeycloakAPICredentials object built"); + } + + /** + * Read the properties from the infrastructure + */ + private void lookupPropertiesFromIs() { + + logger.info("Starting creating KeycloakAPICredentials"); + + // String ctx = SecretManagerProvider.instance.get().getContext(); + // TODO: verificare che sia contesto corretto + ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears + + logger.info("Discovering liferay user's credentials in context " + + ctx.container().configuration().infrastructure()); + + try { + List resources = getConfigurationFromIS(); + if (resources.size() == 0) { + logger.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME + " and Category " + + CATEGORY + " in this scope."); + throw new Exception("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME + + " and Category " + CATEGORY + " in this scope."); + } else { + for (ServiceEndpoint res : resources) { + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator + .next(); + + if (accessPoint.name().equals("d4science")) { + keycloakURL = accessPoint.address(); + realm = accessPoint.name(); + clientid = accessPoint.username(); + password = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); + logger.info("Found accesspoint URL = " + keycloakURL); + } + } + } + + } + } catch (Exception e) { + logger.error("Unable to retrieve such service endpoint information!", e); + return; + // }finally{ + // if(oldContext != null) + // ScopeProvider.instance.set(oldContext); + } + + logger.info("Bean built " + toString()); + } + + /** + * Retrieve endpoints information from IS for DB + * + * @return list of endpoints for ckan database + * @throws Exception + */ + private List getConfigurationFromIS() throws Exception { + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '" + RUNTIME_RESOURCE_NAME + "'"); + query.addCondition("$resource/Profile/Category/text() eq '" + CATEGORY + "'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + } + + public static KeycloakAPIFactory getSingleton() { + if (singleton == null) + singleton = new KeycloakAPIFactory(); + return singleton; + } + + public String getServerURL() { + return keycloakURL; + } + + public String getClientid() { + return clientid; + } + + public String getPassword() { + return password; + } + + public String getRealm() { + return realm; + } + + public KeycloackApiClient createtKeycloakInstance(String context) { + // String clientIdContext = KeycloackUtils.getClientIdContext(context); + String realm = this.getRealm(); + Keycloak keycloak = KeycloakBuilder.builder() + .serverUrl(this.getServerURL()) + .realm(realm) + .grantType(OAuth2Constants.CLIENT_CREDENTIALS) + .clientId(this.getClientid()) // + .clientSecret(this.getPassword()).build(); + return new KeycloackApiClient(keycloak, realm, context); + } + +} diff --git a/src/main/java/org/gcube/rest/ResponseBean.java b/src/main/java/org/gcube/rest/ResponseBean.java new file mode 100644 index 0000000..84daeac --- /dev/null +++ b/src/main/java/org/gcube/rest/ResponseBean.java @@ -0,0 +1,71 @@ +package org.gcube.rest; +import java.io.Serializable; + +/** + * Response bean + * + */ +public class ResponseBean implements Serializable { + + private static final long serialVersionUID = -2725238162673879658L; + /** + * The result of the request: true if it succeeded, false otherwise + */ + private boolean success; + + /** + * An error message if something wrong happened, null/empty otherwise + */ + private String message; + /** + * The result object of the request + */ + private Object result; + + public ResponseBean() { + super(); + } + + /** + * @param success + * @param message + * @param result + */ + public ResponseBean(boolean success, String message, Object result) { + super(); + this.success = success; + this.message = message; + this.result = result; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + + @Override + public String toString() { + return "ResponseBean [success=" + success + + ", message=" + message + ", result=" + result + "]"; + } +} + diff --git a/src/main/java/org/gcube/rest/annotation/PATCH.java b/src/main/java/org/gcube/rest/annotation/PATCH.java new file mode 100644 index 0000000..bd46340 --- /dev/null +++ b/src/main/java/org/gcube/rest/annotation/PATCH.java @@ -0,0 +1,17 @@ +package org.gcube.rest.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.ws.rs.HttpMethod; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@HttpMethod("PATCH") +public @interface PATCH { +} \ No newline at end of file diff --git a/src/main/java/org/gcube/rest/annotation/PURGE.java b/src/main/java/org/gcube/rest/annotation/PURGE.java new file mode 100644 index 0000000..004980d --- /dev/null +++ b/src/main/java/org/gcube/rest/annotation/PURGE.java @@ -0,0 +1,17 @@ +package org.gcube.rest.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.ws.rs.HttpMethod; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@HttpMethod("PURGE") +public @interface PURGE { +} \ No newline at end of file diff --git a/src/main/java/org/gcube/rest/utils/HTTPUtility.java b/src/main/java/org/gcube/rest/utils/HTTPUtility.java new file mode 100644 index 0000000..6b5f9e9 --- /dev/null +++ b/src/main/java/org/gcube/rest/utils/HTTPUtility.java @@ -0,0 +1,66 @@ +package org.gcube.rest.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response.Status; + +import org.gcube.common.gxhttp.request.GXHTTPStringRequest; +import org.gcube.keycloack.KeycloackClientParams_UNUSED; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +public class HTTPUtility { + + private static final Logger logger = LoggerFactory.getLogger(HTTPUtility.class); + + public static StringBuilder getStringBuilder(InputStream inputStream) throws IOException { + StringBuilder result = new StringBuilder(); + try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while((line = reader.readLine()) != null) { + result.append(line); + } + } + + return result; + } + + public static GXHTTPStringRequest createGXHTTPStringRequest(String url, String path, boolean post) + throws UnsupportedEncodingException { + GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(url); + gxhttpStringRequest.from(KeycloackClientParams_UNUSED.CATALOGUE_NAME); + if(post) { + gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + } + gxhttpStringRequest.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); + gxhttpStringRequest.path(path); + return gxhttpStringRequest; + } + + public static String getResultAsString(HttpURLConnection httpURLConnection) throws IOException { + int responseCode = httpURLConnection.getResponseCode(); + if(responseCode >= Status.BAD_REQUEST.getStatusCode()) { + Status status = Status.fromStatusCode(responseCode); + InputStream inputStream = httpURLConnection.getErrorStream(); + StringBuilder result = getStringBuilder(inputStream); + logger.trace(result.toString()); + throw new WebApplicationException(result.toString(), status); + } + InputStream inputStream = httpURLConnection.getInputStream(); + String ret = getStringBuilder(inputStream).toString(); + logger.trace("Got Respose is {}", ret); + return ret; + } + +} diff --git a/src/main/java/org/gcube/utils/IdmConstantsaa/IdmConstants.java b/src/main/java/org/gcube/utils/IdmConstantsaa/IdmConstants.java new file mode 100644 index 0000000..3b1b958 --- /dev/null +++ b/src/main/java/org/gcube/utils/IdmConstantsaa/IdmConstants.java @@ -0,0 +1,12 @@ +package org.gcube.utils.IdmConstantsaa; + +public class IdmConstants { + + public static final String SERVICE_CLASS = "org.gcube.data-catalogue"; + public static final String SERVICE_NAME = "gcat"; + + public final static String CONFIGURATION_CATEGORY = IdmConstants.SERVICE_CLASS; + public final static String CONFIGURATION_NAME = IdmConstants.SERVICE_NAME + "-configuration"; + + public static final String SERVICE_ENTRY_NAME = "org.gcube.gcat.ResourceInitializer"; +} diff --git a/src/main/resources/META-INF/enunciate/d4science_docs.fmt b/src/main/resources/META-INF/enunciate/d4science_docs.fmt new file mode 100644 index 0000000..a5ee6ab --- /dev/null +++ b/src/main/resources/META-INF/enunciate/d4science_docs.fmt @@ -0,0 +1,1183 @@ +[#ftl] +[#-- + + Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) + + 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. + +--] +[#-- @ftlvariable name="resourceApis" type="java.util.List" --] +[#-- @ftlvariable name="serviceApis" type="java.util.List" --] +[#-- @ftlvariable name="data" type="java.util.List" --] +[#-- @ftlvariable name="downloads" type="java.util.List" --] +[#-- @ftlvariable name="title" type="java.lang.String" --] +[#-- @ftlvariable name="indexPageName" type="java.lang.String" --] +[#-- @ftlvariable name="disableMountpoint" type="java.lang.Boolean" --] +[#-- @ftlvariable name="disableResourceLinks" type="java.lang.Boolean" --] +[#-- @ftlvariable name="apiRelativePath" type="java.lang.String" --] +[#-- @ftlvariable name="cssFile" type="java.lang.String" --] +[#-- @ftlvariable name="additionalCssFiles" type="java.util.List" --] +[#-- @ftlvariable name="copyright" type="java.lang.String" --] +[#-- @ftlvariable name="apiDoc" type="java.lang.String" --] +[#-- @ftlvariable name="swaggerUI" type="com.webcohesion.enunciate.api.InterfaceDescriptionFile" --] +[#-- @ftlvariable name="favicon" type="java.lang.String" --] +[#-- @ftlvariable name="includeApplicationPath" type="java.lang.Boolean" --] +[#-- @ftlvariable name="includeDataTypesHomeIndex" type="java.lang.Boolean" --] +[#--set up the subnavigation menus--] +[#assign nav_sections = { } /] +[#if resourceApis?size > 0] + [#assign nav_sections = nav_sections + { "Resources" : "resources.html" }/] +[/#if] +[#if serviceApis?size > 0] + [#assign nav_sections = nav_sections + { "Services" : "services.html" }/] +[/#if] +[#if data?size > 0] + [#list data as syntax] + [#assign nav_sections = { syntax.label : syntax.slug + ".html" } /] + [/#list] +[/#if] +[#if downloads?size > 0] + [#assign nav_sections = nav_sections + { "Files and Libraries" : "downloads.html"} /] +[/#if] +[#--Basic boilerplate macro.--] +[#macro boilerplate title=title breadcrumbs=[{"title" : "Home", "href" : indexPageName}] pagenav=[] codeblocks=true] + + + + + + + + + ${title} + + + + + + + + [#if cssFile??] + + + [/#if] + [#list additionalCssFiles as additionalCssFile] + + [/#list] + [#if favicon??] + + + [/#if] + + + + + + +
+ D4Science + D4Science + + Don't have a D4Science account? + + Create one + + Could not find what you are looking for? + + Contact us. +
+ + + +
+
+ + +
+ + + [#nested/] + +
+
+

[#if copyright??]Copyright © ${copyright}. [/#if]Generated by Enunciate.

+
+
+ +
+
+
+ + + + + + + + + + + + + + +[/#macro] +[#--Macro that wraps its text in a deprecated tag if the element is deprecated.--] +[#macro deprecation element] + [#assign deprecated=(element?? && element.deprecated??)/] + [#if deprecated][/#if][#nested/][#if deprecated][/#if] +[/#macro] +[@file name=indexPageName] + [#assign pagenav=[]/] + [#if resourceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#resources", "title" : "Resources" }]/] + [/#if] + [#if serviceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#services", "title" : "Services" }]/] + [/#if] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate pagenav=pagenav] + [#if apiDoc??] + + [/#if] + [#if resourceApis?size > 0] + +

Resources

+ [#list resourceApis as resourceApi] + [#if downloads?size > 0] + +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] + +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] + +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + [@processResourceGroup resourceGroup=resourceGroup/] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/#if] + [#if serviceApis?size > 0] + +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + [@processService service=service/] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace!"(Default)"}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/#if] + [#if data?size > 0 && includeDataTypesHomeIndex] + +

Data Types

+ [#list data as syntax] + [@processDataSyntax syntax=syntax/] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [#elseif data?size > 0] + [#list data as syntax] + [@processDataSyntax syntax=syntax/] + [/#list] + [/#if] + [/@boilerplate] +[/@file] +[@file name="data.html"] + [#assign pagenav=[]/] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate title=title + ": Data Types" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Data Types" , "href" : "data.html"}] pagenav=pagenav] +

Data Types

+ [#list data as syntax] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [/@boilerplate] +[/@file] +[#if downloads?size > 0] + [@file name="downloads.html"] + [#assign pagenav=[]/] + [#list downloads as download] + [#assign pagenav=pagenav + [{ "href" : "#" + download.slug, "title" : download.name }]/] + [/#list] + [@boilerplate title=title + ": Files and Libraries" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, { "title" : "Files and Libraries" , "href" : "downloads.html"}] codeblocks=true pagenav=pagenav] +

Files and Libraries

+ + [#list downloads as download] +

${download.name}

+ [#if download.created??] +

Created ${download.created?date?string.long}

+ [/#if] + [#if download.artifactId??] +
+ [#if download.groupId??] +
groupId
+
${download.groupId}
+ [/#if] + [#if download.artifactId??] +
artifactId
+
${download.artifactId}
+ [/#if] + [#if download.version??] +
version
+
${download.version}
+
+ [/#if] + [/#if] + [#if download.description??] +

${download.description}

+ [/#if] + + + + + + + + + + + [#list download.files as file] + + + + + + [/#list] + +
Files
namesizedescription
${file.name}${file.size}${file.description!download.description!" "}
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if resourceApis?size > 0] + [@file name="resources.html"] + [@boilerplate title=title + ": Resources" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}]] +

Resources

+ + [#list resourceApis as resourceApi] + [#if downloads?size > 0] +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if serviceApis?size > 0] + [@file name="services.html"] + [@boilerplate title=title + ": Services" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Services" , "href" : "services.html"}]] +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/@boilerplate] + [/@file] +[/#if] +[#macro processResourceGroup resourceGroup] + [#assign pagenav=[]/] + [#list resourceGroup.resources as resource] + [#list resource.methods as method] + [#assign path=resource.path/] + [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))] + [#assign path="/" + resourceGroup.relativeContextPath + resource.path/] + [/#if] + [#assign pagenav=pagenav + [{ "href" : "#" + method.slug, "title" : method.label + " " + path }]/] + [/#list] + [/#list] + [#-- @ftlvariable name="resourceGroup" type="com.webcohesion.enunciate.api.resources.ResourceGroup" --] + [@file name=resourceGroup.slug + ".html"] + [@boilerplate title=title + ": " + resourceGroup.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}, {"title" : resourceGroup.label , "href" : resourceGroup.slug + ".html"}] pagenav=pagenav] +

${resourceGroup.label} Resource

+ [#if resourceGroup.description??] + +

${resourceGroup.description}

+ [/#if] + [#list resourceGroup.resources as resource] + [#if resource.since?? || resource.version?? || resource.seeAlso??] + +
+ [#if resource.since??] +
Available Since
+
${resource.since}
+ [/#if] + [#if resource.version??] +
Version
+
${resource.version}
+ [/#if] + [#if resource.seeAlso??] + [#list resource.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#list resource.methods as method] + +
+

${method.label} [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${resource.path}[#if !disableResourceLinks!false] [/#if]

+ [#if resourceGroup.deprecated?? || method.deprecated??] + +
This method has been deprecated. [#if method.deprecated??] ${method.deprecated!""}[#else] ${resource.deprecated!""}[/#if]
+ [/#if] + [#if method.description??] + +

${method.description}

+ [/#if] + [#-- would be nice to enable a "Try it out" link to Swagger. See https://github.com/swagger-api/swagger-spec/issues/239 + [#if swaggerUI??] + +

Try it out!

+ [/#if] + --] + [#assign securityRoles=method.securityRoles![]/] + [#if (method.since?? || method.version?? || method.seeAlso?? || securityRoles?size > 0)] + +
+ [#if method.since??] +
Available Since
+
${method.since}
+ [/#if] + [#if method.version??] +
Version
+
${method.version}
+ [/#if] + [#if securityRoles?size > 0] +
Security Roles Allowed
+
[#list securityRoles as role]${role}[#if role_has_next], [/#if][/#list]
+ [/#if] + [#if method.seeAlso??] + [#list method.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if method.parameters?size > 0] + + + + + + + + + [#assign includeDefault=method.includeDefaultParameterValues/] + [#if includeDefault] + + [/#if] + [#assign includeConstraints=method.hasParameterConstraints/] + [#if includeConstraints] + + [/#if] + [#assign includeMultiplicity=method.hasParameterMultiplicity/] + [#if includeMultiplicity] + + [/#if] + + + + [#list method.parameters as parameter] + + + + + [#if includeDefault] + + [/#if] + [#if includeConstraints] + + [/#if] + [#if includeMultiplicity] + + [/#if] + + [/#list] + +
Request Parameters
nametypedescriptiondefaultconstraintsmultivalued
${parameter.name}${parameter.typeLabel}${parameter.description!" "}${parameter.defaultValue!" "}${parameter.constraints!" "}${parameter.multivalued?string("yes", "no")}
+ [/#if] + [#if method.requestEntity??] + + + + + + + + [#if method.requestEntity.description??] + + [/#if] + + + + [#list method.requestEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.requestEntity.description??] + 1] rowspan="${method.requestEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.requestEntity.description} + [/#if] + + [/#list] + +
Request Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.responseCodes?size > 0] + + + + + + + + [#assign hasExpectedTypes=false/] + [#list method.responseCodes as responseCode] + [#if responseCode.mediaTypes?size > 0] + [#assign hasExpectedTypes=true/] + [/#if] + [/#list] + [#if hasExpectedTypes] + + [/#if] + + + + [#list method.responseCodes as responseCode] + + + + [#if hasExpectedTypes] + + [/#if] + + [/#list] + +
Response Codes
codeconditiontype
${responseCode.code}${responseCode.condition!""}
    [#list responseCode.mediaTypes as io]
  • [@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
  • [/#list]
+ [/#if] + [#if method.responseEntity??] + + + + + + + + [#if method.responseEntity.description??] + + [/#if] + + + + [#list method.responseEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.responseEntity.description??] + 1] rowspan="${method.responseEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.responseEntity.description} + [/#if] + + [/#list] + +
Response Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.warnings?size > 0] + + + + + + + + + + + [#list method.warnings as responseCode] + + + + + [/#list] + +
Response Warnings
codecondition
${responseCode.code}${responseCode.condition!""}
+ [/#if] + [#if method.responseHeaders?size > 0] + + + + + + + + + + + [#list method.responseHeaders as header] + + + + + [/#list] + +
Response Headers
namedescription
${header.name}${header.description!" "}
+ + [/#if] + [#if method.example??] +

Example

+ +
+
+
+
Request
+
+${method.example.requestHeaders?xhtml}
+            [#if method.example.requestLang??]
+                
+${method.example.requestBody?xhtml}
+                
+            [/#if]
+              
+
+
+
Response
+
+${method.example.responseHeaders?xhtml}
+            [#if method.example.responseLang??]
+                
+${method.example.responseBody?xhtml}
+                
+            [/#if]
+              
+
+
+
+ [/#if] +
+ [/#list] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processService service] + [#assign pagenav=[]/] + [#list service.operations as operation] + [#assign pagenav=pagenav + [{ "href" : "#" + operation.slug, "title" : operation.name }]/] + [/#list] + [#-- @ftlvariable name="service" type="com.webcohesion.enunciate.api.services.Service" --] + [@file name=service.slug + ".html"] + [@boilerplate title=title + ": " + service.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : service.label , "href" : service.slug + ".html"}] pagenav=pagenav] +

${service.label} Service

+ [#if service.deprecated??] + +
This service has been deprecated. ${service.deprecated}
+ [/#if] + [#if service.description??] + +

${service.description}

+ [/#if] + +
+ [#if service.namespace?? && service.namespace?length > 0] +
Namespace
+
${service.namespace}
+ [/#if] + [#if service.group.wsdlFile??] +
WSDL
+
${service.group.wsdlFile.href}
+ [/#if] + [#if service.path??] +
Path
+
${service.path}
+ [/#if] + [#if service.since??] +
Available Since
+
${service.since}
+ [/#if] + [#if service.version??] +
Version
+
${service.version}
+ [/#if] + [#if service.seeAlso??] + [#list service.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#list service.operations as operation] + +

${operation.name} Operation

+ [#if operation.deprecated??] + +
This method has been deprecated. ${operation.deprecated}
+ [/#if] + [#if operation.description??] + +

${operation.description}

+ [/#if] + [#if (operation.since?? || operation.version?? || operation.seeAlso??)] + +
+ [#if operation.since??] +
Available Since
+
${operation.since}
+ [/#if] + [#if operation.version??] +
Version
+
${operation.version}
+ [/#if] + [#if operation.seeAlso??] + [#list operation.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if operation.inputParameters?size > 0] + + + + + + + + + + + + [#list operation.inputParameters as parameter] + + + + + + [/#list] + +
Input Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.httpRequestHeaders?size > 0] + + + + + + + + + + + + [#list operation.httpRequestHeaders as parameter] + + + + + + [/#list] + +
HTTP Request Headers
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.outputParameters?size > 0] + + + + + + + + + + + + [#list operation.outputParameters as parameter] + + + + + + [/#list] + +
Output Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.returnType??] + + + + + + + + + + + + + + + +
Return Value
typedescription
[@referenceDataType referenceType=operation.returnType/]${operation.returnDescription!" "}
+ [/#if] + [#if operation.faults?size > 0] + + + + + + + + + + + + [#list operation.faults as fault] + + + + + + [/#list] + +
Faults
nametypeconditions
${fault.name}[@referenceDataType referenceType=fault.dataType/]${fault.conditions!" "}
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataSyntax syntax] + [#-- @ftlvariable name="syntax" type="com.webcohesion.enunciate.api.datatype.Syntax" --] + [@file name=syntax.slug + ".html"] + [@boilerplate title=title + ": " + syntax.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : syntax.label , "href" : syntax.slug + ".html"} ]] +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + [@processDataType type=type/] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataType type] + [#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataType" --] + [#assign pagenav=[]/] + [#if type.values??] + [#list type.values as value] + [#assign pagenav=pagenav + [{ "href" : "#" + value.value, "title" : value.value }]/] + [/#list] + [/#if] + [@file name=type.slug + ".html"] + [@boilerplate title=title + ": " + type.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : type.syntax.label , "href" : type.syntax.slug + ".html"}, {"title" : type.label , "href" : type.slug + ".html"} ] pagenav=pagenav codeblocks=true] +

${type.label} Data Type

+ [#if type.deprecated??] + +
This data type has been deprecated. ${type.deprecated}
+ [/#if] + [#if type.description??] + +

${type.description}

+ [/#if] + +
+ [#if type.namespace.uri??] +
Namespace
+ [#if type.namespace.uri?length > 0] +
${type.namespace.uri}
+ [#else] +
(Default)
+ [/#if] + [/#if] + [#if type.namespace.schemaFile??] +
Schema
+
${type.namespace.schemaFile.href}
+ [/#if] + [#if type.since??] +
Available Since
+
${type.since}
+ [/#if] + [#if type.version??] +
Version
+
${type.version}
+ [/#if] + [#if type.abstract] +
Abstract Type
+
+ [/#if] + [#if type.subtypes??] +
Subtypes
+
[#list type.subtypes as subtype][#if subtype.slug??]${subtype.label}[#else]${subtype.label}[/#if][#if subtype_has_next], [/#if][/#list]
+ [/#if] + [#if type.interfaces??] +
Implemented Interfaces
+
[#list type.interfaces as iface]${iface.label}[#if iface_has_next], [/#if][/#list]
+ [/#if] + [#if type.seeAlso??] + [#list type.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#if type.values??] + + + + + + + + + + + [#list type.values as value] + + + + + [/#list] + +
Values
valuedescription
${value.value}[#if value.since??]since ${value.since} [/#if]${value.description!" "}
+ [/#if] + [#if type.properties??] + + + + + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + + + [#list type.properties as property] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [#if type.supertypes??] + [#list type.supertypes as supertype] + [#if supertype.value?? && supertype.value.properties?? && supertype.value.properties?size > 0] + + + + + [#list supertype.value.properties as superProperty] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [/#if] + [/#list] + [/#if] +
Properties
namedata type${type.propertyMetadata[meta]}description
[@deprecation element=property]${property.name}[/@deprecation][@deprecation element=property][@referenceDataType referenceType=property.dataType/][/@deprecation][@deprecation element=property][@printPropertyMetadata property=property meta=meta/][/@deprecation][@deprecation element=property][#if property.since??]since ${property.since} [/#if]${property.description!" "}[/@deprecation]
Properties inherited from ${supertype.label}
${superProperty.name}[@referenceDataType referenceType=superProperty.dataType/][@printPropertyMetadata property=superProperty meta=meta/][#if superProperty.since??]since ${superProperty.since} [/#if]${superProperty.description!" "}
+ [/#if] + [#if type.example??] + +

Example

+ [#if type.abstract && type.subtypes?? ] + +
This data type is abstract. The example below may be incomplete. More accurate examples can be found in subtypes pages.
+ [/#if] + +
${type.example.body?xhtml}
+ [/#if] + [/@boilerplate] + [/@file] +[/#macro] +[#macro referenceDataType referenceType] +[#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataTypeReference" --] +[#if referenceType.containers??][#list referenceType.containers as container]${container?string} of [/#list][/#if][#if referenceType.slug??][/#if]${referenceType.label!"(custom)"}[#if referenceType.slug??][/#if] +[/#macro] +[#macro printPropertyMetadata property meta] + [#assign metaValue=property[meta]!({ "structure" : true })/] + [#if metaValue?is_hash && metaValue.structure!false] +[#if metaValue.href??][/#if][#if metaValue.title??][/#if]${metaValue.value!" "}[#if metaValue.title??][/#if][#if metaValue.href??][/#if] + [#else] +${metaValue} + [/#if] +[/#macro] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/.gitignore b/src/main/webapp/WEB-INF/.gitignore new file mode 100644 index 0000000..a252987 --- /dev/null +++ b/src/main/webapp/WEB-INF/.gitignore @@ -0,0 +1,5 @@ +/LICENSE.md +/README.md +/changelog.xml +/gcube-app.xml +/CHANGELOG.md diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..ef9a39b --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,19 @@ + + + + + org.gcube.acme.ResourceInitializer + + + default + /docs/* + + + default + /api-docs/* + + + org.gcube.acme.ResourceInitializer + /* + + \ No newline at end of file diff --git a/src/main/webapp/docs/css/d4science_enunciate_custom.css b/src/main/webapp/docs/css/d4science_enunciate_custom.css new file mode 100644 index 0000000..9dae183 --- /dev/null +++ b/src/main/webapp/docs/css/d4science_enunciate_custom.css @@ -0,0 +1,25 @@ +.d4science_intro { + top: 0; + z-index: 2000; + position: fixed; + display: block ruby; + padding: 10px; + background: white; + width: 100%; +} + +.navbar-fixed-top { + top: 100px !important; +} + +.sidebar { + top: 160px !important; +} + +.navbar { + margin-bottom: 40px !important; +} + +.main { + top: 90px; +} \ No newline at end of file