first commit. smartgear service, keycloack client prototipe

This commit is contained in:
Alfredo Oliviero 2024-02-06 16:26:56 +01:00
commit 493961ad0b
41 changed files with 4141 additions and 0 deletions

171
.gitignore vendored Normal file
View File

@ -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

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"java.compile.nullAnalysis.mode": "automatic",
"java.configuration.updateBuildConfiguration": "automatic"
}

9
CHANGELOG.md Normal file
View File

@ -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

11
Dockerfile Normal file
View File

@ -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

26
FUNDING.md Normal file
View File

@ -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);

312
LICENSE.md Normal file
View File

@ -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 Licensees 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

49
README.md Normal file
View File

@ -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)

21
api.md Normal file
View File

@ -0,0 +1,21 @@
<!-- // da implementare rispetto al contesto -->
/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

23
appunti.md Normal file
View File

@ -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" .
<source> 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" </source>
</configuration>

25
docker/container.ini Normal file
View File

@ -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

25
docker/logback.xml Normal file
View File

@ -0,0 +1,25 @@
<configuration scan="true" debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>Ï
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.gcube" level="DEBUG" />
<logger name="org.gcube.smartgears" level="TRACE" />
<logger name="org.gcube.smartgears.handlers" level="TRACE"/>
<logger name="org.gcube.common.events" level="WARN" />
<logger name="org.gcube.data.publishing" level="ERROR" />
<logger name="org.gcube.documentstore" level="ERROR" />
<logger name="org.gcube.common.core.publisher.is.legacy" level="TRACE" />
<logger name="org.gcube.data.access" level="TRACE" />
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG"/>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>

1
docs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/_build/

20
docs/Makefile Normal file
View File

@ -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)

54
docs/conf.py Normal file
View File

@ -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']

484
docs/index.md Normal file
View File

@ -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
<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)](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:
<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
<https://tools.ietf.org/html/rfc7231#section-6.5.1>;
- **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
<https://tools.ietf.org/html/rfc7235#section-3.1>;
- **404 Not Found** used to indicate that the requested instance does
not exist <https://tools.ietf.org/html/rfc7231#section-6.5.4>;
- **405 Method Not Allowed** the used HTTP method is not supported for
the requested URL
<https://tools.ietf.org/html/rfc7231#section-6.5.5>. The response
contains the *Allow* HTTP Header indicating the supported HTTP
method for such URL
<https://tools.ietf.org/html/rfc7231#section-7.4.1>;
- **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)
<https://tools.ietf.org/html/rfc7231#section-6.5.8>;
- **500 Internal Server Error** indicate a server failure
<https://tools.ietf.org/html/rfc7231#section-6.6.1>.
You can find a complete list of HTTP Status at
<https://httpstatuses.com/>
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<br/>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<br/>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\"
<https://tools.ietf.org/html/rfc7231#section-4.2.1>;
- 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\"
<https://tools.ietf.org/html/rfc7231#section-4.2.2>.
You can find more information about HTTP Methods at
<https://restfulapi.net/http-methods/>
### 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}
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat-client</artifactId>
<version>[2.2.0, 3.0.0-SNAPSHOT)</version>
```
**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}
<ServiceClass>org.gcube.data-catalogue</ServiceClass>
<ServiceName>gcat</ServiceName>
```
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}
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat</artifactId>
```

510
docs/index.rst Normal file
View File

@ -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) <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: 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 `<https://tools.ietf.org/html/rfc7231#section-6.5.1>`_;
* **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 `<https://tools.ietf.org/html/rfc7235#section-3.1>`_;
* **404 Not Found** used to indicate that the requested instance does not exist `<https://tools.ietf.org/html/rfc7231#section-6.5.4>`_;
* **405 Method Not Allowed** the used HTTP method is not supported for the requested URL `<https://tools.ietf.org/html/rfc7231#section-6.5.5>`_.
The response contains the *Allow* HTTP Header indicating the supported HTTP method for such URL `<https://tools.ietf.org/html/rfc7231#section-7.4.1>`_;
* **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) `<https://tools.ietf.org/html/rfc7231#section-6.5.8>`_;
* **500 Internal Server Error** indicate a server failure `<https://tools.ietf.org/html/rfc7231#section-6.6.1>`_.
You can find a complete list of HTTP Status at `<https://httpstatuses.com/>`_
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 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 . OReilly. 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"
`<https://tools.ietf.org/html/rfc7231#section-4.2.1>`_;
* 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"
`<https://tools.ietf.org/html/rfc7231#section-4.2.2>`_.
You can find more information about HTTP Methods at `<https://restfulapi.net/http-methods/>`_
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.
.. 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
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat-client</artifactId>
<version>[2.2.0, 3.0.0-SNAPSHOT)</version>
**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
<ServiceClass>org.gcube.data-catalogue</ServiceClass>
<ServiceName>gcat</ServiceName>
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
<groupId>org.gcube.data-catalogue</groupId>
<artifactId>gcat</artifactId>

35
docs/make.bat Normal file
View File

@ -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

23
docs/tests.rst Normal file
View File

@ -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 |
+---------------+---------------+----------------------------------------+

23
enunciate.xml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<enunciate
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.14.0.xsd">
<description package="org.gcube.acme.rest"/>
<api-classes>
<include pattern="org.gcube.acme.rest.*" />
<exclude pattern="org.gcube.acme.*" />
</api-classes>
<modules>
<gwt-json-overlay disabled="true " />
<php-json-client disabled="true" />
<ruby-json-client disabled="true" />
<java-json-client disabled="true" />
<javascript-client disabled="true" />
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
<swagger basePath="/${project.artifactId}" />
<docs freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<additional-css
file="css/d4science_enunciate_custom.css" />
</docs>
</modules>
</enunciate>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<application mode='online'>
<name>${project.artifactId}</name>
<group>${project.groupId}</group>
<version>${project.version}</version>
<description>${project.description}</description>
<exclude>/api-docs.*</exclude>
<exclude>/docs.*</exclude>
</application>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<!-- <context-param>
<param-name>resolver-basepath</param-name>
<param-value>https://data-d.d4science.org/shub</param-value>
</context-param> -->
<servlet>
<servlet-name>org.gcube.data.access.storagehub.StorageHub</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>org.gcube.data.access.storagehub.StorageHub</servlet-name>
<url-pattern>/workspace/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -0,0 +1,6 @@
name: IdentityManagerService
group: IAM
version: ${version}
description: ${description}
excludes:
- path: /workspace/api-docs/*

209
pom.xml Normal file
View File

@ -0,0 +1,209 @@
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.gcube.tools</groupId>
<artifactId>maven-parent</artifactId>
<version>1.2.0</version>
</parent>
<groupId>org.gcube.idm</groupId>
<artifactId>identity-manager</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Identity Manager Service</name>
<description>Identity Manager Smargears Service</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<webappDirectory>
${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF</webappDirectory>
<enunciate.version>2.14.0</enunciate.version>
</properties>
<scm>
<connection>
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.gcube.distribution</groupId>
<artifactId>gcube-smartgears-bom</artifactId>
<version>3.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-security</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>keycloak-client</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears-app</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
</dependency>
<!-- Required for Enunciate plugin -->
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-core-annotations</artifactId>
<version>${enunciate.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-rt-util</artifactId>
<version>${enunciate.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- END Required for Enunciate plugin -->
<!-- Test libraries -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gcube.portlets.user</groupId>
<artifactId>uri-resolver-manager</artifactId>
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Sphinx plugin' -->
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>sphinx-maven-plugin</artifactId>
<version>2.10.0</version>
<configuration>
<outputDirectory>
${project.build.directory}/${project.artifactId}-${project.version}/docs</outputDirectory>
<builder>html</builder>
<configDirectory>${basedir}/docs</configDirectory>
<sourceDirectory>${basedir}/docs</sourceDirectory>
</configuration>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Enunciate Maven plugin -->
<plugin>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-maven-plugin</artifactId>
<version>${enunciate.version}</version>
<executions>
<execution>
<id>assemble</id>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Copy Enunciate Documentation from your-application/api-docs
into your war -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-enunciate-docs</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target</outputDirectory>
<resources>
<resource>
<targetPath>
${project.build.directory}/${project.artifactId}-${project.version}/api-docs</targetPath>
<directory>
${project.build.directory}/api-docs</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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<Exception> {
@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();
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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 <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
})
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 "{}";
}
}

View File

@ -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 <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
})
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<String> usernames = new ArrayList<String>();
try {
String ctx = SecretManagerProvider.instance.get().getContext();
KeycloackApiClient keycloackApiClient = KeycloakAPIFactory.getSingleton().createtKeycloakInstance(ctx);
List<UserRepresentation> 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<UserRepresentation> searchByRole(KeycloackApiClient keycloackApiClient, String roleName) {
logger.info("Searching by role: {}", roleName);
List<ClientRepresentation> 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<UserRepresentation> 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();
}
}

View File

@ -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";
}

View File

@ -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();
}
}

View File

@ -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<String, String> 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<String, String> entry = new SimpleEntry<String, String>(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<String, String> params = getClientIdAndClientSecret(context);
this.clientId = params.getKey();
this.clientSecret = params.getKey();
this.gcubeKeycloakClient = new DefaultKeycloakClient();
}
}

View File

@ -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<ServiceEndpoint> 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<AccessPoint> 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<ServiceEndpoint> 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<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
List<ServiceEndpoint> 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);
}
}

View File

@ -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 + "]";
}
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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";
}

File diff suppressed because it is too large Load Diff

5
src/main/webapp/WEB-INF/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/LICENSE.md
/README.md
/changelog.xml
/gcube-app.xml
/CHANGELOG.md

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<web-app>
<servlet>
<servlet-name>org.gcube.acme.ResourceInitializer</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/api-docs/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>org.gcube.acme.ResourceInitializer</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -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;
}